diff --git a/Data/SearchEngine.hs b/Data/SearchEngine.hs
--- a/Data/SearchEngine.hs
+++ b/Data/SearchEngine.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE BangPatterns, NamedFieldPuns, RecordWildCards #-}
-
 module Data.SearchEngine (
 
     -- * Basic interface
@@ -8,12 +6,16 @@
     Term,
     query,
 
+    -- *** Query auto-completion \/ auto-suggestion
+    queryAutosuggest,
+    ResultsFilter(..),
+
     -- ** Making a search engine instance
     initSearchEngine,
     SearchEngine,
     SearchConfig(..),
     SearchRankParameters(..),
-    BM25F.FeatureFunction(..),
+    FeatureFunction(..),
 
     -- ** Helper type for non-term features
     NoFeatures,
@@ -26,361 +28,15 @@
 
     -- * Explain mode for query result rankings
     queryExplain,
-    BM25F.Explanation(..),
+    Explanation(..),
     setRankParams,
 
     -- * Internal sanity check
     invariant,
   ) where
 
-import Data.SearchEngine.SearchIndex (SearchIndex, Term, TermId)
-import qualified Data.SearchEngine.SearchIndex as SI
-import Data.SearchEngine.DocIdSet (DocIdSet, DocId)
-import qualified Data.SearchEngine.DocIdSet as DocIdSet
-import Data.SearchEngine.DocTermIds (DocTermIds)
-import qualified Data.SearchEngine.DocTermIds as DocTermIds
-import Data.SearchEngine.DocFeatVals (DocFeatVals)
-import qualified Data.SearchEngine.DocFeatVals as DocFeatVals
-import qualified Data.SearchEngine.BM25F as BM25F
-
-import Data.Ix
-import Data.Array.Unboxed
-import Data.List
-import Data.Function
-import Data.Maybe
-
--------------------
--- Doc layer
---
--- That is, at the layer of documents, so covering the issues of:
---  - inserting/removing whole documents
---  - documents having multiple fields
---  - documents having multiple terms
---  - transformations (case-fold/normalisation/stemming) on the doc terms
---  - transformations on the search terms
---
-
-data SearchConfig doc key field feature = SearchConfig {
-       documentKey          :: doc -> key,
-       extractDocumentTerms :: doc -> field -> [Term],
-       transformQueryTerm   :: Term -> field -> Term,
-       documentFeatureValue :: doc -> feature -> Float
-     }
-
-data SearchRankParameters field feature = SearchRankParameters {
-       paramK1                 :: !Float,
-       paramB                  :: field -> Float,
-       paramFieldWeights       :: field -> Float,
-       paramFeatureWeights     :: feature -> Float,
-       paramFeatureFunctions   :: feature -> BM25F.FeatureFunction,
-       paramResultsetSoftLimit :: !Int,
-       paramResultsetHardLimit :: !Int
-     }
-
-data SearchEngine doc key field feature = SearchEngine {
-       searchIndex      :: !(SearchIndex      key field feature),
-       searchConfig     :: !(SearchConfig doc key field feature),
-       searchRankParams :: !(SearchRankParameters field feature),
-
-       -- cached info
-       sumFieldLengths :: !(UArray field Int),
-       bm25Context     :: BM25F.Context TermId field feature
-     }
-
-initSearchEngine :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                    SearchConfig doc key field feature ->
-                    SearchRankParameters field feature ->
-                    SearchEngine doc key field feature
-initSearchEngine config params =
-    cacheBM25Context
-      SearchEngine {
-        searchIndex      = SI.emptySearchIndex,
-        searchConfig     = config,
-        searchRankParams = params,
-        sumFieldLengths  = listArray (minBound, maxBound) (repeat 0),
-        bm25Context      = undefined
-      }
-
-setRankParams :: SearchRankParameters field feature ->
-                 SearchEngine doc key field feature ->
-                 SearchEngine doc key field feature
-setRankParams params@SearchRankParameters{..} se =
-    se {
-      searchRankParams = params,
-      bm25Context      = (bm25Context se) {
-        BM25F.paramK1         = paramK1,
-        BM25F.paramB          = paramB,
-        BM25F.fieldWeight     = paramFieldWeights,
-        BM25F.featureWeight   = paramFeatureWeights,
-        BM25F.featureFunction = paramFeatureFunctions
-      }
-    }
-
-invariant :: (Ord key, Ix field, Bounded field) =>
-             SearchEngine doc key field feature -> Bool
-invariant SearchEngine{searchIndex} =
-    SI.invariant searchIndex
--- && check caches
-
-cacheBM25Context :: Ix field =>
-                    SearchEngine doc key field feature ->
-                    SearchEngine doc key field feature
-cacheBM25Context
-    se@SearchEngine {
-      searchRankParams = SearchRankParameters{..},
-      searchIndex,
-      sumFieldLengths
-    }
-  = se { bm25Context = bm25Context' }
-  where
-    bm25Context' = BM25F.Context {
-      BM25F.numDocsTotal    = SI.docCount searchIndex,
-      BM25F.avgFieldLength  = \f -> fromIntegral (sumFieldLengths ! f)
-                                  / fromIntegral (SI.docCount searchIndex),
-      BM25F.numDocsWithTerm = DocIdSet.size . SI.lookupTermId searchIndex,
-      BM25F.paramK1         = paramK1,
-      BM25F.paramB          = paramB,
-      BM25F.fieldWeight     = paramFieldWeights,
-      BM25F.featureWeight   = paramFeatureWeights,
-      BM25F.featureFunction = paramFeatureFunctions
-    }
-
-updateCachedFieldLengths :: (Ix field, Bounded field) =>
-                            Maybe (DocTermIds field) -> Maybe (DocTermIds field) ->
-                            SearchEngine doc key field feature ->
-                            SearchEngine doc key field feature
-updateCachedFieldLengths Nothing (Just newDoc) se@SearchEngine{sumFieldLengths} =
-    se {
-      sumFieldLengths =
-        array (bounds sumFieldLengths)
-              [ (i, n + DocTermIds.fieldLength newDoc i)
-              | (i, n) <- assocs sumFieldLengths ]
-    }
-updateCachedFieldLengths (Just oldDoc) (Just newDoc) se@SearchEngine{sumFieldLengths} =
-    se {
-      sumFieldLengths =
-        array (bounds sumFieldLengths)
-              [ (i, n - DocTermIds.fieldLength oldDoc i
-                      + DocTermIds.fieldLength newDoc i)
-              | (i, n) <- assocs sumFieldLengths ]
-    }
-updateCachedFieldLengths (Just oldDoc) Nothing se@SearchEngine{sumFieldLengths} =
-    se {
-      sumFieldLengths =
-        array (bounds sumFieldLengths)
-              [ (i, n - DocTermIds.fieldLength oldDoc i)
-              | (i, n) <- assocs sumFieldLengths ]
-    }
-updateCachedFieldLengths Nothing Nothing se = se
-
-insertDocs :: (Ord key, Ix field, Bounded field, Ix feature, Bounded feature) =>
-              [doc] ->
-              SearchEngine doc key field feature ->
-              SearchEngine doc key field feature
-insertDocs docs se = foldl' (\se' doc -> insertDoc doc se') se docs
-
-insertDoc :: (Ord key, Ix field, Bounded field, Ix feature, Bounded feature) =>
-             doc ->
-             SearchEngine doc key field feature ->
-             SearchEngine doc key field feature
-insertDoc doc se@SearchEngine{ searchConfig = SearchConfig {
-                                 documentKey, 
-                                 extractDocumentTerms,
-                                 documentFeatureValue
-                               }
-                             , searchIndex } =
-    let key = documentKey doc
-        searchIndex' = SI.insertDoc key (extractDocumentTerms doc)
-                                        (documentFeatureValue doc)
-                                        searchIndex
-        oldDoc       = SI.lookupDocKey searchIndex  key
-        newDoc       = SI.lookupDocKey searchIndex' key
-
-     in cacheBM25Context $
-        updateCachedFieldLengths oldDoc newDoc $
-          se { searchIndex = searchIndex' }
-
-deleteDoc :: (Ord key, Ix field, Bounded field) =>
-             key ->
-             SearchEngine doc key field feature ->
-             SearchEngine doc key field feature
-deleteDoc key se@SearchEngine{searchIndex} =
-    let searchIndex' = SI.deleteDoc key searchIndex
-        oldDoc       = SI.lookupDocKey searchIndex key
-
-     in cacheBM25Context $
-        updateCachedFieldLengths oldDoc Nothing $
-          se { searchIndex = searchIndex' }
-
-query :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-         SearchEngine doc key field feature ->
-         [Term] -> [key]
-query se@SearchEngine{ searchIndex,
-                       searchConfig     = SearchConfig{transformQueryTerm},
-                       searchRankParams = SearchRankParameters{..} }
-      terms =
-
-  let -- Start by transforming/normalising all the query terms.
-      -- This can be done differently for each field we search by.
-      lookupTerms :: [Term]
-      lookupTerms = [ term'
-                    | term  <- terms
-                    , let transformForField = transformQueryTerm term
-                    , term' <- nub [ transformForField field
-                                   | field <- range (minBound, maxBound) ]
-                    ]
-
-      -- Then we look up all the normalised terms in the index.
-      rawresults :: [Maybe (TermId, DocIdSet)] 
-      rawresults = map (SI.lookupTerm searchIndex) lookupTerms
-
-      -- For the terms that occur in the index, this gives us the term's id
-      -- and the set of documents that the term occurs in.
-      termids   :: [TermId]
-      docidsets :: [DocIdSet]
-      (termids, docidsets) = unzip (catMaybes rawresults)
-
-      -- We looked up the documents that *any* of the term occur in (not all)
-      -- so this could be rather a lot of docs if the user uses a few common
-      -- terms. Scoring these result docs is a non-trivial cost so we want to
-      -- limit the number that we have to score. The standard trick is to
-      -- consider the doc sets in the order of size, smallest to biggest. Once
-      -- we have gone over a certain threshold of docs then don't bother with
-      -- the doc sets for the remaining terms. This tends to work because the
-      -- scoring gives lower weight to terms that occur in many documents.
-      unrankedResults :: DocIdSet
-      unrankedResults = pruneRelevantResults
-                          paramResultsetSoftLimit
-                          paramResultsetHardLimit
-                          docidsets
-
-      --TODO: technically this isn't quite correct. Because each field can
-      -- be normalised differently, we can end up with different termids for
-      -- the same original search term, and then we score those as if they
-      -- were different terms, which makes a difference when the term appears
-      -- in multiple fields (exactly the case BM25F is supposed to deal with).
-      -- What we ought to have instead is an Array (Int, field) TermId, and
-      -- make the scoring use the appropriate termid for each field, but to
-      -- consider them the "same" term.
-   in rankResults se termids (DocIdSet.toList unrankedResults)
-
-rankResults :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-               SearchEngine doc key field feature ->
-               [TermId] -> [DocId] -> [key]
-rankResults se@SearchEngine{searchIndex} queryTerms docids =
-    map snd
-  $ sortBy (flip compare `on` fst)
-      [ (relevanceScore se queryTerms doctermids docfeatvals, dockey)
-      | docid <- docids
-      , let (dockey, doctermids, docfeatvals) = SI.lookupDocId searchIndex docid ]
-
-relevanceScore :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                  SearchEngine doc key field feature ->
-                  [TermId] -> DocTermIds field -> DocFeatVals feature -> Float
-relevanceScore SearchEngine{bm25Context} queryTerms doctermids docfeatvals =
-    BM25F.score bm25Context doc queryTerms
-  where
-    doc = indexDocToBM25Doc doctermids docfeatvals
-
-indexDocToBM25Doc :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                     DocTermIds field ->
-                     DocFeatVals feature ->
-                     BM25F.Doc TermId field feature
-indexDocToBM25Doc doctermids docfeatvals =
-    BM25F.Doc {
-      BM25F.docFieldLength        = DocTermIds.fieldLength    doctermids,
-      BM25F.docFieldTermFrequency = DocTermIds.fieldTermCount doctermids,
-      BM25F.docFeatureValue       = DocFeatVals.featureValue docfeatvals
-    }
-
-pruneRelevantResults :: Int -> Int -> [DocIdSet] -> DocIdSet
-pruneRelevantResults softLimit hardLimit =
-    -- Look at the docsets starting with the smallest ones. Smaller docsets
-    -- correspond to the rarer terms, which are the ones that score most highly.
-    go DocIdSet.empty . sortBy (compare `on` DocIdSet.size)
-  where
-    go !acc [] = acc
-    go !acc (d:ds)
-        -- If this is the first one, we add it anyway, otherwise we're in
-        -- danger of returning no results at all.
-      | DocIdSet.null acc = go d ds
-        -- We consider the size our docset would be if we add this extra one...
-        -- If it puts us over the hard limit then stop.
-      | size > hardLimit  = acc
-        -- If it puts us over soft limit then we add it and stop
-      | size > softLimit  = DocIdSet.union acc d
-        -- Otherwise we can add it and carry on to consider the remainder
-      | otherwise         = go (DocIdSet.union acc d) ds
-      where
-        size = DocIdSet.size acc + DocIdSet.size d
-
------------------------------
-
-queryExplain :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                SearchEngine doc key field feature ->
-                [Term] -> [(BM25F.Explanation field feature Term, key)]
-queryExplain se@SearchEngine{ searchIndex,
-                              searchConfig     = SearchConfig{transformQueryTerm},
-                              searchRankParams = SearchRankParameters{..} }
-      terms =
-
-  -- See 'query' above for explanation. Really we ought to combine them.
-  let lookupTerms :: [Term]
-      lookupTerms = [ term'
-                    | term  <- terms
-                    , let transformForField = transformQueryTerm term
-                    , term' <- nub [ transformForField field
-                                   | field <- range (minBound, maxBound) ]
-                    ]
-
-      rawresults :: [Maybe (TermId, DocIdSet)] 
-      rawresults = map (SI.lookupTerm searchIndex) lookupTerms
-
-      termids   :: [TermId]
-      docidsets :: [DocIdSet]
-      (termids, docidsets) = unzip (catMaybes rawresults)
-
-      unrankedResults :: DocIdSet
-      unrankedResults = pruneRelevantResults
-                          paramResultsetSoftLimit
-                          paramResultsetHardLimit
-                          docidsets
-
-   in rankExplainResults se termids (DocIdSet.toList unrankedResults)
-
-rankExplainResults :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                      SearchEngine doc key field feature -> 
-                      [TermId] ->
-                      [DocId] -> 
-                      [(BM25F.Explanation field feature Term, key)]
-rankExplainResults se@SearchEngine{searchIndex} queryTerms docids =
-    sortBy (flip compare `on` (BM25F.overallScore . fst))
-      [ (explainRelevanceScore se queryTerms doctermids docfeatvals, dockey)
-      | docid <- docids
-      , let (dockey, doctermids, docfeatvals) = SI.lookupDocId searchIndex docid ]
-
-explainRelevanceScore :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
-                         SearchEngine doc key field feature ->
-                         [TermId] ->
-                         DocTermIds field ->
-                         DocFeatVals feature -> 
-                         BM25F.Explanation field feature Term
-explainRelevanceScore SearchEngine{bm25Context, searchIndex}
-                      queryTerms doctermids docfeatvals =
-    fmap (SI.getTerm searchIndex) (BM25F.explain bm25Context doc queryTerms)
-  where
-    doc = indexDocToBM25Doc doctermids docfeatvals
-
------------------------------
-
-data NoFeatures = NoFeatures
-  deriving (Eq, Ord, Bounded)
-
-instance Ix NoFeatures where
-  range   _   = []
-  inRange _ _ = False
-  index   _ _ = -1
-
-noFeatures :: NoFeatures -> a
-noFeatures _ = error "noFeatures"
+import Data.SearchEngine.Types
+import Data.SearchEngine.Update
+import Data.SearchEngine.Query
+import Data.SearchEngine.Autosuggest
 
diff --git a/Data/SearchEngine/Autosuggest.hs b/Data/SearchEngine/Autosuggest.hs
new file mode 100644
--- /dev/null
+++ b/Data/SearchEngine/Autosuggest.hs
@@ -0,0 +1,501 @@
+{-# LANGUAGE BangPatterns, NamedFieldPuns, RecordWildCards,
+             ScopedTypeVariables #-}
+
+module Data.SearchEngine.Autosuggest (
+
+    -- * Query auto-completion \/ auto-suggestion
+    queryAutosuggest,
+    ResultsFilter(..),
+
+  ) where
+
+import Data.SearchEngine.Types
+import Data.SearchEngine.Query (ResultsFilter(..))
+import qualified Data.SearchEngine.Query       as Query
+import qualified Data.SearchEngine.SearchIndex as SI
+import qualified Data.SearchEngine.DocIdSet    as DocIdSet
+import qualified Data.SearchEngine.DocTermIds  as DocTermIds
+import qualified Data.SearchEngine.BM25F       as BM25F
+
+import Data.Ix
+import Data.Ord
+import Data.List
+import Data.Maybe
+import qualified Data.Map as Map
+import qualified Data.IntSet as IntSet
+import qualified Data.Vector.Unboxed as Vec
+
+
+-- | Execute an \"auto-suggest\" query. This is where one of the search terms
+-- is an incomplete prefix and we are looking for possible completions of that
+-- search term, and result documents to go with the possible completions.
+--
+-- An auto-suggest query only gives useful results when the 'SearchEngine' is
+-- configured to use a non-term feature score. That is, when we can give
+-- documents an importance score independent of what terms we are looking for.
+-- This is because an auto-suggest query is backwards from a normal query: we
+-- are asking for relevant terms occurring in important or popular documents
+-- so we need some notion of important or popular. Without this we would just
+-- be ranking based on term frequency which while it makes sense for normal
+-- \"forward\" queries is pretty meaningless for auto-suggest \"reverse\"
+-- queries. Indeed for single-term auto-suggest queries the ranking function
+-- we use will assign 0 for all documents and completions if there is no 
+-- non-term feature scores.
+--
+queryAutosuggest :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                    SearchEngine doc key field feature ->
+                    ResultsFilter key ->
+                    [Term] -> Term -> ([(Term, Float)], [(key, Float)])
+queryAutosuggest se resultsFilter precedingTerms partialTerm =
+
+     step_external
+   . step_rank
+   . step_scoreDs
+   . step_scoreTs
+   . step_cache
+   . step_postfilterlimit
+   . step_filter
+   . step_prefilterlimit
+   . step_process
+   $ step_prep
+       precedingTerms partialTerm
+
+  where
+    -- Construct the auto-suggest query from the query terms
+    step_prep pre_ts t = mkAutosuggestQuery se pre_ts t
+
+    -- Find the appropriate subset of ts and ds
+    -- and an intermediate result that will be useful later:
+    -- { (t, ds ∩ ds_t) | t ∈ ts, ds ∩ ds_t ≠ ∅ }
+    step_process (ts, ds, pre_ts) = (ts', ds', tdss', pre_ts)
+      where
+        (tdss', ts', ds') = processAutosuggestQuery (ts, ds, pre_ts)
+
+    -- If the number of docs results is huge then we may not want to bother
+    -- and just return no results. Even the filtering of a huge number of
+    -- docs can be expensive.
+    step_prefilterlimit args@(_, ds, _, _)
+      | withinPrefilterLimit se ds = args
+      | otherwise                  = ([], DocIdSet.empty, [], [])
+
+    -- Filter ds to those that are visible for this query
+    -- and at the same time, do the docid -> docinfo lookup
+    -- (needed at this step anyway to do the filter)
+    step_filter (ts, ds, tdss, pre_ts) = (ts, ds_info, tdss, pre_ts)
+      where
+        ds_info = filterAutosuggestQuery se resultsFilter ds
+
+    -- If the number of docs results is huge then we may not want to bother
+    -- and just return no results. Scoring a large number of docs is expensive.
+    step_postfilterlimit args@(_, ds_info, _, _)
+      | withinPostfilterLimit se ds_info = args
+      | otherwise                        = ([], [], [], [])
+
+    -- For all ds, calculate and cache a couple bits of info needed
+    -- later for scoring completion terms and doc results
+    step_cache (ts, ds_info, tdss, pre_ts) = (ds_info', tdss)
+      where
+        ds_info' = cacheDocScoringInfo se ts ds_info pre_ts
+
+    -- Score the completion terms
+    step_scoreTs (ds_info, tdss) = (ds_info, tdss, ts_scored)
+      where
+        ts_scored = scoreAutosuggestQueryCompletions tdss ds_info
+
+    -- Score the doc results (making use of the completion scores)
+    step_scoreDs (ds_info, tdss, ts_scored) = (ts_scored, ds_scored)
+      where
+        ds_scored = scoreAutosuggestQueryResults tdss ds_info ts_scored
+
+    -- Rank the completions and results based on their scores
+    step_rank = sortResults
+
+    -- Convert from internal Ids into external forms: Term and doc key
+    step_external = convertIdsToExternal se
+
+
+-- We apply hard limits both before and after filtering.
+-- The post-filter limit is to avoid scoring 1000s of documents.
+-- The pre-filter limit is to avoid filtering 1000s of docs (which in some
+-- apps may be expensive itself)
+
+withinPrefilterLimit :: SearchEngine doc key field feature ->
+                        DocIdSet -> Bool
+withinPrefilterLimit SearchEngine{searchRankParams} ds =
+    DocIdSet.size ds <= paramAutosuggestPrefilterLimit searchRankParams
+
+withinPostfilterLimit :: SearchEngine doc key field feature ->
+                         [a] -> Bool
+withinPostfilterLimit SearchEngine{searchRankParams} ds_info =
+    length ds_info <= paramAutosuggestPostfilterLimit searchRankParams
+
+
+sortResults :: (Ord av, Ord bv) => ([(a,av)], [(b,bv)]) -> ([(a,av)], [(b,bv)])
+sortResults (xs, ys) =
+    ( sortBySndDescending xs
+    , sortBySndDescending ys )
+  where
+    sortBySndDescending :: Ord v => [(x,v)] -> [(x,v)]
+    sortBySndDescending = sortBy (flip (comparing snd))
+
+convertIdsToExternal :: SearchEngine doc key field feature ->
+                        ([(TermId, v)], [(DocId, v)]) -> ([(Term, v)], [(key, v)])
+convertIdsToExternal SearchEngine{searchIndex} (termids, docids) =
+    ( [ (SI.getTerm   searchIndex termid, s) | (termid, s) <- termids ]
+    , [ (SI.getDocKey searchIndex docid,  s) | (docid,  s) <- docids  ]
+    )
+
+
+-- From Bast and Weber:
+--
+--   An autocompletion query is a pair (T, D), where T is a range of terms
+--   (all possible completions of the last term which the user has started
+--   typing) and D is a set of documents (the hits for the preceding part of
+--   the query).
+--
+-- We augment this with the preceding terms because we will need these to
+-- score the set of documents D.
+--
+-- Note that the set D will be the entire collection in the case that the
+-- preceding part of the query is empty. For efficiency we represent that
+-- case specially with Maybe.
+
+type AutosuggestQuery = (Map.Map TermId DocIdSet, Maybe DocIdSet, [TermId])
+
+mkAutosuggestQuery :: (Ix field, Bounded field) =>
+                      SearchEngine doc key field feature ->
+                      [Term] -> Term -> AutosuggestQuery
+mkAutosuggestQuery se@SearchEngine{ searchIndex }
+                   precedingTerms partialTerm =
+    (completionTerms, precedingDocHits, precedingTerms')
+  where
+    completionTerms =
+      Map.unions
+        [ Map.fromList (SI.lookupTermsByPrefix searchIndex partialTerm')
+        | partialTerm' <- Query.expandTransformedQueryTerm se partialTerm
+        ]
+
+    (precedingTerms', precedingDocHits)
+      | null precedingTerms = ([], Nothing)
+      | otherwise           = fmap (Just . DocIdSet.unions)
+                                   (lookupRawResults precedingTerms)
+
+    lookupRawResults :: [Term] -> ([TermId], [DocIdSet])
+    lookupRawResults ts =
+      unzip $ catMaybes
+        [ SI.lookupTerm searchIndex t'
+        | t  <- ts
+        , t' <- Query.expandTransformedQueryTerm se t
+        ]
+
+
+
+-- From Bast and Weber:
+--
+--   To process the query means to compute the subset T' ⊆ T of terms that
+--   occur in at least one document from D, as well as the subset D' ⊆ D of
+--   documents that contain at least one of these words.
+--
+--   The obvious way to use an inverted index to process an autocompletion
+--   query (T, D) is to compute, for each t ∈ T, the intersections D ∩ Dt.
+--   Then, T' is simply the set of all t for which the intersection was
+--   non-empty, and D' is the union of all (non-empty) intersections.
+--
+-- We will do this but additionally we will return all the non-empty
+-- intersections because they will be useful when scoring.
+
+processAutosuggestQuery :: AutosuggestQuery ->
+                           ([(TermId, DocIdSet)], [TermId], DocIdSet)
+processAutosuggestQuery (completionTerms, precedingDocHits, _) =
+    ( completionTermAndDocSets
+    , completionTerms'
+    , allTermDocSet
+    )
+  where
+    -- We look up each candidate completion to find the set of documents
+    -- it appears in, and filtering (intersecting) down to just those
+    -- appearing in the existing partial query results (if any).
+    -- Candidate completions not appearing at all within the existing
+    -- partial query results are excluded at this stage.
+    --
+    -- We have to keep these doc sets for the whole process, so we keep
+    -- them as the compact DocIdSet type.
+    --
+    completionTermAndDocSets :: [(TermId, DocIdSet)]
+    completionTermAndDocSets =
+      [ (t, ds_t')
+      | (t, ds_t) <- Map.toList completionTerms
+      , let ds_t' = case precedingDocHits of
+                      Just ds -> ds `DocIdSet.intersection` ds_t
+                      Nothing -> ds_t
+      , not (DocIdSet.null ds_t')
+      ]
+
+    -- The remaining candidate completions
+    completionTerms' = [ w | (w, _ds_w) <- completionTermAndDocSets ]
+
+    -- The union of all these is this set of documents that form the results.
+    allTermDocSet :: DocIdSet
+    allTermDocSet =
+      DocIdSet.unions [ ds_t | (_t, ds_t) <- completionTermAndDocSets ]
+
+
+filterAutosuggestQuery :: SearchEngine doc key field feature ->
+                          ResultsFilter key ->
+                          DocIdSet ->
+                          [(DocId, (key, DocTermIds field, DocFeatVals feature))]
+filterAutosuggestQuery SearchEngine{ searchIndex } resultsFilter ds =
+    case resultsFilter of
+      NoFilter ->
+        [ (docid, doc)
+        | docid <- DocIdSet.toList ds
+        , let doc = SI.lookupDocId searchIndex docid ]
+
+      FilterPredicate predicate ->
+        [ (docid, doc)
+        | docid <- DocIdSet.toList ds
+        , let doc@(k,_,_) = SI.lookupDocId searchIndex docid
+        , predicate k ]
+
+      FilterBulkPredicate bulkPredicate ->
+        [ (docid, doc)
+        | let docids = DocIdSet.toList ds
+              docinf = map (SI.lookupDocId searchIndex) docids
+              keep   = bulkPredicate [ k | (k,_,_) <- docinf ]
+        , (docid, doc, True) <- zip3 docids docinf keep ]
+
+
+-- Scoring
+-------------
+--
+-- From Bast and Weber:
+--   In practice, only a selection of items from these lists can and will be
+--   presented to the user, and it is of course crucial that the most relevant
+--   completions and hits are selected.
+--
+--   A standard approach for this task in ad-hoc retrieval is to have a
+--   precomputed score for each term-in-document pair, and when a query is
+--   being processed, to aggregate these scores for each candidate document,
+--   and return documents with the highest such aggregated scores.
+--
+--   Both INV and HYB can be easily adapted to implement any such scoring and
+--   aggregation scheme: store by each term-in-document pair its precomputed
+--   score, and when intersecting, aggregate the scores. A decision has to be
+--   made on how to reconcile scores from different completions within the
+--   same document. We suggest the following: when merging the intersections
+--   (which gives the set D' according to Definition 1), compute for each
+--   document in D' the maximal score achieved for some completion in T'
+--   contained in that document, and compute for each completion in T' the
+--   maximal score achieved for a hit from D' achieved for this completion.
+--
+-- So firstly let us explore what this means and then discuss why it does not
+-- work for BM25.
+--
+-- The "precomputed score for each term-in-document pair" refers to the bm25
+-- score for this term in this document (and obviously doesn't have to be
+-- precomputed, though that'd be faster).
+--
+-- So the score for a document d ∈ D' is:
+--   maximum of score for d ∈ D ∩ Dt, for any t ∈ T'
+--
+-- While the score for a completion t ∈ T' is:
+--   maximum of score for d ∈ D ∩ Dt
+--
+-- So for documents we say their score is their best score for any of the
+-- completion terms they contain. While for completions we say their score
+-- is their best score for any of the documents they appear in.
+--
+-- For a scoring function like BM25 this appears to be not a good method, both
+-- in principle and in practice. Consider what terms get high BM25 scores:
+-- very rare ones. So this means we're going to score highly documents that
+-- contain the least frequent terms, and completions that are themselves very
+-- rare. This is silly.
+--
+-- Another important thing to note is that if we use this scoring method then
+-- we are using the BM25 score in a way that makes no sense. The BM25 score
+-- for different documents for the /same/ set of terms are comparable. The
+-- score for the same for different document with different terms are simply
+-- not comparable.
+--
+-- This also makes sense if you consider what question the BM25 score is
+-- answering: "what is the likelihood that this document is relevant given that
+-- I judge these terms to be relevant". However an auto-suggest query is
+-- different: "what is the likelihood that this term is relevant given the
+-- importance/popularity of the documents (and any preceding terms I've judged
+-- to be relevant)". They are both conditional likelihood questions but with
+-- different premises.
+--
+-- More generally, term frequency information simply isn't useful for
+-- auto-suggest queries. We don't want results that have the most obscure terms
+-- nor the most common terms, not even something in-between. Term frequency
+-- just doesn't tell us anything unless we've already judged terms to be
+-- relevant, and in an auto-suggest query we've not done that yet.
+--
+-- What we really need is information on the importance/popularity of the
+-- documents. We can actually do something with that.
+--
+-- So, instead we follow a different strategy. We require that we have
+-- importance/popularity info for the documents.
+--
+-- A first approximation would be to rank result documents by their importance
+-- and completion terms by the sum of the importance of the documents each
+-- term appears in.
+--
+-- Score for a document d ∈ D'
+--   importance score for d
+--
+-- Score for a completion t ∈ T'
+--   sum of importance score for d ∈ D ∩ Dt
+--
+-- The only problem with this is that just because a term appears in an
+-- important document, doesn't mean that term is /about/ that document, or to
+-- put it another way, that term may not be relevant for that document. For
+-- example common words like "the" likely appear in all important documents
+-- but this doesn't really tell us anything because "the" isn't an important
+-- keyword.
+--
+-- So what we want to do is to weight the document importance by the relevance
+-- of the keyword to the document. So now if we have an important document and
+-- a relevant keyword for that document then we get a high score, but an
+-- irrelevant term like "the" would get a very low weighting and so would not
+-- contribute much to the score, even for very important documents.
+--
+-- The intuition is that we will score term completions by taking the
+-- document importance weighted by the relevance of that term to that document
+-- and summing over all the documents where the term occurs.
+--
+-- We define document importance (for the set D') to be the BM25F score for
+-- the documents with any preceding terms. So this includes the non-term
+-- feature score for the importance/popularity, and also takes account of
+-- preceding terms if there were any.
+--
+-- We define term relevance (for terms in documents) to be the BM25F score for
+-- that term in that document as a fraction of the total BM25F score for all
+-- terms in the document. Thus the relevance of all terms in a document sums
+-- to 1.
+--
+-- Now we can re-weight the document importance by the term relevance:
+--
+-- Score for a completion t ∈ T'
+--   sum (for d ∈ D ∩ Dt) of ( importance for d * relevance for t in d )
+--
+-- And now for document result scores. We don't want to just stick with the
+-- innate document importance. We want to re-weight by the completion term
+-- scores:
+--
+-- Score for a document d ∈ D'
+--   sum (for t ∈ T' ∩ d) (importance score for d * score for completion t)
+--
+-- Clear as mud?
+
+type DocImportance = Float
+type TermRelevanceBreakdown = Map.Map TermId Float
+
+-- | Precompute the document importance and the term relevance breakdown for
+-- all the documents. This will be used in scoring the term completions
+-- and the result documents. They will all be used and some used many
+-- times so it's better to compute up-front and share.
+--
+-- This is actually the expensive bit (which is why we've filtered already).
+--
+cacheDocScoringInfo :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                       SearchEngine doc key field feature ->
+                       [TermId] ->
+                       [(DocId, (key, DocTermIds field, DocFeatVals feature))] ->
+                       [TermId] ->
+                       Map.Map DocId (DocImportance, TermRelevanceBreakdown)
+cacheDocScoringInfo se completionTerms allTermDocInfo precedingTerms =
+    Map.fromList
+      [ (docid, (docImportance, termRelevances))
+      | (docid, (_dockey, doctermids, docfeatvals)) <- allTermDocInfo
+      , let docImportance  = Query.relevanceScore se precedingTerms
+                                                  doctermids docfeatvals
+            termRelevances = relevanceBreakdown se doctermids docfeatvals
+                                                completionTerms
+      ]
+
+-- | Calculate the relevance of each of a given set of terms to the given
+-- document.
+--
+-- We define the \"relevance\" of each term in a document to be its
+-- term-in-document score as a fraction of the total of the scores for all
+-- terms in the document. Thus the sum of all the relevance values in the
+-- document is 1.
+--
+-- Note: we have to calculate the relevance for all terms in the document
+-- but we only keep the relevance value for the terms of interest.
+--
+relevanceBreakdown :: forall doc key field feature.
+                      (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                      SearchEngine doc key field feature ->
+                      DocTermIds field -> DocFeatVals feature ->
+                      [TermId] -> TermRelevanceBreakdown
+relevanceBreakdown SearchEngine{ bm25Context } doctermids docfeatvals ts =
+    let -- We'll calculate the bm25 score for each term in this document
+        bm25Doc     = Query.indexDocToBM25Doc doctermids docfeatvals
+
+        -- Cache the info that depends only on this doc, not the terms
+        termScore   :: (TermId -> (field -> Int) -> Float)
+        termScore   = BM25F.scoreTermsBulk bm25Context bm25Doc
+
+        -- The DocTermIds has the info we need to do bulk scoring, but it's
+        -- a sparse representation, so we first convert it to a dense table
+        term        :: Int -> TermId
+        count       :: Int -> field -> Int
+        (!numTerms, term, count) = DocTermIds.denseTable doctermids
+
+        -- We generate the vector of scores for all terms, based on looking up
+        -- the termid and the per-field counts in the dense table
+        termScores  :: Vec.Vector Float
+        !termScores = Vec.generate numTerms $ \i ->
+                       termScore (term i) (\f -> count i f)
+
+        -- We keep only the values for the terms we're interested in
+        -- and normalise so we get the relevence fraction
+        !scoreSum   = Vec.sum termScores
+        !tset       = IntSet.fromList (map fromEnum ts)
+     in Map.fromList
+      . Vec.toList
+      . Vec.map    (\(t,s) -> (t, s/scoreSum))
+      . Vec.filter (\(t,_) -> fromEnum t `IntSet.member` tset)
+      . Vec.imap   (\i s   -> (term i, s))
+      $ termScores
+
+
+scoreAutosuggestQueryCompletions :: [(TermId, DocIdSet)]
+                                 -> Map.Map DocId (Float, Map.Map TermId Float)
+                                 -> [(TermId, Float)]
+scoreAutosuggestQueryCompletions completionTermAndDocSets allTermDocInfo =
+    [ (t, candidateScore t ds_t)
+    | (t, ds_t) <- completionTermAndDocSets ]
+  where
+    -- The score for a completion is the sum of the importance of the
+    -- documents in which that completion occurs, weighted by the relevance
+    -- of the term to each document. For example we can have a very
+    -- important document and our completion term is highly relevant to it
+    -- or we could have a large number of moderately important documents
+    -- that our term is quite relevant to. In either example the completion
+    -- term would score highly.
+    candidateScore :: TermId -> DocIdSet -> Float
+    candidateScore t ds_t =
+      sum [ docImportance * termRelevence
+          | Just (docImportance, termRelevances) <-
+               map (`Map.lookup` allTermDocInfo) (DocIdSet.toList ds_t)
+          , let Just termRelevence = Map.lookup t termRelevances
+          ]
+
+
+scoreAutosuggestQueryResults :: [(TermId, DocIdSet)] ->
+                                Map.Map DocId (Float, Map.Map TermId Float) ->
+                                [(TermId, Float)] ->
+                                [(DocId, Float)]
+scoreAutosuggestQueryResults completionTermAndDocSets allTermDocInfo
+                             scoredCandidates =
+  Map.toList $ Map.fromListWith (+)
+    [ (docid, docImportance * score_t)
+    | ((_, ds_t), (_, score_t)) <- zip completionTermAndDocSets scoredCandidates
+    , let docids  = DocIdSet.toList ds_t
+          docinfo = map (`Map.lookup` allTermDocInfo) docids
+    , (docid, Just (docImportance, _)) <- zip docids docinfo
+    ]
+
diff --git a/Data/SearchEngine/BM25F.hs b/Data/SearchEngine/BM25F.hs
--- a/Data/SearchEngine/BM25F.hs
+++ b/Data/SearchEngine/BM25F.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE RecordWildCards, BangPatterns, ScopedTypeVariables #-}
 
 -- | An implementation of BM25F ranking. See:
 --
@@ -16,6 +16,8 @@
     Context(..),
     FeatureFunction(..),
     Doc(..),
+    -- ** Specialised variants
+    scoreTermsBulk,
 
     -- * Explaining the score
     Explanation(..),
@@ -23,6 +25,7 @@
   ) where
 
 import Data.Ix
+import Data.Array.Unboxed
 
 data Context term field feature = Context {
          numDocsTotal     :: !Int,
@@ -117,6 +120,53 @@
 applyFeatureFunction (LogarithmicFunction p1) = \fi -> log (p1 + fi)
 applyFeatureFunction (RationalFunction    p1) = \fi -> fi / (p1 + fi)
 applyFeatureFunction (SigmoidFunction  p1 p2) = \fi -> 1 / (p1 + exp (-fi * p2))
+
+
+-----------------------------
+-- Bulk scoring of many terms
+--
+
+-- | Most of the time we want to score several different documents for the same
+-- set of terms, but sometimes we want to score one document for many terms
+-- and in that case we can save a bit of work by doing it in bulk. It lets us
+-- calculate once and share things that depend only on the document, and not
+-- the term.
+--
+-- To take advantage of the sharing you must partially apply and name the
+-- per-doc score functon, e.g.
+--
+-- > let score :: term -> (field -> Int) -> Float
+-- >     score = BM25.bulkScorer ctx doc
+-- >  in sum [ score t (\f -> counts ! (t, f)) | t <- ts ]
+--
+scoreTermsBulk :: forall field term feature. (Ix field, Bounded field) =>
+                  Context term field feature ->
+                  Doc term field feature ->
+                  (term -> (field -> Int) -> Float)
+scoreTermsBulk ctx doc = 
+    -- This is just a rearrangement of weightedTermScore and
+    -- weightedDocTermFrequency above, with the doc-constant bits hoisted out.
+
+    \t tFreq ->
+    let !tf' = sum [ w!f * tf_f / _B!f
+                   | f <- range (minBound, maxBound)
+                   , let tf_f = fromIntegral (tFreq f)
+                   ]
+
+     in weightIDF ctx t *     tf'
+                         / (k1 + tf')
+  where
+    -- So long as the caller does the partial application thing then these
+    -- values can all be shared between many calls with different terms.
+
+    !k1 = paramK1 ctx
+    w, _B :: UArray field Float
+    !w  = array (minBound, maxBound)
+                [ (field, fieldWeight ctx field)
+                | field <- range (minBound, maxBound) ]
+    !_B = array (minBound, maxBound)
+                [ (field, lengthNorm ctx doc field)
+                | field <- range (minBound, maxBound) ]
 
 
 ------------------
diff --git a/Data/SearchEngine/DocIdSet.hs b/Data/SearchEngine/DocIdSet.hs
--- a/Data/SearchEngine/DocIdSet.hs
+++ b/Data/SearchEngine/DocIdSet.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE BangPatterns, GeneralizedNewtypeDeriving #-}
 module Data.SearchEngine.DocIdSet (
-    DocId,  
-    DocIdSet,
+    DocId(..),
+    DocIdSet(..),
     null,
     size,
     empty,
@@ -12,6 +12,8 @@
     insert,
     delete,
     union,
+    unions,
+    intersection,
     invariant,
   ) where
 
@@ -24,11 +26,11 @@
 import Control.Monad.ST
 import Data.Set (Set)
 import qualified Data.Set as Set
+import Data.List (foldl', sortBy)
+import Data.Function (on)
 
 import Prelude hiding (null)
 
---import Test.QuickCheck
---import qualified Data.List as List
 
 newtype DocId = DocId Word32
   deriving (Eq, Ord, Show, Enum, Bounded, Vec.Unbox,
@@ -94,17 +96,22 @@
           EQ -> (mid, True)
           GT -> binarySearch vec (mid+1) b key
 
+unions :: [DocIdSet] -> DocIdSet
+unions = foldl' union empty
+         -- a bit more effecient if we merge small ones first
+       . sortBy (compare `on` size)
+
 union :: DocIdSet -> DocIdSet -> DocIdSet
 union x y | null x = y
           | null y = x
 union (DocIdSet xs) (DocIdSet ys) =
-    DocIdSet (Vec.create (MVec.new sizeBound >>= writeMerged xs ys))
+    DocIdSet (Vec.create (MVec.new sizeBound >>= writeMergedUnion xs ys))
   where
     sizeBound = Vec.length xs + Vec.length ys
 
-writeMerged :: Vec.Vector DocId -> Vec.Vector DocId ->
-              MVec.MVector s DocId -> ST s (MVec.MVector s DocId)
-writeMerged xs0 ys0 out = do
+writeMergedUnion :: Vec.Vector DocId -> Vec.Vector DocId ->
+                    MVec.MVector s DocId -> ST s (MVec.MVector s DocId)
+writeMergedUnion xs0 ys0 !out = do
     i <- go xs0 ys0 0
     return $! MVec.take i out
   where
@@ -115,60 +122,34 @@
                          return (i + Vec.length xs)
       | otherwise   = let x = Vec.head xs; y = Vec.head ys
                       in case compare x y of
-                GT -> do MVec.write out i y
-                         go           xs  (Vec.tail ys) (i+1) 
-                EQ -> do MVec.write out i x
-                         go (Vec.tail xs) (Vec.tail ys) (i+1)
-                LT -> do MVec.write out i x   
-                         go (Vec.tail xs)           ys  (i+1)
-
-
--------------
--- tests
---
-{-
-instance Arbitrary DocIdSet where
-  arbitrary = fromList `fmap` (listOf arbitrary)
-
-instance Arbitrary DocId where
-  arbitrary = DocId `fmap` choose (0,15)
-
-
-prop_insert :: DocIdSet -> DocId -> Bool
-prop_insert dset x =
-    let dset' = insert x dset
-     in invariant dset && invariant dset'
-     && all (`member` dset') (x : toList dset)
-
-prop_delete :: DocIdSet -> DocId -> Bool
-prop_delete dset x =
-    let dset' = DocIdSet.delete x dset
-     in invariant dset && invariant dset'
-     && all (`member` dset') (List.delete x (toList dset))
-     && not (x `member` dset')
-
-prop_delete' :: DocIdSet -> Bool
-prop_delete' dset =
-    all (prop_delete dset) (toList dset)
-
-prop_union :: DocIdSet -> DocIdSet -> Bool
-prop_union dset1 dset2 =
-    let dset  = union dset1 dset2
-        dset' = fromList (List.union (toList dset1) (toList dset2))
+                          GT -> do MVec.write out i y
+                                   go           xs  (Vec.tail ys) (i+1)
+                          EQ -> do MVec.write out i x
+                                   go (Vec.tail xs) (Vec.tail ys) (i+1)
+                          LT -> do MVec.write out i x
+                                   go (Vec.tail xs)           ys  (i+1)
 
-     in invariant dset && invariant dset'
-     && dset == dset'
+intersection :: DocIdSet -> DocIdSet -> DocIdSet
+intersection x y | null x = empty
+                 | null y = empty
+intersection (DocIdSet xs) (DocIdSet ys) =
+    DocIdSet (Vec.create (MVec.new sizeBound >>= writeMergedIntersection xs ys))
+  where
+    sizeBound = max (Vec.length xs) (Vec.length ys)
 
-prop_union' :: DocIdSet -> DocIdSet -> Bool
-prop_union' dset1 dset2 =
-    let dset   = union dset1 dset2
-        dset'  = List.foldl' (\s i -> insert i s) dset1 (toList dset2)
-        dset'' = List.foldl' (\s i -> insert i s) dset2 (toList dset1)
-     in invariant dset && invariant dset' && invariant dset''
-     && dset == dset'
-     && dset' == dset''
+writeMergedIntersection :: Vec.Vector DocId -> Vec.Vector DocId ->
+                           MVec.MVector s DocId -> ST s (MVec.MVector s DocId)
+writeMergedIntersection xs0 ys0 !out = do
+    i <- go xs0 ys0 0
+    return $! MVec.take i out
+  where
+    go !xs !ys !i
+      | Vec.null xs = return i
+      | Vec.null ys = return i
+      | otherwise   = let x = Vec.head xs; y = Vec.head ys
+                      in case compare x y of
+                          GT ->    go           xs  (Vec.tail ys)  i
+                          EQ -> do MVec.write out i x
+                                   go (Vec.tail xs) (Vec.tail ys) (i+1)
+                          LT ->    go (Vec.tail xs)           ys   i
 
-member :: DocId -> DocIdSet -> Bool
-member x (DocIdSet vec) =
-   x `List.elem` Vec.toList vec
--}
diff --git a/Data/SearchEngine/DocTermIds.hs b/Data/SearchEngine/DocTermIds.hs
--- a/Data/SearchEngine/DocTermIds.hs
+++ b/Data/SearchEngine/DocTermIds.hs
@@ -6,6 +6,7 @@
     fieldTermCount,
     fieldElems,
     create,
+    denseTable,
     vecIndexIx,
     vecCreateIx,
   ) where
@@ -15,6 +16,7 @@
 
 import Data.Vector (Vector, (!))
 import qualified Data.Vector as Vec
+import qualified Data.Vector.Unboxed as UVec
 import Data.Ix (Ix)
 import qualified Data.Ix as Ix
 
@@ -38,14 +40,31 @@
 fieldLength docterms field =
     TermBag.size (getField docterms field)
 
--- | The frequency of a particular term in a field within the document.
-fieldTermCount :: (Ix field, Bounded field) => DocTermIds field -> field -> TermId -> Int
+-- | /O(log n)/ The frequency of a particular term in a field within the document.
+--
+fieldTermCount :: (Ix field, Bounded field) =>
+                  DocTermIds field -> field -> TermId -> Int
 fieldTermCount docterms field termid =
-    TermBag.termCount (getField docterms field) termid
+    fromIntegral (TermBag.termCount (getField docterms field) termid)
 
 fieldElems :: (Ix field, Bounded field) => DocTermIds field -> field -> [TermId]
 fieldElems docterms field =
     TermBag.elems (getField docterms field)
+
+-- | The 'DocTermIds' is really a sparse 2d array, and doing lookups with
+-- 'fieldTermCount' has a O(log n) cost. This function converts to a dense
+-- tabular representation which then enables linear scans.
+--
+denseTable :: (Ix field, Bounded field) => DocTermIds field ->
+              (Int, Int -> TermId, Int -> field -> Int)
+denseTable (DocTermIds fieldVec) =
+    let (!termids, !termcounts) = TermBag.denseTable (Vec.toList fieldVec)
+        !numTerms = UVec.length termids
+     in ( numTerms
+        , \i    -> termids UVec.! i
+        , \i ix -> let j = Ix.index (minBound, maxBound) ix
+                    in fromIntegral (termcounts UVec.! (j * numTerms + i))
+        )
 
 ---------------------------------
 -- Vector indexed by Ix Bounded
diff --git a/Data/SearchEngine/Query.hs b/Data/SearchEngine/Query.hs
new file mode 100644
--- /dev/null
+++ b/Data/SearchEngine/Query.hs
@@ -0,0 +1,257 @@
+{-# LANGUAGE BangPatterns, NamedFieldPuns, RecordWildCards #-}
+
+module Data.SearchEngine.Query (
+
+    -- * Querying
+    query,
+    ResultsFilter(..),
+
+    -- * Explain mode for query result rankings
+    queryExplain,
+    BM25F.Explanation(..),
+    setRankParams,
+
+    -- ** Utils used by autosuggest
+    relevanceScore,
+    indexDocToBM25Doc,
+    expandTransformedQueryTerm,
+  ) where
+
+import Data.SearchEngine.Types
+import qualified Data.SearchEngine.SearchIndex as SI
+import qualified Data.SearchEngine.DocIdSet    as DocIdSet
+import qualified Data.SearchEngine.DocTermIds  as DocTermIds
+import qualified Data.SearchEngine.DocFeatVals as DocFeatVals
+import qualified Data.SearchEngine.BM25F       as BM25F
+
+import Data.Ix
+import Data.List
+import Data.Function
+import Data.Maybe
+
+
+-- | Execute a normal query. Find the documents in which one or more of
+-- the search terms appear and return them in ranked order.
+--
+-- The number of documents returned is limited by the 'paramResultsetSoftLimit'
+-- and 'paramResultsetHardLimit' paramaters. This also limits the cost of the
+-- query (which is primarily the cost of scoring each document).
+--
+-- The given terms are all assumed to be complete (as opposed to prefixes
+-- like with 'queryAutosuggest').
+--
+query :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+         SearchEngine doc key field feature ->
+         [Term] -> [key]
+query se@SearchEngine{ searchIndex,
+                       searchRankParams = SearchRankParameters{..} }
+      terms =
+
+  let -- Start by transforming/normalising all the query terms.
+      -- This can be done differently for each field we search by.
+      lookupTerms :: [Term]
+      lookupTerms = concatMap (expandTransformedQueryTerm se) terms
+
+      -- Then we look up all the normalised terms in the index.
+      rawresults :: [Maybe (TermId, DocIdSet)]
+      rawresults = map (SI.lookupTerm searchIndex) lookupTerms
+
+      -- For the terms that occur in the index, this gives us the term's id
+      -- and the set of documents that the term occurs in.
+      termids   :: [TermId]
+      docidsets :: [DocIdSet]
+      (termids, docidsets) = unzip (catMaybes rawresults)
+
+      -- We looked up the documents that *any* of the term occur in (not all)
+      -- so this could be rather a lot of docs if the user uses a few common
+      -- terms. Scoring these result docs is a non-trivial cost so we want to
+      -- limit the number that we have to score. The standard trick is to
+      -- consider the doc sets in the order of size, smallest to biggest. Once
+      -- we have gone over a certain threshold of docs then don't bother with
+      -- the doc sets for the remaining terms. This tends to work because the
+      -- scoring gives lower weight to terms that occur in many documents.
+      unrankedResults :: DocIdSet
+      unrankedResults = pruneRelevantResults
+                          paramResultsetSoftLimit
+                          paramResultsetHardLimit
+                          docidsets
+
+      --TODO: technically this isn't quite correct. Because each field can
+      -- be normalised differently, we can end up with different termids for
+      -- the same original search term, and then we score those as if they
+      -- were different terms, which makes a difference when the term appears
+      -- in multiple fields (exactly the case BM25F is supposed to deal with).
+      -- What we ought to have instead is an Array (Int, field) TermId, and
+      -- make the scoring use the appropriate termid for each field, but to
+      -- consider them the "same" term.
+   in rankResults se termids (DocIdSet.toList unrankedResults)
+
+-- | Before looking up a term in the main index we need to normalise it
+-- using the 'transformQueryTerm'. Of course the transform can be different
+-- for different fields, so we have to collect all the forms (eliminating
+-- duplicates).
+--
+expandTransformedQueryTerm :: (Ix field, Bounded field) =>
+                              SearchEngine doc key field feature ->
+                              Term -> [Term]
+expandTransformedQueryTerm SearchEngine{searchConfig} term =
+    nub [ transformForField field
+        | let transformForField = transformQueryTerm searchConfig term
+        , field <- range (minBound, maxBound) ]
+
+
+rankResults :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+               SearchEngine doc key field feature ->
+               [TermId] -> [DocId] -> [key]
+rankResults se@SearchEngine{searchIndex} queryTerms docids =
+    map snd
+  $ sortBy (flip compare `on` fst)
+      [ (relevanceScore se queryTerms doctermids docfeatvals, dockey)
+      | docid <- docids
+      , let (dockey, doctermids, docfeatvals) = SI.lookupDocId searchIndex docid ]
+
+relevanceScore :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                  SearchEngine doc key field feature ->
+                  [TermId] -> DocTermIds field -> DocFeatVals feature -> Float
+relevanceScore SearchEngine{bm25Context} queryTerms doctermids docfeatvals =
+    BM25F.score bm25Context doc queryTerms
+  where
+    doc = indexDocToBM25Doc doctermids docfeatvals
+
+indexDocToBM25Doc :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                     DocTermIds field ->
+                     DocFeatVals feature ->
+                     BM25F.Doc TermId field feature
+indexDocToBM25Doc doctermids docfeatvals =
+    BM25F.Doc {
+      BM25F.docFieldLength        = DocTermIds.fieldLength    doctermids,
+      BM25F.docFieldTermFrequency = DocTermIds.fieldTermCount doctermids,
+      BM25F.docFeatureValue       = DocFeatVals.featureValue docfeatvals
+    }
+
+pruneRelevantResults :: Int -> Int -> [DocIdSet] -> DocIdSet
+pruneRelevantResults softLimit hardLimit =
+    -- Look at the docsets starting with the smallest ones. Smaller docsets
+    -- correspond to the rarer terms, which are the ones that score most highly.
+    go DocIdSet.empty . sortBy (compare `on` DocIdSet.size)
+  where
+    go !acc [] = acc
+    go !acc (d:ds)
+        -- If this is the first one, we add it anyway, otherwise we're in
+        -- danger of returning no results at all.
+      | DocIdSet.null acc = go d ds
+        -- We consider the size our docset would be if we add this extra one...
+        -- If it puts us over the hard limit then stop.
+      | size > hardLimit  = acc
+        -- If it puts us over soft limit then we add it and stop
+      | size > softLimit  = DocIdSet.union acc d
+        -- Otherwise we can add it and carry on to consider the remainder
+      | otherwise         = go (DocIdSet.union acc d) ds
+      where
+        size = DocIdSet.size acc + DocIdSet.size d
+
+
+--------------------------------
+-- Normal query with explanation
+--
+
+queryExplain :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                SearchEngine doc key field feature ->
+                [Term] -> [(BM25F.Explanation field feature Term, key)]
+queryExplain se@SearchEngine{ searchIndex,
+                              searchConfig     = SearchConfig{transformQueryTerm},
+                              searchRankParams = SearchRankParameters{..} }
+      terms =
+
+  -- See 'query' above for explanation. Really we ought to combine them.
+  let lookupTerms :: [Term]
+      lookupTerms = [ term'
+                    | term  <- terms
+                    , let transformForField = transformQueryTerm term
+                    , term' <- nub [ transformForField field
+                                   | field <- range (minBound, maxBound) ]
+                    ]
+
+      rawresults :: [Maybe (TermId, DocIdSet)]
+      rawresults = map (SI.lookupTerm searchIndex) lookupTerms
+
+      termids   :: [TermId]
+      docidsets :: [DocIdSet]
+      (termids, docidsets) = unzip (catMaybes rawresults)
+
+      unrankedResults :: DocIdSet
+      unrankedResults = pruneRelevantResults
+                          paramResultsetSoftLimit
+                          paramResultsetHardLimit
+                          docidsets
+
+   in rankExplainResults se termids (DocIdSet.toList unrankedResults)
+
+rankExplainResults :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                      SearchEngine doc key field feature ->
+                      [TermId] ->
+                      [DocId] ->
+                      [(BM25F.Explanation field feature Term, key)]
+rankExplainResults se@SearchEngine{searchIndex} queryTerms docids =
+    sortBy (flip compare `on` (BM25F.overallScore . fst))
+      [ (explainRelevanceScore se queryTerms doctermids docfeatvals, dockey)
+      | docid <- docids
+      , let (dockey, doctermids, docfeatvals) = SI.lookupDocId searchIndex docid ]
+
+
+explainRelevanceScore :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                         SearchEngine doc key field feature ->
+                         [TermId] ->
+                         DocTermIds field ->
+                         DocFeatVals feature ->
+                         BM25F.Explanation field feature Term
+explainRelevanceScore SearchEngine{bm25Context, searchIndex}
+                      queryTerms doctermids docfeatvals =
+    fmap (SI.getTerm searchIndex) (BM25F.explain bm25Context doc queryTerms)
+  where
+    doc = indexDocToBM25Doc doctermids docfeatvals
+
+
+setRankParams :: SearchRankParameters field feature ->
+                 SearchEngine doc key field feature ->
+                 SearchEngine doc key field feature
+setRankParams params@SearchRankParameters{..} se =
+    se {
+      searchRankParams = params,
+      bm25Context      = (bm25Context se) {
+        BM25F.paramK1         = paramK1,
+        BM25F.paramB          = paramB,
+        BM25F.fieldWeight     = paramFieldWeights,
+        BM25F.featureWeight   = paramFeatureWeights,
+        BM25F.featureFunction = paramFeatureFunctions
+      }
+    }
+
+
+--------------------------------
+-- Results filter
+--
+
+-- | In some applications it is necessary to enforce some security or
+-- visibility rule about the query results (e.g. in a typical DB-based
+-- application different users can see different data items). Typically
+-- it would be too expensive to build different search indexes for the
+-- different contexts and so the strategy is to use one index containing
+-- everything and filter for visibility in the results. This means the
+-- filter condition is different for different queries (e.g. performed
+-- on behalf of different users).
+--
+-- Filtering the results after a query is possible but not the most efficient
+-- thing to do because we've had to score all the not-visible documents.
+-- The better thing to do is to filter as part of the query, this way we can
+-- filter before the expensive scoring.
+--
+-- We provide one further optimisation: bulk predicates. In some applications
+-- it can be quicker to check the security\/visibility of a whole bunch of
+-- results all in one go.
+--
+data ResultsFilter key = NoFilter
+                       | FilterPredicate     (key -> Bool)
+                       | FilterBulkPredicate ([key] -> [Bool])
+--TODO: allow filtering & non-feature score lookup in one bulk op
+
diff --git a/Data/SearchEngine/SearchIndex.hs b/Data/SearchEngine/SearchIndex.hs
--- a/Data/SearchEngine/SearchIndex.hs
+++ b/Data/SearchEngine/SearchIndex.hs
@@ -12,12 +12,14 @@
 
     docCount,
     lookupTerm,
+    lookupTermsByPrefix,
     lookupTermId,
     lookupDocId,
     lookupDocKey,
-    
+
     getTerm,
-    
+    getDocKey,
+
     invariant,
   ) where
 
@@ -36,6 +38,7 @@
 import qualified Data.IntMap as IntMap
 import qualified Data.Set as Set
 import Data.Text (Text)
+import qualified Data.Text as T
 import Data.List (foldl')
 
 import Control.Exception (assert)
@@ -59,21 +62,24 @@
 -- So the mappings we maintain can be depicted as:
 --
 -- >  Term   <-- 1:1 -->   TermId
--- >                         ^
--- >                         |
--- >                     many:many
--- >                         |
--- >                         v
+-- >          \              ^
+-- >           \             |
+-- >           1:many    many:many
+-- >                \        |
+-- >                 \->     v
 -- > DocKey  <-- 1:1 -->   DocId
 --
 -- For efficiency, these details are exposed in the interface. In particular
 -- the mapping from TermId to many DocIds is exposed via a 'DocIdSet',
 -- and the mapping from DocIds to TermIds is exposed via 'DocTermIds'.
 --
+-- The main reason we need to keep the DocId -> TermId is to allow for
+-- efficient incremental updates.
+--
 data SearchIndex key field feature = SearchIndex {
        -- the indexes
        termMap           :: !(Map Term TermInfo),
-       termIdMap         :: !(IntMap Term),
+       termIdMap         :: !(IntMap TermIdInfo),
        docIdMap          :: !(IntMap (DocInfo key field feature)),
        docKeyMap         :: !(Map key DocId),
 
@@ -86,6 +92,9 @@
 data TermInfo = TermInfo !TermId !DocIdSet
   deriving Show
 
+data TermIdInfo = TermIdInfo !Term !DocIdSet
+  deriving (Show, Eq)
+
 data DocInfo key field feature = DocInfo !key !(DocTermIds field)
                                               !(DocFeatVals feature)
   deriving Show
@@ -112,12 +121,14 @@
 invariant :: (Ord key, Ix field, Bounded field) =>
              SearchIndex key field feature -> Bool
 invariant SearchIndex{termMap, termIdMap, docKeyMap, docIdMap} =
-      and [ IntMap.lookup (fromEnum termId) termIdMap == Just term
-          | (term, (TermInfo termId _)) <- Map.assocs termMap ]
+      and [ IntMap.lookup (fromEnum termId) termIdMap
+            == Just (TermIdInfo term docidset)
+          | (term, (TermInfo termId docidset)) <- Map.assocs termMap ]
   &&  and [ case Map.lookup term termMap of
-              Just (TermInfo termId' _) -> toEnum termId == termId'
-              Nothing                   -> False
-          | (termId, term) <- IntMap.assocs termIdMap ]
+              Just (TermInfo termId' docidset') -> toEnum termId == termId'
+                                                   && docidset == docidset'
+              Nothing                           -> False
+          | (termId, (TermIdInfo term docidset)) <- IntMap.assocs termIdMap ]
   &&  and [ case IntMap.lookup (fromEnum docId) docIdMap of
               Just (DocInfo docKey' _ _) -> docKey == docKey'
               Nothing                  -> False
@@ -151,14 +162,17 @@
       Nothing                         -> Nothing
       Just (TermInfo termid docidset) -> Just (termid, docidset)
 
+lookupTermsByPrefix :: SearchIndex key field feature ->
+                       Term -> [(TermId, DocIdSet)]
+lookupTermsByPrefix SearchIndex{termMap} term =
+    [ (termid, docidset)
+    | (TermInfo termid docidset) <- lookupPrefix term termMap ]
+
 lookupTermId :: SearchIndex key field feature -> TermId -> DocIdSet
-lookupTermId SearchIndex{termIdMap, termMap} termid =
+lookupTermId SearchIndex{termIdMap} termid =
     case IntMap.lookup (fromEnum termid) termIdMap of
-      Nothing   -> error $ "lookupTermId: not found " ++ show termid
-      Just term ->
-        case Map.lookup term termMap of
-          Nothing                    -> error "lookupTermId: internal error"
-          Just (TermInfo _ docidset) -> docidset
+      Nothing -> error $ "lookupTermId: not found " ++ show termid
+      Just (TermIdInfo _ docidset) -> docidset
 
 lookupDocId :: SearchIndex key field feature ->
                DocId -> (key, DocTermIds field, DocFeatVals feature)
@@ -169,7 +183,8 @@
   where
     errNotFound = error $ "lookupDocId: not found " ++ show docid
 
-lookupDocKey :: Ord key => SearchIndex key field feature -> key -> Maybe (DocTermIds field)
+lookupDocKey :: Ord key => SearchIndex key field feature ->
+                key -> Maybe (DocTermIds field)
 lookupDocKey SearchIndex{docKeyMap, docIdMap} key = do
     case Map.lookup key docKeyMap of
       Nothing    -> Nothing
@@ -181,12 +196,17 @@
 
 getTerm :: SearchIndex key field feature -> TermId -> Term
 getTerm SearchIndex{termIdMap} termId =
-    termIdMap IntMap.! fromEnum termId
+    case termIdMap IntMap.! fromEnum termId of TermIdInfo term _ -> term
 
 getTermId :: SearchIndex key field feature -> Term -> TermId
 getTermId SearchIndex{termMap} term =
     case termMap Map.! term of TermInfo termid _ -> termid
 
+getDocKey :: SearchIndex key field feature -> DocId -> key
+getDocKey SearchIndex{docIdMap} docid =
+    case docIdMap IntMap.! fromEnum docid of
+      DocInfo dockey _ _ -> dockey
+
 getDocTermIds :: SearchIndex key field feature -> DocId -> DocTermIds field
 getDocTermIds SearchIndex{docIdMap} docid =
     case docIdMap IntMap.! fromEnum docid of
@@ -315,14 +335,22 @@
 insertTermToDocIdEntry term !docid si@SearchIndex{termMap, termIdMap, nextTermId} =
     case Map.lookup term termMap of
       Nothing ->
-        let !termInfo' = TermInfo nextTermId (DocIdSet.singleton docid)
+        let docIdSet'    = DocIdSet.singleton docid
+            !termInfo'   = TermInfo nextTermId docIdSet'
+            !termIdInfo' = TermIdInfo term     docIdSet'
          in si { termMap    = Map.insert term termInfo' termMap
-               , termIdMap  = IntMap.insert (fromEnum nextTermId) term termIdMap
+               , termIdMap  = IntMap.insert (fromEnum nextTermId)
+                                            termIdInfo' termIdMap
                , nextTermId = succ nextTermId }
 
       Just (TermInfo termId docIdSet) ->
-        let !termInfo' = TermInfo termId (DocIdSet.insert docid docIdSet)
-         in si { termMap = Map.insert term termInfo' termMap }
+        let docIdSet'    = DocIdSet.insert docid docIdSet
+            !termInfo'   = TermInfo termId docIdSet'
+            !termIdInfo' = TermIdInfo term docIdSet'
+         in si { termMap   = Map.insert term termInfo' termMap
+               , termIdMap = IntMap.insert (fromEnum termId)
+                                           termIdInfo' termIdMap
+               }
 
 -- | Add multiple entries into the 'Term' to 'DocId' mapping: many terms that
 -- map to the same document.
@@ -340,12 +368,16 @@
     case  Map.lookup term termMap of
       Nothing -> si
       Just (TermInfo termId docIdSet) ->
-        let docIdSet' = DocIdSet.delete docid docIdSet
-            termInfo' = TermInfo termId docIdSet'
+        let docIdSet'    = DocIdSet.delete docid docIdSet
+            !termInfo'   = TermInfo termId docIdSet'
+            !termIdInfo' = TermIdInfo term docIdSet'
         in if DocIdSet.null docIdSet'
             then si { termMap = Map.delete term termMap
                     , termIdMap = IntMap.delete (fromEnum termId) termIdMap }
-            else si { termMap = Map.insert term termInfo' termMap }
+            else si { termMap   = Map.insert term termInfo' termMap
+                    , termIdMap = IntMap.insert (fromEnum termId)
+                                                termIdInfo' termIdMap
+                    }
 
 -- | Delete multiple entries from the 'Term' to 'DocId' mapping: many terms
 -- that map to the same document.
@@ -390,4 +422,31 @@
 deleteDocEntry docid key si@SearchIndex{docIdMap, docKeyMap} =
      si { docIdMap  = IntMap.delete (fromEnum docid) docIdMap
         , docKeyMap = Map.delete key docKeyMap }
+
+--
+-- Data.Map utils
+--
+
+-- Data.Map does not support prefix lookups directly (unlike a trie)
+-- but we can implement it reasonably efficiently using split:
+
+-- | Lookup values for a range of keys (inclusive lower bound and exclusive
+-- upper bound)
+--
+lookupRange :: Ord k => (k, k) -> Map k v -> [v]
+lookupRange (lb, ub) m =
+  let (_, mv, gt)  = Map.splitLookup lb m
+      (between, _) = Map.split       ub gt
+   in case mv of
+        Just v  -> v : Map.elems between
+        Nothing ->     Map.elems between
+
+lookupPrefix :: Text -> Map Text v -> [v]
+lookupPrefix t _ | T.null t = []
+lookupPrefix t m = lookupRange (t, prefixUpperBound t) m
+
+prefixUpperBound :: Text -> Text
+prefixUpperBound = succLast . T.dropWhileEnd (== maxBound)
+  where
+    succLast t = T.init t `T.snoc` succ (T.last t)
 
diff --git a/Data/SearchEngine/TermBag.hs b/Data/SearchEngine/TermBag.hs
--- a/Data/SearchEngine/TermBag.hs
+++ b/Data/SearchEngine/TermBag.hs
@@ -1,20 +1,32 @@
 {-# LANGUAGE BangPatterns, GeneralizedNewtypeDeriving #-}
 module Data.SearchEngine.TermBag (
-    TermId,
+    TermId(..), TermCount,
     TermBag,
     size,
     fromList,
+    toList,
     elems,
     termCount,
+    denseTable,
+    invariant
   ) where
 
-import qualified Data.Vector.Unboxed as Vec
+import qualified Data.Vector.Unboxed         as Vec
+import qualified Data.Vector.Unboxed.Mutable as MVec
+import qualified Data.Vector.Generic.Base    as VecGen
+import qualified Data.Vector.Unboxed.Base    as VecBase
+import qualified Data.Vector.Generic.Mutable as VecMut
+import Control.Monad.ST
 import qualified Data.Map as Map
-import Data.Word (Word32)
+import Data.Word (Word32, Word8)
 import Data.Bits
+import Data.List (sortBy, foldl')
+import Data.Function (on)
 
 newtype TermId = TermId Word32
-  deriving (Eq, Ord, Show, Enum)
+  deriving (Eq, Ord, Show, Enum,
+            Vec.Unbox, VecGen.Vector VecBase.Vector,
+            VecMut.MVector VecBase.MVector)
 
 instance Bounded TermId where
   minBound = TermId 0
@@ -25,6 +37,7 @@
 
 -- We sneakily stuff both the TermId and the bag count into one 32bit word
 type TermIdAndCount = Word32
+type TermCount      = Word8
 
 -- Bottom 24 bits is the TermId, top 8 bits is the bag count
 termIdAndCount :: TermId -> Int -> TermIdAndCount
@@ -35,9 +48,16 @@
 getTermId :: TermIdAndCount -> TermId
 getTermId word = TermId (word .&. 0x00FFFFFF)
 
-getTermCount :: TermIdAndCount -> Int
+getTermCount :: TermIdAndCount -> TermCount
 getTermCount word = fromIntegral (word `shiftR` 24)
 
+invariant :: TermBag -> Bool
+invariant (TermBag _ vec) =
+    strictlyAscending (Vec.toList vec)
+  where
+    strictlyAscending (a:xs@(b:_)) = getTermId a < getTermId b
+                                  && strictlyAscending xs
+    strictlyAscending _  = True
 
 size :: TermBag -> Int
 size (TermBag sz _) = sz
@@ -45,11 +65,15 @@
 elems :: TermBag -> [TermId]
 elems (TermBag _ vec) = map getTermId (Vec.toList vec)
 
-termCount :: TermBag -> TermId -> Int
+toList :: TermBag -> [(TermId, TermCount)]
+toList (TermBag _ vec) = [ (getTermId x, getTermCount x)
+                         | x <- Vec.toList vec ]
+
+termCount :: TermBag -> TermId -> TermCount
 termCount (TermBag _ vec) =
     binarySearch 0 (Vec.length vec - 1)
   where
-    binarySearch :: Int -> Int -> TermId -> Int
+    binarySearch :: Int -> Int -> TermId -> TermCount
     binarySearch !a !b !key
       | a > b     = 0
       | otherwise =
@@ -69,3 +93,117 @@
                             | (termid, freq) <- Map.toAscList bag ]
      in TermBag sz vec
 
+-- | Given a bunch of term bags, merge them into a table for easier subsequent
+-- processing. This is bascially a sparse to dense conversion. Missing entries
+-- are filled in with 0. We represent the table as one vector for the
+-- term ids and a 2d array for the counts.
+--
+-- Unfortunately vector does not directly support 2d arrays and array does
+-- not make it easy to trim arrays.
+--
+denseTable :: [TermBag] -> (Vec.Vector TermId, Vec.Vector TermCount)
+denseTable termbags = 
+    (tids, tcts)
+  where
+    -- First merge the TermIds into one array
+    -- then make a linear pass to create the counts array
+    -- filling in 0s or the counts as we find them
+    !numBags   = length termbags
+    !tids      = unionsTermId termbags
+    !numTerms  = Vec.length tids
+    !numCounts = numTerms * numBags
+    !tcts      = Vec.create (do
+                   out <- MVec.new numCounts
+                   sequence_
+                     [ writeMergedTermCounts tids bag out i
+                     | (n, TermBag _ bag) <- zip [0..] termbags
+                     , let i = n * numTerms ]
+                   return out
+                 )
+
+writeMergedTermCounts :: Vec.Vector TermId -> Vec.Vector TermIdAndCount ->
+                         MVec.MVector s TermCount -> Int -> ST s ()
+writeMergedTermCounts xs0 ys0 !out i0 =
+    -- assume xs & ys are sorted, and ys contains a subset of xs
+    go xs0 ys0 i0
+  where
+    go !xs !ys !i
+      | Vec.null ys = MVec.set (MVec.slice i (Vec.length xs) out) 0
+      | Vec.null xs = return ()
+      | otherwise   = let x   = Vec.head xs
+                          ytc = Vec.head ys
+                          y   = getTermId ytc
+                          c   = getTermCount ytc
+                      in case x == y of
+                           True  -> do MVec.write out i c
+                                       go (Vec.tail xs) (Vec.tail ys) (i+1)
+                           False -> do MVec.write out i 0
+                                       go (Vec.tail xs)           ys  (i+1)
+
+-- | Given a set of term bags, form the set of TermIds
+--
+unionsTermId :: [TermBag] -> Vec.Vector TermId
+unionsTermId tbs =
+    case sortBy (compare `on` bagVecLength) tbs of
+      []             -> Vec.empty
+      [TermBag _ xs] -> (Vec.map getTermId xs)
+      (x0:x1:xs)     -> foldl' union3 (union2 x0 x1) xs
+  where
+    bagVecLength (TermBag _ vec) = Vec.length vec
+
+union2 :: TermBag -> TermBag -> Vec.Vector TermId
+union2 (TermBag _ xs) (TermBag _ ys) =
+    Vec.create (MVec.new sizeBound >>= writeMergedUnion2 xs ys)
+  where
+    sizeBound = Vec.length xs + Vec.length ys
+
+writeMergedUnion2 :: Vec.Vector TermIdAndCount -> Vec.Vector TermIdAndCount ->
+                     MVec.MVector s TermId -> ST s (MVec.MVector s TermId)
+writeMergedUnion2 xs0 ys0 !out = do
+    i <- go xs0 ys0 0
+    return $! MVec.take i out
+  where
+    go !xs !ys !i
+      | Vec.null xs = do Vec.copy (MVec.slice i (Vec.length ys) out)
+                                  (Vec.map getTermId ys)
+                         return (i + Vec.length ys)
+      | Vec.null ys = do Vec.copy (MVec.slice i (Vec.length xs) out)
+                                  (Vec.map getTermId xs)
+                         return (i + Vec.length xs)
+      | otherwise   = let x = getTermId (Vec.head xs)
+                          y = getTermId (Vec.head ys)
+                      in case compare x y of
+                          GT -> do MVec.write out i y
+                                   go           xs  (Vec.tail ys) (i+1)
+                          EQ -> do MVec.write out i x
+                                   go (Vec.tail xs) (Vec.tail ys) (i+1)
+                          LT -> do MVec.write out i x
+                                   go (Vec.tail xs)           ys  (i+1)
+
+union3 :: Vec.Vector TermId -> TermBag -> Vec.Vector TermId
+union3 xs (TermBag _ ys) =
+    Vec.create (MVec.new sizeBound >>= writeMergedUnion3 xs ys)
+  where
+    sizeBound = Vec.length xs + Vec.length ys
+
+writeMergedUnion3 :: Vec.Vector TermId -> Vec.Vector TermIdAndCount ->
+                     MVec.MVector s TermId -> ST s (MVec.MVector s TermId)
+writeMergedUnion3 xs0 ys0 !out = do
+    i <- go xs0 ys0 0
+    return $! MVec.take i out
+  where
+    go !xs !ys !i
+      | Vec.null xs = do Vec.copy (MVec.slice i (Vec.length ys) out)
+                                  (Vec.map getTermId ys)
+                         return (i + Vec.length ys)
+      | Vec.null ys = do Vec.copy (MVec.slice i (Vec.length xs) out) xs
+                         return (i + Vec.length xs)
+      | otherwise   = let x =            Vec.head xs
+                          y = getTermId (Vec.head ys)
+                      in case compare x y of
+                          GT -> do MVec.write out i y
+                                   go           xs  (Vec.tail ys) (i+1)
+                          EQ -> do MVec.write out i x
+                                   go (Vec.tail xs) (Vec.tail ys) (i+1)
+                          LT -> do MVec.write out i x
+                                   go (Vec.tail xs)           ys  (i+1)
diff --git a/Data/SearchEngine/Types.hs b/Data/SearchEngine/Types.hs
new file mode 100644
--- /dev/null
+++ b/Data/SearchEngine/Types.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE NamedFieldPuns, RecordWildCards #-}
+
+module Data.SearchEngine.Types (
+    -- * Search engine types and helper functions
+    SearchEngine(..),
+    SearchConfig(..),
+    SearchRankParameters(..),
+    BM25F.FeatureFunction(..),
+    initSearchEngine,
+    cacheBM25Context,
+
+    -- ** Helper type for non-term features
+    NoFeatures,
+    noFeatures,
+
+    -- * Re-export SearchIndex and other types
+    SearchIndex, Term, TermId,
+    DocIdSet, DocId,
+    DocTermIds, DocFeatVals,
+
+    -- * Internal sanity check
+    invariant,
+  ) where
+
+import Data.SearchEngine.SearchIndex (SearchIndex, Term, TermId)
+import qualified Data.SearchEngine.SearchIndex as SI
+import Data.SearchEngine.DocIdSet (DocIdSet, DocId)
+import qualified Data.SearchEngine.DocIdSet as DocIdSet
+import Data.SearchEngine.DocFeatVals (DocFeatVals)
+import Data.SearchEngine.DocTermIds  (DocTermIds)
+import qualified Data.SearchEngine.BM25F as BM25F
+
+import Data.Ix
+import Data.Array.Unboxed
+
+
+
+data SearchConfig doc key field feature = SearchConfig {
+       documentKey          :: doc -> key,
+       extractDocumentTerms :: doc -> field -> [Term],
+       transformQueryTerm   :: Term -> field -> Term,
+       documentFeatureValue :: doc -> feature -> Float
+     }
+
+data SearchRankParameters field feature = SearchRankParameters {
+       paramK1                 :: !Float,
+       paramB                  :: field -> Float,
+       paramFieldWeights       :: field -> Float,
+       paramFeatureWeights     :: feature -> Float,
+       paramFeatureFunctions   :: feature -> BM25F.FeatureFunction,
+
+       paramResultsetSoftLimit   :: !Int,
+       paramResultsetHardLimit   :: !Int,
+       paramAutosuggestPrefilterLimit  :: !Int,
+       paramAutosuggestPostfilterLimit :: !Int
+     }
+
+data SearchEngine doc key field feature = SearchEngine {
+       searchIndex      :: !(SearchIndex      key field feature),
+       searchConfig     :: !(SearchConfig doc key field feature),
+       searchRankParams :: !(SearchRankParameters field feature),
+
+       -- cached info
+       sumFieldLengths :: !(UArray field Int),
+       bm25Context     :: BM25F.Context TermId field feature
+     }
+
+invariant :: (Ord key, Ix field, Bounded field) =>
+             SearchEngine doc key field feature -> Bool
+invariant SearchEngine{searchIndex} =
+    SI.invariant searchIndex
+-- && check caches
+
+initSearchEngine :: (Ix field, Bounded field, Ix feature, Bounded feature) =>
+                    SearchConfig doc key field feature ->
+                    SearchRankParameters field feature ->
+                    SearchEngine doc key field feature
+initSearchEngine config params =
+    cacheBM25Context
+      SearchEngine {
+        searchIndex      = SI.emptySearchIndex,
+        searchConfig     = config,
+        searchRankParams = params,
+        sumFieldLengths  = listArray (minBound, maxBound) (repeat 0),
+        bm25Context      = undefined
+      }
+
+cacheBM25Context :: Ix field =>
+                    SearchEngine doc key field feature ->
+                    SearchEngine doc key field feature
+cacheBM25Context
+    se@SearchEngine {
+      searchRankParams = SearchRankParameters{..},
+      searchIndex,
+      sumFieldLengths
+    }
+  = se { bm25Context = bm25Context' }
+  where
+    bm25Context' = BM25F.Context {
+      BM25F.numDocsTotal    = SI.docCount searchIndex,
+      BM25F.avgFieldLength  = \f -> fromIntegral (sumFieldLengths ! f)
+                                  / fromIntegral (SI.docCount searchIndex),
+      BM25F.numDocsWithTerm = DocIdSet.size . SI.lookupTermId searchIndex,
+      BM25F.paramK1         = paramK1,
+      BM25F.paramB          = paramB,
+      BM25F.fieldWeight     = paramFieldWeights,
+      BM25F.featureWeight   = paramFeatureWeights,
+      BM25F.featureFunction = paramFeatureFunctions
+    }
+
+
+-----------------------------
+
+data NoFeatures = NoFeatures
+  deriving (Eq, Ord, Bounded)
+
+instance Ix NoFeatures where
+  range   _   = []
+  inRange _ _ = False
+  index   _ _ = -1
+
+noFeatures :: NoFeatures -> a
+noFeatures _ = error "noFeatures"
+
diff --git a/Data/SearchEngine/Update.hs b/Data/SearchEngine/Update.hs
new file mode 100644
--- /dev/null
+++ b/Data/SearchEngine/Update.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE BangPatterns, NamedFieldPuns, RecordWildCards #-}
+
+module Data.SearchEngine.Update (
+
+    -- * Managing documents to be searched
+    insertDoc,
+    insertDocs,
+    deleteDoc,
+
+  ) where
+
+import Data.SearchEngine.Types
+import qualified Data.SearchEngine.SearchIndex as SI
+import qualified Data.SearchEngine.DocTermIds as DocTermIds
+
+import Data.Ix
+import Data.Array.Unboxed
+import Data.List
+
+
+insertDocs :: (Ord key, Ix field, Bounded field, Ix feature, Bounded feature) =>
+              [doc] ->
+              SearchEngine doc key field feature ->
+              SearchEngine doc key field feature
+insertDocs docs se = foldl' (\se' doc -> insertDoc doc se') se docs
+
+
+insertDoc :: (Ord key, Ix field, Bounded field, Ix feature, Bounded feature) =>
+             doc ->
+             SearchEngine doc key field feature ->
+             SearchEngine doc key field feature
+insertDoc doc se@SearchEngine{ searchConfig = SearchConfig {
+                                 documentKey,
+                                 extractDocumentTerms,
+                                 documentFeatureValue
+                               }
+                             , searchIndex } =
+    let key = documentKey doc
+        searchIndex' = SI.insertDoc key (extractDocumentTerms doc)
+                                        (documentFeatureValue doc)
+                                        searchIndex
+        oldDoc       = SI.lookupDocKey searchIndex  key
+        newDoc       = SI.lookupDocKey searchIndex' key
+
+     in cacheBM25Context $
+        updateCachedFieldLengths oldDoc newDoc $
+          se { searchIndex = searchIndex' }
+
+
+deleteDoc :: (Ord key, Ix field, Bounded field) =>
+             key ->
+             SearchEngine doc key field feature ->
+             SearchEngine doc key field feature
+deleteDoc key se@SearchEngine{searchIndex} =
+    let searchIndex' = SI.deleteDoc key searchIndex
+        oldDoc       = SI.lookupDocKey searchIndex key
+
+     in cacheBM25Context $
+        updateCachedFieldLengths oldDoc Nothing $
+          se { searchIndex = searchIndex' }
+
+
+updateCachedFieldLengths :: (Ix field, Bounded field) =>
+                            Maybe (DocTermIds field) -> Maybe (DocTermIds field) ->
+                            SearchEngine doc key field feature ->
+                            SearchEngine doc key field feature
+updateCachedFieldLengths Nothing (Just newDoc) se@SearchEngine{sumFieldLengths} =
+    se {
+      sumFieldLengths =
+        array (bounds sumFieldLengths)
+              [ (i, n + DocTermIds.fieldLength newDoc i)
+              | (i, n) <- assocs sumFieldLengths ]
+    }
+updateCachedFieldLengths (Just oldDoc) (Just newDoc) se@SearchEngine{sumFieldLengths} =
+    se {
+      sumFieldLengths =
+        array (bounds sumFieldLengths)
+              [ (i, n - DocTermIds.fieldLength oldDoc i
+                      + DocTermIds.fieldLength newDoc i)
+              | (i, n) <- assocs sumFieldLengths ]
+    }
+updateCachedFieldLengths (Just oldDoc) Nothing se@SearchEngine{sumFieldLengths} =
+    se {
+      sumFieldLengths =
+        array (bounds sumFieldLengths)
+              [ (i, n - DocTermIds.fieldLength oldDoc i)
+              | (i, n) <- assocs sumFieldLengths ]
+    }
+updateCachedFieldLengths Nothing Nothing se = se
+
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
 Copyright (c) 2013-2014 Duncan Coutts, 2014 Well-Typed LLP
+              2014 IRIS Connect Ltd.
 
 All rights reserved.
 
diff --git a/changelog b/changelog
new file mode 100644
--- /dev/null
+++ b/changelog
@@ -0,0 +1,19 @@
+0.2.1.0 Duncan Coutts <duncan@well-typed.com> March 2014
+        * Add auto-complete / auto-suggest feature
+        * Add demo program
+        * Moved QC props into a separate test suite
+        * Work sponsored by IRIS Connect Ltd.
+
+0.2.0.0 Duncan Coutts <duncan@well-typed.com> Feb 2014
+        * Initial version as a separate library
+
+0.1.1.0 Duncan Coutts <duncan@community.haskell.org> Nov 2013
+        * Add "explain" mode for query
+        * Add non-term feature scores
+
+0.1.0.1 Duncan Coutts <duncan@community.haskell.org> Sept 2013
+        * Fix bug in index update (thanks to Matthew Gruen)
+
+0.1.0.0 Duncan Coutts <duncan@community.haskell.org> July 2013
+        * Initial version (included in hackage-server)
+
diff --git a/demo/ExtractDescriptionTerms.hs b/demo/ExtractDescriptionTerms.hs
new file mode 100644
--- /dev/null
+++ b/demo/ExtractDescriptionTerms.hs
@@ -0,0 +1,113 @@
+
+module ExtractDescriptionTerms (
+    extractSynopsisTerms,
+    extractDescriptionTerms
+  ) where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Char
+import qualified NLP.Tokenize as NLP
+import qualified NLP.Snowball as NLP
+import Control.Monad ((>=>))
+import Data.Maybe
+
+import HaddockTypes as Haddock
+import HaddockHtml  as Haddock (markup)
+import qualified HaddockParse as Haddock (parseHaddockParagraphs)
+import qualified HaddockLex   as Haddock (tokenise)
+
+
+extractSynopsisTerms :: Set Text -> String -> [Text]
+extractSynopsisTerms stopWords =
+      NLP.stems NLP.English
+    . filter (`Set.notMember` stopWords)
+    . map (T.toCaseFold . T.pack)
+    . concatMap splitTok
+    . filter (not . ignoreTok)
+    . NLP.tokenize
+
+
+ignoreTok :: String -> Bool  
+ignoreTok = all isPunctuation
+
+splitTok :: String -> [String]
+splitTok tok =
+    case go tok of
+      toks@(_:_:_) -> tok:toks
+      toks         -> toks
+  where
+    go remaining =
+      case break (\c -> c == ')' || c == '-' || c == '/') remaining of
+        ([],      _:trailing) -> go trailing
+        (leading, _:trailing) -> leading : go trailing
+        ([],      [])         -> []
+        (leading, [])         -> leading : []
+
+
+extractDescriptionTerms :: Set Text -> String -> [Text]
+extractDescriptionTerms stopWords =
+      NLP.stems NLP.English
+    . filter (`Set.notMember` stopWords)
+    . map (T.toCaseFold . T.pack)
+    . maybe
+        [] --TODO: something here
+        (  filter (not . ignoreTok)
+         . NLP.tokenize
+         . concat . Haddock.markup termsMarkup)
+    . (Haddock.tokenise >=> Haddock.parseHaddockParagraphs)
+
+termsMarkup :: DocMarkup String [String]
+termsMarkup = Markup {
+  markupEmpty         = [],
+  markupString        = \s -> [s],
+  markupParagraph     = id,
+  markupAppend        = (++),
+  markupIdentifier    = \s -> [s],
+  markupModule        = const [], -- i.e. filter these out
+  markupEmphasis      = id,
+  markupMonospaced    = \s -> if length s > 1 then [] else s,
+  markupUnorderedList = concat,
+  markupOrderedList   = concat,
+  markupDefList       = concatMap (\(d,t) -> d ++ t),
+  markupCodeBlock     = const [],
+  markupHyperlink     = \(Hyperlink _url mLabel) -> maybeToList mLabel,
+                        --TODO: extract main part of hostname
+  markupPic           = const [],
+  markupAName         = const []
+  }
+
+{-
+-------------------
+-- Main experiment
+--
+
+main = do
+    pkgsFile <- readFile "pkgs"
+    let mostFreq :: [String]
+        pkgs     :: [PackageDescription]
+        (mostFreq, pkgs) = read pkgsFile
+    
+    stopWordsFile <- T.readFile "stopwords.txt"
+--    wordsFile <- T.readFile "/usr/share/dict/words"
+--    let ws = Set.fromList (map T.toLower $ T.lines wordsFile)
+
+
+    print "reading file"
+    evaluate (length mostFreq + length pkgs)
+    print "done"
+
+    let stopWords = Set.fromList $ T.lines stopWordsFile
+    print stopWords
+
+    sequence_
+      [ putStrLn $ display (packageName pkg) ++ ": "
+                ++ --intercalate ", "
+                   (description pkg) ++ "\n" 
+                ++ intercalate ", "
+                   (map T.unpack $ extractDescriptionTerms stopWords (description pkg)) ++ "\n"
+      | pkg <- pkgs
+      , let pkgname = display (packageName pkg) ]
+-}
diff --git a/demo/ExtractNameTerms.hs b/demo/ExtractNameTerms.hs
new file mode 100644
--- /dev/null
+++ b/demo/ExtractNameTerms.hs
@@ -0,0 +1,181 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module ExtractNameTerms (
+    extractPackageNameTerms,
+    extractModuleNameTerms,
+  ) where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Char (isUpper, isDigit)
+import Data.List
+import Data.List.Split hiding (Splitter)
+import Data.Maybe (maybeToList)
+
+import Data.Functor.Identity
+import Control.Monad
+import Control.Monad.List
+import Control.Monad.Writer
+import Control.Monad.State
+
+
+extractModuleNameTerms :: String -> [Text]
+extractModuleNameTerms modname =
+  map T.toCaseFold $
+  nub $
+  map T.pack $
+  flip runSplitter modname $ do
+    _ <- forEachPart splitDot
+    _ <- forEachPart splitCamlCase
+    satisfy (not . singleChar)
+    get >>= emit
+
+extractPackageNameTerms :: String -> [Text]
+extractPackageNameTerms pkgname =
+  map T.toCaseFold $
+  nub $
+  map T.pack $
+  flip runSplitter pkgname $ do
+
+    fstComponentHyphen <- forEachPart splitHyphen
+
+    satisfy (`notElem` ["hs", "haskell"])
+
+    _ <- forEachPart stripPrefixH
+
+    fstComponentCaml <- forEachPart splitCamlCase
+
+    fstComponent2 <- forEachPart splitOn2
+
+    when (fstComponentHyphen && fstComponentCaml && fstComponent2) $ do
+      forEachPartAndWhole stripPrefix_h
+    _ <- forEachPart (maybeToList . stripPrefix "lib")
+    _ <- forEachPart (maybeToList . stripSuffix "lib")
+    _ <- forEachPart stripSuffixNum
+    satisfy (not . singleChar)
+
+    get >>= emit
+
+newtype Split a = Split (StateT String (ListT (WriterT [String] Identity)) a)
+  deriving (Monad, MonadPlus, MonadState String)
+
+emit :: String -> Split ()
+emit x = Split (lift (lift (tell [x])))
+
+forEach :: [a] -> Split a
+forEach = msum . map return
+
+runSplitter :: Split () -> String -> [String]
+runSplitter (Split m) s = snd (runWriter (runListT (runStateT m s)))
+
+singleChar :: String -> Bool
+singleChar [_] = True
+singleChar _   = False
+
+satisfy :: (String -> Bool) -> Split ()
+satisfy p = get >>= guard . p
+
+forEachPart :: (String -> [String]) -> Split Bool
+forEachPart parts = do
+  t <- get
+  case parts t of
+    []             -> return True
+    [t'] | t == t' -> return True
+    ts             -> do emit t
+                         (t', n) <- forEach (zip ts [1::Int ..])
+                         put t'
+                         return (n==1)
+
+forEachPartAndWhole :: (String -> [String]) -> Split ()
+forEachPartAndWhole parts = do
+  t <- get
+  case parts t of
+    []             -> return ()
+    ts             -> forEach (t:ts) >>= put
+  
+
+splitDot :: String -> [String]
+splitDot = split (dropBlanks $ dropDelims $ whenElt (=='.'))
+
+splitHyphen :: String -> [String]
+splitHyphen = split (dropBlanks $ dropDelims $ whenElt (=='-'))
+
+splitCamlCase :: String -> [String]
+splitCamlCase = split (dropInitBlank $ condense $ keepDelimsL $ whenElt isUpper)
+
+stripPrefixH :: String -> [String]
+stripPrefixH ('H':'S':frag)   | all isUpper frag = [frag]
+stripPrefixH "HTTP"                              = []
+stripPrefixH ('H':frag@(c:_)) | isUpper c        = [frag]
+stripPrefixH _                                   = []
+
+stripPrefix_h :: String -> [String]
+stripPrefix_h "http"         = []
+stripPrefix_h "html"         = []
+stripPrefix_h ('h':'s':frag) = ['s':frag, frag]
+stripPrefix_h ('h':frag) {- | Set.notMember (T.pack w) ws -} = [frag]
+stripPrefix_h _              = []
+
+stripSuffix :: String -> String -> Maybe String
+stripSuffix s t = fmap reverse (stripPrefix (reverse s) (reverse t))
+
+stripSuffixNum :: String -> [String]
+stripSuffixNum s
+  | null rd || null rs' = []
+  | otherwise           = [s', d]
+  where
+    rs        = reverse s
+    (rd, rs') = span isDigit rs
+    d         = reverse rd
+    s'        = reverse rs'
+
+splitOn2 :: String -> [String]
+splitOn2 t =
+  case break (=='2') t of
+    (from@(_:_), '2':to@(c:_))
+      | not (isDigit c)
+      , not (length from == 1 && length to == 1)
+      -> [from, to]
+    _ -> []
+
+{-
+-------------------
+-- Main experiment
+--
+
+main = do
+    pkgsFile <- readFile "pkgs"
+    let mostFreq :: [String]
+        pkgs     :: [PackageDescription]
+        (mostFreq, pkgs) = read pkgsFile
+        
+--    wordsFile <- T.readFile "/usr/share/dict/words"
+--    let ws = Set.fromList (map T.toLower $ T.lines wordsFile)
+
+
+    print "reading file"
+    evaluate (length mostFreq + length pkgs)
+    print "done"
+
+    sequence_
+      [ putStrLn $ display (packageName pkg) ++ " -> "
+                ++ intercalate ", " (map T.unpack $ extractNameTerms pkgname)
+      | pkg <- pkgs
+      , let pkgname = display (packageName pkg) ]
+
+main = do
+    pkgsFile <- readFile "pkgs3"
+    let pkgs     :: [PackageDescription]
+        pkgs = map read (lines pkgsFile)
+
+--    print "forcing pkgs..."
+--    evaluate (foldl' (\a p -> seq p a) () pkgs)
+
+    sequence_
+      [ putStrLn $ display (packageName pkg) ++ ": " ++ display mod ++ " -> "
+                ++ intercalate ", " (map T.unpack $ extractModuleNameTerms (display mod))
+      | pkg <- pkgs
+      , Just lib <- [library pkg]
+      , let mods = exposedModules lib
+      , mod <- mods ]
+-}
diff --git a/demo/HaddockHtml.hs b/demo/HaddockHtml.hs
new file mode 100644
--- /dev/null
+++ b/demo/HaddockHtml.hs
@@ -0,0 +1,84 @@
+-- stolen from Haddock's Util.hs and Doc.hs
+module HaddockHtml (markup, docAppend, docParagraph) where
+
+import HaddockTypes
+import Data.Char                (isSpace)
+
+markup :: DocMarkup id a -> Doc id -> a
+markup m DocEmpty               = markupEmpty m
+markup m (DocAppend d1 d2)      = markupAppend m (markup m d1) (markup m d2)
+markup m (DocString s)          = markupString m s
+markup m (DocParagraph d)       = markupParagraph m (markup m d)
+markup m (DocIdentifier x)      = markupIdentifier m x
+markup m (DocModule mod0)       = markupModule m mod0
+markup m (DocEmphasis d)        = markupEmphasis m (markup m d)
+markup m (DocMonospaced d)      = markupMonospaced m (markup m d)
+markup m (DocUnorderedList ds)  = markupUnorderedList m (map (markup m) ds)
+markup m (DocOrderedList ds)    = markupOrderedList m (map (markup m) ds)
+markup m (DocDefList ds)        = markupDefList m (map (markupPair m) ds)
+markup m (DocCodeBlock d)       = markupCodeBlock m (markup m d)
+markup m (DocHyperlink l)       = markupHyperlink m l
+markup m (DocAName ref)         = markupAName m ref
+markup m (DocPic img)           = markupPic m img
+
+markupPair :: DocMarkup id a -> (Doc id, Doc id) -> (a, a)
+markupPair m (a,b) = (markup m a, markup m b)
+
+-- -----------------------------------------------------------------------------
+-- ** Smart constructors
+
+-- used to make parsing easier; we group the list items later
+docAppend :: Doc id -> Doc id -> Doc id
+docAppend (DocUnorderedList ds1) (DocUnorderedList ds2)
+  = DocUnorderedList (ds1++ds2)
+docAppend (DocUnorderedList ds1) (DocAppend (DocUnorderedList ds2) d)
+  = DocAppend (DocUnorderedList (ds1++ds2)) d
+docAppend (DocOrderedList ds1) (DocOrderedList ds2)
+  = DocOrderedList (ds1++ds2)
+docAppend (DocOrderedList ds1) (DocAppend (DocOrderedList ds2) d)
+  = DocAppend (DocOrderedList (ds1++ds2)) d
+docAppend (DocDefList ds1) (DocDefList ds2)
+  = DocDefList (ds1++ds2)
+docAppend (DocDefList ds1) (DocAppend (DocDefList ds2) d)
+  = DocAppend (DocDefList (ds1++ds2)) d
+docAppend DocEmpty d = d
+docAppend d DocEmpty = d
+docAppend d1 d2
+  = DocAppend d1 d2
+
+
+-- again to make parsing easier - we spot a paragraph whose only item
+-- is a DocMonospaced and make it into a DocCodeBlock
+docParagraph :: Doc id -> Doc id
+docParagraph (DocMonospaced p)
+  = DocCodeBlock (docCodeBlock p)
+docParagraph (DocAppend (DocString s1) (DocMonospaced p))
+  | all isSpace s1
+  = DocCodeBlock (docCodeBlock p)
+docParagraph (DocAppend (DocString s1)
+    (DocAppend (DocMonospaced p) (DocString s2)))
+  | all isSpace s1 && all isSpace s2
+  = DocCodeBlock (docCodeBlock p)
+docParagraph (DocAppend (DocMonospaced p) (DocString s2))
+  | all isSpace s2
+  = DocCodeBlock (docCodeBlock p)
+docParagraph p
+  = DocParagraph p
+
+
+-- Drop trailing whitespace from @..@ code blocks.  Otherwise this:
+--
+--    -- @
+--    -- foo
+--    -- @
+--
+-- turns into (DocCodeBlock "\nfoo\n ") which when rendered in HTML
+-- gives an extra vertical space after the code block.  The single space
+-- on the final line seems to trigger the extra vertical space.
+--
+docCodeBlock :: Doc id -> Doc id
+docCodeBlock (DocString s)
+  = DocString (reverse $ dropWhile (`elem` " \t") $ reverse s)
+docCodeBlock (DocAppend l r)
+  = DocAppend l (docCodeBlock r)
+docCodeBlock d = d
diff --git a/demo/HaddockLex.x b/demo/HaddockLex.x
new file mode 100644
--- /dev/null
+++ b/demo/HaddockLex.x
@@ -0,0 +1,189 @@
+--
+-- Haddock - A Haskell Documentation Tool
+--
+-- (c) Simon Marlow 2002
+--
+
+{
+-- Disable warnings that the generated code causes
+{-# OPTIONS_GHC -fno-warn-deprecated-flags
+                -fno-warn-unused-binds
+                -fno-warn-unused-imports
+                -fno-warn-unused-matches
+                -fno-warn-missing-signatures
+                -fno-warn-tabs #-}
+module HaddockLex (
+        Token(..),
+        tokenise
+ ) where
+
+import Data.Char
+import Data.Word (Word8)
+import qualified Data.Bits
+import Numeric
+import Control.Monad (liftM)
+import HaddockTypes (RdrName)
+}
+
+$ws    = $white # \n
+$digit = [0-9]
+$hexdigit = [0-9a-fA-F]
+$special =  [\"\@]
+$alphanum = [A-Za-z0-9]
+$ident    = [$alphanum \'\_\.\!\#\$\%\&\*\+\/\<\=\>\?\@\\\\\^\|\-\~\:]
+
+:-
+
+-- beginning of a paragraph
+<0,para> {
+ $ws* \n                ;
+ $ws* \>                { begin birdtrack }
+ $ws* [\*\-]            { token TokBullet `andBegin` string }
+ $ws* \[                { token TokDefStart `andBegin` def }
+ $ws* \( $digit+ \)     { token TokNumber `andBegin` string }
+ $ws*                   { begin string }
+}
+
+-- beginning of a line
+<line> {
+  $ws* \>               { begin birdtrack }
+  $ws* \n               { token TokPara `andBegin` para }
+  -- Here, we really want to be able to say
+  -- $ws* (\n | <eof>)  { token TokPara `andBegin` para}
+  -- because otherwise a trailing line of whitespace will result in
+  -- a spurious TokString at the end of a docstring.  We don't have <eof>,
+  -- though (NOW I realise what it was for :-).  To get around this, we always
+  -- append \n to the end of a docstring.
+  ()                    { begin string }
+}
+
+<birdtrack> .*  \n?     { strtokenNL TokBirdTrack `andBegin` line }
+
+<string,def> {
+  $special                      { strtoken $ \s -> TokSpecial (head s) }
+  \<\< [^\<\>]* \>\>            { strtoken $ \s -> TokPic (init $ init $ tail $ tail s) }
+  \< [^\<\>]* \>                { strtoken $ \s -> TokURL (init (tail s)) }
+  \# [^\#]* \#                  { strtoken $ \s -> TokAName (init (tail s)) }
+  \/ [^\/]* \/                  { strtoken $ \s -> TokEmphasis (init (tail s)) }
+  [\'\`] $ident+ [\'\`]         { ident }
+  \\ .                          { strtoken (TokString . tail) }
+  "&#" $digit+ \;               { strtoken $ \s -> TokString [chr (read (init (drop 2 s)))] }
+  "&#" [xX] $hexdigit+ \;       { strtoken $ \s -> case readHex (init (drop 3 s)) of [(n,_)] -> TokString [chr n]; _ -> error "hexParser: Can't happen" }
+  -- allow special characters through if they don't fit one of the previous
+  -- patterns.
+  [\/\'\`\<\#\&\\]                      { strtoken TokString }
+  [^ $special \/ \< \# \n \'\` \& \\ \]]* \n { strtokenNL TokString `andBegin` line }
+  [^ $special \/ \< \# \n \'\` \& \\ \]]+    { strtoken TokString }
+}
+
+<def> {
+  \]                            { token TokDefEnd `andBegin` string }
+}
+
+-- ']' doesn't have any special meaning outside of the [...] at the beginning
+-- of a definition paragraph.
+<string> {
+  \]                            { strtoken TokString }
+}
+
+{
+data Token
+  = TokPara
+  | TokNumber
+  | TokBullet
+  | TokDefStart
+  | TokDefEnd
+  | TokSpecial Char
+  | TokIdent RdrName
+  | TokString String
+  | TokURL String
+  | TokPic String
+  | TokEmphasis String
+  | TokAName String
+  | TokBirdTrack String
+  deriving Show
+
+-- -----------------------------------------------------------------------------
+-- Alex support stuff
+
+type StartCode = Int
+type Action = String -> StartCode -> (StartCode -> Maybe [Token]) -> Maybe [Token]
+
+--TODO: we ought to switch to ByteString input.
+type AlexInput = (Char, [Word8], String)
+
+-- | For alex >= 3
+--
+-- See also alexGetChar
+alexGetByte :: AlexInput -> Maybe (Word8,AlexInput)
+alexGetByte (c,(b:bs),s) = Just (b,(c,bs,s))
+alexGetByte (c,[],[])    = Nothing
+alexGetByte (_,[],(c:s)) = case utf8Encode c of
+                             (b:bs) -> Just (b, (c, bs, s))
+
+-- | Encode a Haskell String to a list of Word8 values, in UTF8 format.
+utf8Encode :: Char -> [Word8]
+utf8Encode = map fromIntegral . go . ord
+ where
+  go oc
+   | oc <= 0x7f       = [oc]
+
+   | oc <= 0x7ff      = [ 0xc0 + (oc `Data.Bits.shiftR` 6)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+
+   | oc <= 0xffff     = [ 0xe0 + (oc `Data.Bits.shiftR` 12)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+   | otherwise        = [ 0xf0 + (oc `Data.Bits.shiftR` 18)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 12) Data.Bits..&. 0x3f)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+
+-- | For alex < 3
+--
+-- See also alexGetByte
+alexGetChar :: AlexInput -> Maybe (Char, AlexInput)
+alexGetChar (_, _, [])   = Nothing
+alexGetChar (_, _, c:cs) = Just (c, (c,[],cs))
+
+alexInputPrevChar (c,_) = c
+
+tokenise :: String -> Maybe [Token]
+tokenise str =
+    go ('\n', [], eofHack str) para
+  where
+    go inp@(_,_,str') sc =
+          case alexScan inp sc of
+                AlexEOF -> Just []
+                AlexError _ -> Nothing
+                AlexSkip  inp' _       -> go inp' sc
+                AlexToken inp' len act -> act (take len str') sc (\sc' -> go inp' sc')
+
+-- NB. we add a final \n to the string, (see comment in the beginning of line
+-- production above).
+eofHack str = str++"\n"
+
+andBegin  :: Action -> StartCode -> Action
+andBegin act new_sc = \str _ cont -> act str new_sc cont
+
+token :: Token -> Action
+token t = \_ sc cont -> liftM (t :) (cont sc)
+
+strtoken, strtokenNL :: (String -> Token) -> Action
+strtoken t = \str sc cont -> liftM (t str :) (cont sc)
+strtokenNL t = \str sc cont -> liftM (t (filter (/= '\r') str) :) (cont sc)
+-- ^ We only want LF line endings in our internal doc string format, so we
+-- filter out all CRs.
+
+begin :: StartCode -> Action
+begin sc = \_ _ cont -> cont sc
+
+-- -----------------------------------------------------------------------------
+-- Lex a string as a Haskell identifier
+
+ident :: Action
+ident str sc cont = liftM (TokIdent str :) (cont sc)
+}
diff --git a/demo/HaddockParse.y b/demo/HaddockParse.y
new file mode 100644
--- /dev/null
+++ b/demo/HaddockParse.y
@@ -0,0 +1,117 @@
+{
+-- Disable warnings that the generated code causes
+{-# OPTIONS_GHC -fno-warn-deprecated-flags
+                -fno-warn-missing-signatures
+                -fno-warn-unused-binds
+                -fno-warn-unused-matches
+                -fno-warn-lazy-unlifted-bindings
+                -fno-warn-name-shadowing
+                -fno-warn-incomplete-patterns
+                -fno-warn-tabs #-}
+module HaddockParse (parseHaddockParagraphs) where
+
+import HaddockLex
+import HaddockHtml
+import HaddockTypes
+import Data.Char  (isSpace)
+}
+
+%expect 0
+
+%tokentype { Token }
+
+%token
+        '@'     { TokSpecial '@' }
+        '['     { TokDefStart }
+        ']'     { TokDefEnd }
+        DQUO    { TokSpecial '\"' }
+        URL     { TokURL $$ }
+        PIC     { TokPic $$ }
+        ANAME   { TokAName $$ }
+        '/../'  { TokEmphasis $$ }
+        '-'     { TokBullet }
+        '(n)'   { TokNumber }
+        '>..'   { TokBirdTrack $$ }
+        IDENT   { TokIdent $$ }
+        PARA    { TokPara }
+        STRING  { TokString $$ }
+
+%monad { Maybe }
+
+%name parseHaddockParagraphs  doc
+%name parseHaddockString seq
+
+%%
+
+doc     :: { Doc RdrName }
+        : apara PARA doc        { docAppend $1 $3 }
+        | PARA doc              { $2 }
+        | apara                 { $1 }
+        | {- empty -}           { DocEmpty }
+
+apara   :: { Doc RdrName }
+        : ulpara                { DocUnorderedList [$1] }
+        | olpara                { DocOrderedList [$1] }
+        | defpara               { DocDefList [$1] }
+        | para                  { $1 }
+
+ulpara  :: { Doc RdrName }
+        : '-' para              { $2 }
+
+olpara  :: { Doc RdrName }
+        : '(n)' para            { $2 }
+
+defpara :: { (Doc RdrName, Doc RdrName) }
+        : '[' seq ']' seq       { ($2, $4) }
+
+para    :: { Doc RdrName }
+        : seq                   { docParagraph $1 }
+        | codepara              { DocCodeBlock $1 }
+
+codepara :: { Doc RdrName }
+        : '>..' codepara        { docAppend (DocString $1) $2 }
+        | '>..'                 { DocString $1 }
+
+seq     :: { Doc RdrName }
+        : elem seq              { docAppend $1 $2 }
+        | elem                  { $1 }
+
+elem    :: { Doc RdrName }
+        : elem1                 { $1 }
+        | '@' seq1 '@'          { DocMonospaced $2 }
+
+seq1    :: { Doc RdrName }
+        : PARA seq1             { docAppend (DocString "\n") $2 }
+        | elem1 seq1            { docAppend $1 $2 }
+        | elem1                 { $1 }
+
+elem1   :: { Doc RdrName }
+        : STRING                { DocString $1 }
+        | '/../'                { DocEmphasis (DocString $1) }
+        | URL                   { DocHyperlink (makeHyperlink $1) }
+        | PIC                   { DocPic $1 }
+        | ANAME                 { DocAName $1 }
+        | IDENT                 { DocIdentifier $1 }
+        | DQUO strings DQUO     { DocModule $2 }
+
+strings  :: { String }
+        : STRING                { $1 }
+        | STRING strings        { $1 ++ $2 }
+
+{
+happyError :: [Token] -> Maybe a
+happyError toks = Nothing
+
+-- | Create a `Hyperlink` from given string.
+--
+-- A hyperlink consists of a URL and an optional label.  The label is separated
+-- from the url by one or more whitespace characters.
+makeHyperlink :: String -> Hyperlink
+makeHyperlink input = case break isSpace $ strip input of
+  (url, "")    -> Hyperlink url Nothing
+  (url, label) -> Hyperlink url (Just . dropWhile isSpace $ label)
+
+-- | Remove all leading and trailing whitespace
+strip :: String -> String
+strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse
+}
diff --git a/demo/HaddockTypes.hs b/demo/HaddockTypes.hs
new file mode 100644
--- /dev/null
+++ b/demo/HaddockTypes.hs
@@ -0,0 +1,48 @@
+-- stolen from Haddock's Types.hs
+module HaddockTypes where
+
+data Doc id
+  = DocEmpty
+  | DocAppend (Doc id) (Doc id)
+  | DocString String
+  | DocParagraph (Doc id)
+  | DocIdentifier id
+  | DocModule String
+  | DocEmphasis (Doc id)
+  | DocMonospaced (Doc id)
+  | DocUnorderedList [Doc id]
+  | DocOrderedList [Doc id]
+  | DocDefList [(Doc id, Doc id)]
+  | DocCodeBlock (Doc id)
+  | DocHyperlink Hyperlink
+  | DocPic String
+  | DocAName String
+
+data Hyperlink = Hyperlink
+  { hyperlinkUrl   :: String
+  , hyperlinkLabel :: Maybe String
+  } deriving (Eq, Show)
+
+-- | DocMarkup is a set of instructions for marking up documentation.
+-- In fact, it's really just a mapping from 'Doc' to some other
+-- type [a], where [a] is usually the type of the output (HTML, say).
+
+data DocMarkup id a = Markup {
+  markupEmpty         :: a,
+  markupString        :: String -> a,
+  markupParagraph     :: a -> a,
+  markupAppend        :: a -> a -> a,
+  markupIdentifier    :: id -> a,
+  markupModule        :: String -> a,
+  markupEmphasis      :: a -> a,
+  markupMonospaced    :: a -> a,
+  markupUnorderedList :: [a] -> a,
+  markupOrderedList   :: [a] -> a,
+  markupDefList       :: [(a,a)] -> a,
+  markupCodeBlock     :: a -> a,
+  markupHyperlink     :: Hyperlink -> a,
+  markupAName         :: String -> a,
+  markupPic           :: String -> a
+  }
+
+type RdrName = String
diff --git a/demo/Main.hs b/demo/Main.hs
new file mode 100644
--- /dev/null
+++ b/demo/Main.hs
@@ -0,0 +1,93 @@
+module Main where
+
+import Data.SearchEngine
+import PackageSearch
+
+import PackageIndexUtils
+
+import Data.List
+import qualified Data.Map as Map
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+import Data.Time
+
+import Control.Monad
+import Control.Exception
+import System.IO
+import System.Directory
+import System.Exit
+
+import Distribution.PackageDescription (PackageDescription)
+import Distribution.PackageDescription.Configuration (flattenPackageDescription)
+import Distribution.Package (packageVersion, packageName)
+import Distribution.Text (display)
+
+
+-------------------
+-- Main experiment
+--
+
+main :: IO ()
+main = do
+    putStrLn "reading 00-index.tar..."
+    pkgs <- readPackages
+
+    putStrLn "forcing pkgs..."
+    evaluate (foldl' (\a p -> seq p a) () pkgs)
+
+    let searchengine = insertDocs pkgs initialPkgSearchEngine
+
+    putStrLn "constructing index..."
+    printTiming "done" $
+      evaluate searchengine >> return ()
+    putStrLn $ "search engine invariant: " ++ show (invariant searchengine)
+
+--    print [ avgFieldLength ctx s | s <- [minBound..maxBound] ]
+
+--    print $ take 100 $ sortBy (flip compare) $ map Set.size $ Map.elems (termMap searchindex)
+--    T.putStr $ T.unlines $ Map.keys (termMap searchindex)
+--    let SearchEngine{searchIndex=SearchIndex{termMap, termIdMap, docKeyMap, docIdMap}} = searchengine
+--    print (Map.size termMap, IntMap.size termIdMap, Map.size docKeyMap, IntMap.size docIdMap)
+
+    let loop = do
+          putStr "search term> "
+          hFlush stdout 
+          t <- T.getLine
+          unless (T.null t) $ do
+            putStrLn "Ranked results:"
+            let rankedResults = query searchengine (T.words t)
+
+            putStr $ unlines
+              [ {-show rank ++ ": " ++ -}display pkgname
+              | ({-rank, -}pkgname) <- take 10 rankedResults ]
+
+            loop
+    return ()
+    loop
+
+printTiming :: String -> IO () -> IO ()
+printTiming msg action = do
+    t   <- getCurrentTime
+    action
+    t'  <- getCurrentTime
+    putStrLn (msg ++ ". time: " ++ show (diffUTCTime t' t))
+
+readPackages :: IO [PackageDescription]
+readPackages = do
+  exists <- doesFileExist "00-index.tar"
+  when (not exists) $ do
+    putStrLn "This program needs a 00-index.tar package index."
+    putStrLn "Please grab 00-index.tar.gz from hackage and gunzip it."
+    exitFailure
+
+  pkgs <- PackageIndexUtils.readPackageIndexFile "00-index.tar"
+  let latestPkgs = Map.fromListWith
+                     (\a b -> if packageVersion (fst a) > packageVersion (fst b)
+                                then a else b)
+                     [ (packageName pkgid, (pkgid, pkg))
+                     | (pkgid, pkg) <- pkgs ]
+
+  return . map (flattenPackageDescription . snd)
+         . Map.elems
+         $ latestPkgs
+
diff --git a/demo/PackageIndexUtils.hs b/demo/PackageIndexUtils.hs
new file mode 100644
--- /dev/null
+++ b/demo/PackageIndexUtils.hs
@@ -0,0 +1,86 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Distribution.Client.IndexUtils
+-- Copyright   :  (c) Duncan Coutts 2008
+-- License     :  BSD-like
+--
+-- Maintainer  :  duncan@community.haskell.org
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Extra utils related to the package indexes.
+-----------------------------------------------------------------------------
+module PackageIndexUtils (
+    readPackageIndexFile
+  ) where
+
+import qualified Codec.Archive.Tar as Tar
+
+import Distribution.Package
+         ( PackageId, PackageIdentifier(..), PackageName(..) )
+import Distribution.PackageDescription
+         ( GenericPackageDescription )
+import Distribution.PackageDescription.Parse
+         ( parsePackageDescription )
+import Distribution.ParseUtils
+         ( ParseResult(..) )
+import Distribution.Text
+         ( simpleParse )
+import Distribution.Simple.Utils
+         ( fromUTF8 )
+
+import Data.Maybe (fromMaybe)
+import qualified Data.ByteString.Lazy as BS
+import qualified Data.ByteString.Lazy.Char8 as BS.Char8
+import Data.ByteString.Lazy (ByteString)
+import System.FilePath (takeExtension, splitDirectories, normalise)
+
+
+readPackageIndexFile :: FilePath -> IO [(PackageId, GenericPackageDescription)]
+readPackageIndexFile indexFile =
+      either fail return
+    . parsePackageIndex
+  =<< BS.readFile indexFile
+
+-- | Parse an uncompressed \"00-index.tar\" repository index file represented
+-- as a 'ByteString'.
+--
+parsePackageIndex :: ByteString
+                  -> Either String [(PackageId, GenericPackageDescription)]
+parsePackageIndex = accum [] . Tar.read
+  where
+    accum pkgs es = case es of
+      Tar.Fail err   -> Left  (show err)
+      Tar.Done       -> Right (reverse pkgs)
+      Tar.Next e es' -> accum pkgs' es'
+        where
+          pkgs' = extract pkgs e
+
+    extract pkgs entry =
+       fromMaybe pkgs $ tryExtractPkg
+      where
+        tryExtractPkg = do
+          (pkgid, pkg) <- extractPkg entry
+          return ((pkgid, pkg):pkgs)
+
+
+extractPkg :: Tar.Entry -> Maybe (PackageId, GenericPackageDescription)
+extractPkg entry = case Tar.entryContent entry of
+  Tar.NormalFile content _
+     | takeExtension fileName == ".cabal"
+    -> case splitDirectories (normalise fileName) of
+        [pkgname,vers,_] -> case simpleParse vers of
+          Just ver -> Just (pkgid, descr)
+            where
+              pkgid  = PackageIdentifier (PackageName pkgname) ver
+              parsed = parsePackageDescription . fromUTF8 . BS.Char8.unpack
+                                               $ content
+              descr  = case parsed of
+                ParseOk _ d -> d
+                _           -> error $ "Couldn't read cabal file "
+                                    ++ show fileName
+          _ -> Nothing
+        _ -> Nothing
+  _ -> Nothing
+  where
+    fileName = Tar.entryPath entry
diff --git a/demo/PackageSearch.hs b/demo/PackageSearch.hs
new file mode 100644
--- /dev/null
+++ b/demo/PackageSearch.hs
@@ -0,0 +1,106 @@
+{-# LANGUAGE OverloadedStrings, NamedFieldPuns #-}
+module PackageSearch (
+    PkgSearchEngine,
+    initialPkgSearchEngine,
+    defaultSearchRankParameters,
+    PkgDocField(..),
+  ) where
+
+import Data.SearchEngine
+
+import ExtractNameTerms
+import ExtractDescriptionTerms
+
+import Data.Ix
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Text (Text)
+import qualified Data.Text as T
+import NLP.Snowball
+
+import Distribution.Package
+import Distribution.PackageDescription
+import Distribution.Text (display)
+
+
+type PkgSearchEngine = SearchEngine
+                         PackageDescription
+                         PackageName
+                         PkgDocField
+                         NoFeatures
+
+data PkgDocField = NameField
+                 | SynopsisField
+                 | DescriptionField
+  deriving (Eq, Ord, Enum, Bounded, Ix, Show)
+
+initialPkgSearchEngine :: PkgSearchEngine
+initialPkgSearchEngine =
+    initSearchEngine pkgSearchConfig defaultSearchRankParameters
+
+pkgSearchConfig :: SearchConfig PackageDescription
+                                PackageName PkgDocField NoFeatures
+pkgSearchConfig =
+    SearchConfig {
+      documentKey           = packageName,
+      extractDocumentTerms  = extractTokens,
+      transformQueryTerm    = normaliseQueryToken,
+      documentFeatureValue  = const noFeatures
+  }
+  where
+    extractTokens :: PackageDescription -> PkgDocField -> [Text]
+    extractTokens pkg NameField        = extractPackageNameTerms
+                                           (display $ packageName pkg)
+    extractTokens pkg SynopsisField    = extractSynopsisTerms
+                                           stopWords (synopsis    pkg)
+    extractTokens pkg DescriptionField = extractDescriptionTerms
+                                           stopWords (description pkg)
+
+    normaliseQueryToken :: Text -> PkgDocField -> Text
+    normaliseQueryToken tok =
+      let tokFold = T.toCaseFold tok
+          tokStem = stem English tokFold
+       in \field -> case field of
+                      NameField        -> tokFold
+                      SynopsisField    -> tokStem
+                      DescriptionField -> tokStem
+
+defaultSearchRankParameters :: SearchRankParameters PkgDocField NoFeatures
+defaultSearchRankParameters =
+    SearchRankParameters {
+      paramK1,
+      paramB,
+      paramFieldWeights,
+      paramFeatureWeights     = noFeatures,
+      paramFeatureFunctions   = noFeatures,
+      paramResultsetSoftLimit = 200,
+      paramResultsetHardLimit = 400,
+      paramAutosuggestPrefilterLimit  = 500,
+      paramAutosuggestPostfilterLimit = 500
+    }
+  where
+    paramK1 :: Float
+    paramK1 = 1.5
+
+    paramB :: PkgDocField -> Float
+    paramB NameField        = 0.9
+    paramB SynopsisField    = 0.5
+    paramB DescriptionField = 0.5
+
+    paramFieldWeights :: PkgDocField -> Float
+    paramFieldWeights NameField        = 20
+    paramFieldWeights SynopsisField    = 5
+    paramFieldWeights DescriptionField = 1
+
+
+stopWords :: Set Term
+stopWords =
+  Set.fromList
+    ["haskell","library","simple","using","interface","functions",
+     "implementation","package","support","'s","based","for","a","and","the",
+     "to","of","with","in","an","on","from","that","as","into","by","is",
+     "some","which","or","like","your","other","can","at","over","be","it",
+     "within","their","this","but","are","get","one","all","you","so","only",
+     "now","how","where","when","up","has","been","about","them","then","see",
+     "no","do","than","should","out","off","much","if","i","have","also"]
+
diff --git a/dist/build/search-demo/search-demo-tmp/HaddockLex.hs b/dist/build/search-demo/search-demo-tmp/HaddockLex.hs
new file mode 100644
--- /dev/null
+++ b/dist/build/search-demo/search-demo-tmp/HaddockLex.hs
@@ -0,0 +1,368 @@
+{-# LANGUAGE CPP,MagicHash #-}
+{-# LINE 7 "demo/HaddockLex.x" #-}
+
+-- Disable warnings that the generated code causes
+{-# OPTIONS_GHC -fno-warn-deprecated-flags
+                -fno-warn-unused-binds
+                -fno-warn-unused-imports
+                -fno-warn-unused-matches
+                -fno-warn-missing-signatures
+                -fno-warn-tabs #-}
+module HaddockLex (
+        Token(..),
+        tokenise
+ ) where
+
+import Data.Char
+import Data.Word (Word8)
+import qualified Data.Bits
+import Numeric
+import Control.Monad (liftM)
+import HaddockTypes (RdrName)
+
+#if __GLASGOW_HASKELL__ >= 603
+#include "ghcconfig.h"
+#elif defined(__GLASGOW_HASKELL__)
+#include "config.h"
+#endif
+#if __GLASGOW_HASKELL__ >= 503
+import Data.Array
+import Data.Char (ord)
+import Data.Array.Base (unsafeAt)
+#else
+import Array
+import Char (ord)
+#endif
+#if __GLASGOW_HASKELL__ >= 503
+import GHC.Exts
+#else
+import GlaExts
+#endif
+alex_base :: AlexAddr
+alex_base = AlexA# "\xf8\xff\xff\xff\xfc\xff\xff\xff\xf2\x00\x00\x00\xfe\xff\xff\xff\x03\x00\x00\x00\xe8\x01\x00\x00\x12\x00\x00\x00\xde\x02\x00\x00\xd4\x03\x00\x00\xd5\x02\x00\x00\x24\x00\x00\x00\xf4\x00\x00\x00\x54\x04\x00\x00\xd4\x04\x00\x00\x54\x05\x00\x00\xd4\x05\x00\x00\x54\x06\x00\x00\xd4\x06\x00\x00\x54\x07\x00\x00\xd4\x07\x00\x00\x54\x08\x00\x00\xd4\x08\x00\x00\x54\x09\x00\x00\xd4\x09\x00\x00\x54\x0a\x00\x00\xd4\x0a\x00\x00\x00\x00\x00\x00\x45\x0b\x00\x00\x00\x00\x00\x00\xb6\x0b\x00\x00\x00\x00\x00\x00\x27\x0c\x00\x00\x00\x00\x00\x00\x98\x0c\x00\x00\x00\x00\x00\x00\x45\x03\x00\x00\x00\x00\x00\x00\x09\x0d\x00\x00\x00\x00\x00\x00\x7a\x0d\x00\x00\x00\x00\x00\x00\xbb\x0d\x00\x00\x00\x00\x00\x00\xfc\x0d\x00\x00\x00\x00\x00\x00\x3d\x0e\x00\x00\x3d\x0f\x00\x00\xfd\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6e\x0f\x00\x00\x00\x00\x00\x00\xaf\x0f\x00\x00\xda\x00\x00\x00\xc3\x01\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\x00\xf0\x10\x00\x00\xb0\x10\x00\x00\x00\x00\x00\x00\xb0\x11\x00\x00\x70\x11\x00\x00\x00\x00\x00\x00\x70\x12\x00\x00\x30\x12\x00\x00\x00\x00\x00\x00\x26\x13\x00\x00\x00\x00\x00\x00\xa6\x12\x00\x00\x30\x00\x00\x00\x26\x14\x00\x00\xe6\x13\x00\x00\x00\x00\x00\x00\xe6\x14\x00\x00\xa6\x14\x00\x00\x00\x00\x00\x00\x9c\x15\x00\x00\xd3\xff\xff\xff\x9c\x16\x00\x00\x1b\x15\x00\x00\x00\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfb\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x17\x00\x00\x09\x18\x00\x00\xfd\x17\x00\x00\xef\xff\xff\xff\xff\x18\x00\x00\xf5\x19\x00\x00\x00\x00\x00\x00\xeb\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
+
+alex_table :: AlexAddr
+alex_table = AlexA# "\x00\x00\x04\x00\x52\x00\x04\x00\x04\x00\x04\x00\x59\x00\x51\x00\x58\x00\x51\x00\x51\x00\x51\x00\x04\x00\x52\x00\x04\x00\x04\x00\x04\x00\x5b\x00\x36\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x51\x00\x00\x00\x45\x00\x00\x00\x54\x00\x04\x00\x00\x00\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x45\x00\x00\x00\x54\x00\x00\x00\x00\x00\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x53\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x53\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x55\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x55\x00\x62\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x4e\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x4f\x00\x0c\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x44\x00\x0f\x00\x31\x00\x31\x00\x31\x00\x32\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x6a\x00\x00\x00\x00\x00\x51\x00\x58\x00\x51\x00\x51\x00\x51\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x5a\x00\x64\x00\x51\x00\x00\x00\x67\x00\x66\x00\x00\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x65\x00\x00\x00\x00\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x69\x00\x63\x00\x00\x00\x00\x00\x5a\x00\x00\x00\x57\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x00\x6c\x00\x00\x00\x00\x00\x66\x00\x00\x00\x00\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x46\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x47\x00\x0e\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x34\x00\x14\x00\x24\x00\x24\x00\x24\x00\x25\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x6a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5a\x00\x64\x00\x00\x00\x00\x00\x67\x00\x66\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x65\x00\x00\x00\x00\x00\x00\x00\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x69\x00\x00\x00\x00\x00\x00\x00\x5a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x00\x6d\x00\x00\x00\x00\x00\x66\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x46\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x47\x00\x0e\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x34\x00\x14\x00\x24\x00\x24\x00\x24\x00\x25\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x5f\x00\x00\x00\x00\x00\x09\x00\x09\x00\x5d\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x09\x00\x60\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3d\x00\x11\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2b\x00\x17\x00\x1e\x00\x1e\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5e\x00\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x39\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3a\x00\x12\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x29\x00\x18\x00\x1c\x00\x1c\x00\x1c\x00\x1d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x4e\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x50\x00\x49\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x46\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x0c\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x43\x00\x3f\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x3c\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x39\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x0d\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x0e\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x2e\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x10\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x11\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x12\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x15\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x23\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x29\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2b\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x34\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x38\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x44\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x47\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x4a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x4d\x00\x4f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2e\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x2f\x00\x15\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x22\x00\x23\x00\x19\x00\x1a\x00\x1a\x00\x1a\x00\x1b\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x5c\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3f\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x40\x00\x10\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2d\x00\x16\x00\x20\x00\x20\x00\x20\x00\x21\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x09\x00\xff\xff\x09\x00\x09\x00\x09\x00\x09\x00\x5f\x00\x00\x00\x00\x00\x09\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x5d\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x09\x00\x60\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3e\x00\x3d\x00\x11\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2a\x00\x2b\x00\x17\x00\x1e\x00\x1e\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x00\x00\x09\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x5e\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x09\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x00\x00\x09\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x39\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3b\x00\x3a\x00\x12\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x28\x00\x29\x00\x18\x00\x1c\x00\x1c\x00\x1c\x00\x1d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x49\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4b\x00\x4a\x00\x0d\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x37\x00\x38\x00\x13\x00\x26\x00\x26\x00\x26\x00\x27\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x00\x00\x00\x5c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3f\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x41\x00\x40\x00\x10\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2c\x00\x2d\x00\x16\x00\x20\x00\x20\x00\x20\x00\x21\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x6a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x46\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x48\x00\x47\x00\x0e\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x33\x00\x34\x00\x14\x00\x24\x00\x24\x00\x24\x00\x25\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#
+
+alex_check :: AlexAddr
+alex_check = AlexA# "\xff\xff\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0a\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x3e\x00\x23\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x20\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x20\x00\xff\xff\x28\x00\xff\xff\x2a\x00\x20\x00\xff\xff\x2d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x28\x00\xff\xff\x2a\x00\xff\xff\xff\xff\x2d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\x00\xff\xff\xff\xff\xff\xff\xff\xff\x29\x00\x3e\x00\xff\xff\xff\xff\xff\xff\xff\xff\x3e\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5b\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x5b\x00\x3b\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x22\x00\x23\x00\x20\x00\xff\xff\x26\x00\x27\x00\xff\xff\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x2f\x00\xff\xff\xff\xff\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3c\x00\x3b\x00\xff\xff\xff\xff\x40\x00\xff\xff\x3e\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5c\x00\x5d\x00\xff\xff\xff\xff\x60\x00\xff\xff\xff\xff\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x22\x00\x23\x00\xff\xff\xff\xff\x26\x00\x27\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2f\x00\xff\xff\xff\xff\xff\xff\x58\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\xff\xff\xff\xff\xff\xff\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x78\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5c\x00\x5d\x00\xff\xff\xff\xff\x60\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x21\x00\xff\xff\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\xff\xff\xff\xff\x2a\x00\x2b\x00\x23\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\xff\xff\x5c\x00\xff\xff\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\xff\xff\x7c\x00\xff\xff\x7e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2f\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\xff\xff\x3e\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\xff\xff\x3e\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x3b\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\x21\x00\x0a\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\xff\xff\xff\xff\x2a\x00\x2b\x00\xff\xff\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x23\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\xff\xff\x5c\x00\xff\xff\x5e\x00\x5f\x00\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\xff\xff\x7c\x00\xff\xff\x7e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x21\x00\xff\xff\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\xff\xff\xff\xff\x2a\x00\x2b\x00\xff\xff\x2d\x00\x2e\x00\x2f\x00\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3a\x00\x2f\x00\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4a\x00\x4b\x00\x4c\x00\x4d\x00\x4e\x00\x4f\x00\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5a\x00\xff\xff\x5c\x00\xff\xff\x5e\x00\x5f\x00\xff\xff\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00\x6c\x00\x6d\x00\x6e\x00\x6f\x00\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7a\x00\xff\xff\x7c\x00\xff\xff\x7e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\xff\xff\x3e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x22\x00\x23\x00\xff\xff\xff\xff\x26\x00\x27\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\xff\xff\xff\xff\xff\xff\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5c\x00\x5d\x00\xff\xff\xff\xff\x60\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8a\x00\x8b\x00\x8c\x00\x8d\x00\x8e\x00\x8f\x00\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9a\x00\x9b\x00\x9c\x00\x9d\x00\x9e\x00\x9f\x00\xa0\x00\xa1\x00\xa2\x00\xa3\x00\xa4\x00\xa5\x00\xa6\x00\xa7\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb4\x00\xb5\x00\xb6\x00\xb7\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\xc9\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xce\x00\xcf\x00\xd0\x00\xd1\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\xdd\x00\xde\x00\xdf\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\xe6\x00\xe7\x00\xe8\x00\xe9\x00\xea\x00\xeb\x00\xec\x00\xed\x00\xee\x00\xef\x00\xf0\x00\xf1\x00\xf2\x00\xf3\x00\xf4\x00\xf5\x00\xf6\x00\xf7\x00\xf8\x00\xf9\x00\xfa\x00\xfb\x00\xfc\x00\xfd\x00\xfe\x00\xff\x00"#
+
+alex_deflt :: AlexAddr
+alex_deflt = AlexA# "\xff\xff\x01\x00\x6b\x00\xff\xff\xff\xff\x6b\x00\xff\xff\x07\x00\x08\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x22\x00\x22\x00\x28\x00\x28\x00\x2a\x00\x2a\x00\x2c\x00\x2c\x00\x30\x00\x30\x00\x33\x00\x33\x00\x37\x00\x37\x00\x3b\x00\x3b\x00\x3e\x00\x3e\x00\x41\x00\x41\x00\x42\x00\x42\x00\x42\x00\x43\x00\x43\x00\x48\x00\x48\x00\xff\xff\xff\xff\x4b\x00\x4b\x00\x08\x00\x08\x00\x08\x00\x07\x00\x07\x00\x07\x00\x4c\x00\x4c\x00\x4c\x00\x42\x00\x50\x00\x50\x00\xff\xff\x6b\x00\x6b\x00\x6b\x00\x61\x00\x61\x00\x61\x00\x4c\x00\xff\xff\x01\x00\x01\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x07\x00\x08\x00\xff\xff\xff\xff\x61\x00\x4c\x00\xff\xff\x6b\x00\xff\xff\xff\xff"#
+
+alex_accept = listArray (0::Int,109) [AlexAcc (alex_action_5),AlexAcc (alex_action_9),AlexAccNone,AlexAcc (alex_action_8),AlexAcc (alex_action_5),AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccNone,AlexAccSkip,AlexAcc (alex_action_1),AlexAcc (alex_action_2),AlexAcc (alex_action_3),AlexAcc (alex_action_4),AlexAcc (alex_action_6),AlexAcc (alex_action_7),AlexAcc (alex_action_9),AlexAcc (alex_action_10),AlexAcc (alex_action_11),AlexAcc (alex_action_12),AlexAcc (alex_action_13),AlexAcc (alex_action_14),AlexAcc (alex_action_15),AlexAcc (alex_action_15),AlexAcc (alex_action_16),AlexAcc (alex_action_17),AlexAcc (alex_action_18),AlexAcc (alex_action_19),AlexAcc (alex_action_19),AlexAcc (alex_action_19),AlexAcc (alex_action_19),AlexAcc (alex_action_19),AlexAcc (alex_action_19),AlexAcc (alex_action_20),AlexAcc (alex_action_21),AlexAcc (alex_action_22),AlexAcc (alex_action_23)]
+{-# LINE 89 "demo/HaddockLex.x" #-}
+
+data Token
+  = TokPara
+  | TokNumber
+  | TokBullet
+  | TokDefStart
+  | TokDefEnd
+  | TokSpecial Char
+  | TokIdent RdrName
+  | TokString String
+  | TokURL String
+  | TokPic String
+  | TokEmphasis String
+  | TokAName String
+  | TokBirdTrack String
+  deriving Show
+
+-- -----------------------------------------------------------------------------
+-- Alex support stuff
+
+type StartCode = Int
+type Action = String -> StartCode -> (StartCode -> Maybe [Token]) -> Maybe [Token]
+
+--TODO: we ought to switch to ByteString input.
+type AlexInput = (Char, [Word8], String)
+
+-- | For alex >= 3
+--
+-- See also alexGetChar
+alexGetByte :: AlexInput -> Maybe (Word8,AlexInput)
+alexGetByte (c,(b:bs),s) = Just (b,(c,bs,s))
+alexGetByte (c,[],[])    = Nothing
+alexGetByte (_,[],(c:s)) = case utf8Encode c of
+                             (b:bs) -> Just (b, (c, bs, s))
+
+-- | Encode a Haskell String to a list of Word8 values, in UTF8 format.
+utf8Encode :: Char -> [Word8]
+utf8Encode = map fromIntegral . go . ord
+ where
+  go oc
+   | oc <= 0x7f       = [oc]
+
+   | oc <= 0x7ff      = [ 0xc0 + (oc `Data.Bits.shiftR` 6)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+
+   | oc <= 0xffff     = [ 0xe0 + (oc `Data.Bits.shiftR` 12)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+   | otherwise        = [ 0xf0 + (oc `Data.Bits.shiftR` 18)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 12) Data.Bits..&. 0x3f)
+                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
+                        , 0x80 + oc Data.Bits..&. 0x3f
+                        ]
+
+-- | For alex < 3
+--
+-- See also alexGetByte
+alexGetChar :: AlexInput -> Maybe (Char, AlexInput)
+alexGetChar (_, _, [])   = Nothing
+alexGetChar (_, _, c:cs) = Just (c, (c,[],cs))
+
+alexInputPrevChar (c,_) = c
+
+tokenise :: String -> Maybe [Token]
+tokenise str =
+    go ('\n', [], eofHack str) para
+  where
+    go inp@(_,_,str') sc =
+          case alexScan inp sc of
+                AlexEOF -> Just []
+                AlexError _ -> Nothing
+                AlexSkip  inp' _       -> go inp' sc
+                AlexToken inp' len act -> act (take len str') sc (\sc' -> go inp' sc')
+
+-- NB. we add a final \n to the string, (see comment in the beginning of line
+-- production above).
+eofHack str = str++"\n"
+
+andBegin  :: Action -> StartCode -> Action
+andBegin act new_sc = \str _ cont -> act str new_sc cont
+
+token :: Token -> Action
+token t = \_ sc cont -> liftM (t :) (cont sc)
+
+strtoken, strtokenNL :: (String -> Token) -> Action
+strtoken t = \str sc cont -> liftM (t str :) (cont sc)
+strtokenNL t = \str sc cont -> liftM (t (filter (/= '\r') str) :) (cont sc)
+-- ^ We only want LF line endings in our internal doc string format, so we
+-- filter out all CRs.
+
+begin :: StartCode -> Action
+begin sc = \_ _ cont -> cont sc
+
+-- -----------------------------------------------------------------------------
+-- Lex a string as a Haskell identifier
+
+ident :: Action
+ident str sc cont = liftM (TokIdent str :) (cont sc)
+
+
+birdtrack,def,line,para,string :: Int
+birdtrack = 1
+def = 2
+line = 3
+para = 4
+string = 5
+alex_action_1 =  begin birdtrack 
+alex_action_2 =  token TokBullet `andBegin` string 
+alex_action_3 =  token TokDefStart `andBegin` def 
+alex_action_4 =  token TokNumber `andBegin` string 
+alex_action_5 =  begin string 
+alex_action_6 =  begin birdtrack 
+alex_action_7 =  token TokPara `andBegin` para 
+alex_action_8 =  begin string 
+alex_action_9 =  strtokenNL TokBirdTrack `andBegin` line 
+alex_action_10 =  strtoken $ \s -> TokSpecial (head s) 
+alex_action_11 =  strtoken $ \s -> TokPic (init $ init $ tail $ tail s) 
+alex_action_12 =  strtoken $ \s -> TokURL (init (tail s)) 
+alex_action_13 =  strtoken $ \s -> TokAName (init (tail s)) 
+alex_action_14 =  strtoken $ \s -> TokEmphasis (init (tail s)) 
+alex_action_15 =  ident 
+alex_action_16 =  strtoken (TokString . tail) 
+alex_action_17 =  strtoken $ \s -> TokString [chr (read (init (drop 2 s)))] 
+alex_action_18 =  strtoken $ \s -> case readHex (init (drop 3 s)) of [(n,_)] -> TokString [chr n]; _ -> error "hexParser: Can't happen" 
+alex_action_19 =  strtoken TokString 
+alex_action_20 =  strtokenNL TokString `andBegin` line 
+alex_action_21 =  strtoken TokString 
+alex_action_22 =  token TokDefEnd `andBegin` string 
+alex_action_23 =  strtoken TokString 
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+{-# LINE 1 "<command-line>" #-}
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+-- -----------------------------------------------------------------------------
+-- ALEX TEMPLATE
+--
+-- This code is in the PUBLIC DOMAIN; you may copy it freely and use
+-- it for any purpose whatsoever.
+
+-- -----------------------------------------------------------------------------
+-- INTERNALS and main scanner engine
+
+{-# LINE 21 "templates/GenericTemplate.hs" #-}
+
+
+
+
+
+-- Do not remove this comment. Required to fix CPP parsing when using GCC and a clang-compiled alex.
+#if __GLASGOW_HASKELL__ > 706
+#define GTE(n,m) (tagToEnum# (n >=# m))
+#define EQ(n,m) (tagToEnum# (n ==# m))
+#else
+#define GTE(n,m) (n >=# m)
+#define EQ(n,m) (n ==# m)
+#endif
+{-# LINE 51 "templates/GenericTemplate.hs" #-}
+
+
+data AlexAddr = AlexA# Addr#
+-- Do not remove this comment. Required to fix CPP parsing when using GCC and a clang-compiled alex.
+#if __GLASGOW_HASKELL__ < 503
+uncheckedShiftL# = shiftL#
+#endif
+
+{-# INLINE alexIndexInt16OffAddr #-}
+alexIndexInt16OffAddr (AlexA# arr) off =
+#ifdef WORDS_BIGENDIAN
+  narrow16Int# i
+  where
+        i    = word2Int# ((high `uncheckedShiftL#` 8#) `or#` low)
+        high = int2Word# (ord# (indexCharOffAddr# arr (off' +# 1#)))
+        low  = int2Word# (ord# (indexCharOffAddr# arr off'))
+        off' = off *# 2#
+#else
+  indexInt16OffAddr# arr off
+#endif
+
+
+
+
+
+{-# INLINE alexIndexInt32OffAddr #-}
+alexIndexInt32OffAddr (AlexA# arr) off = 
+#ifdef WORDS_BIGENDIAN
+  narrow32Int# i
+  where
+   i    = word2Int# ((b3 `uncheckedShiftL#` 24#) `or#`
+		     (b2 `uncheckedShiftL#` 16#) `or#`
+		     (b1 `uncheckedShiftL#` 8#) `or#` b0)
+   b3   = int2Word# (ord# (indexCharOffAddr# arr (off' +# 3#)))
+   b2   = int2Word# (ord# (indexCharOffAddr# arr (off' +# 2#)))
+   b1   = int2Word# (ord# (indexCharOffAddr# arr (off' +# 1#)))
+   b0   = int2Word# (ord# (indexCharOffAddr# arr off'))
+   off' = off *# 4#
+#else
+  indexInt32OffAddr# arr off
+#endif
+
+
+
+
+
+
+#if __GLASGOW_HASKELL__ < 503
+quickIndex arr i = arr ! i
+#else
+-- GHC >= 503, unsafeAt is available from Data.Array.Base.
+quickIndex = unsafeAt
+#endif
+
+
+
+
+-- -----------------------------------------------------------------------------
+-- Main lexing routines
+
+data AlexReturn a
+  = AlexEOF
+  | AlexError  !AlexInput
+  | AlexSkip   !AlexInput !Int
+  | AlexToken  !AlexInput !Int a
+
+-- alexScan :: AlexInput -> StartCode -> AlexReturn a
+alexScan input (I# (sc))
+  = alexScanUser undefined input (I# (sc))
+
+alexScanUser user input (I# (sc))
+  = case alex_scan_tkn user input 0# input sc AlexNone of
+	(AlexNone, input') ->
+		case alexGetByte input of
+			Nothing -> 
+
+
+
+				   AlexEOF
+			Just _ ->
+
+
+
+				   AlexError input'
+
+	(AlexLastSkip input'' len, _) ->
+
+
+
+		AlexSkip input'' len
+
+	(AlexLastAcc k input''' len, _) ->
+
+
+
+		AlexToken input''' len k
+
+
+-- Push the input through the DFA, remembering the most recent accepting
+-- state it encountered.
+
+alex_scan_tkn user orig_input len input s last_acc =
+  input `seq` -- strict in the input
+  let 
+	new_acc = (check_accs (alex_accept `quickIndex` (I# (s))))
+  in
+  new_acc `seq`
+  case alexGetByte input of
+     Nothing -> (new_acc, input)
+     Just (c, new_input) -> 
+
+
+
+      case fromIntegral c of { (I# (ord_c)) ->
+        let
+                base   = alexIndexInt32OffAddr alex_base s
+                offset = (base +# ord_c)
+                check  = alexIndexInt16OffAddr alex_check offset
+		
+                new_s = if GTE(offset,0#) && EQ(check,ord_c)
+			  then alexIndexInt16OffAddr alex_table offset
+			  else alexIndexInt16OffAddr alex_deflt s
+	in
+        case new_s of
+	    -1# -> (new_acc, input)
+		-- on an error, we want to keep the input *before* the
+		-- character that failed, not after.
+    	    _ -> alex_scan_tkn user orig_input (if c < 0x80 || c >= 0xC0 then (len +# 1#) else len)
+                                                -- note that the length is increased ONLY if this is the 1st byte in a char encoding)
+			new_input new_s new_acc
+      }
+  where
+	check_accs (AlexAccNone) = last_acc
+	check_accs (AlexAcc a  ) = AlexLastAcc a input (I# (len))
+	check_accs (AlexAccSkip) = AlexLastSkip  input (I# (len))
+{-# LINE 198 "templates/GenericTemplate.hs" #-}
+
+data AlexLastAcc a
+  = AlexNone
+  | AlexLastAcc a !AlexInput !Int
+  | AlexLastSkip  !AlexInput !Int
+
+instance Functor AlexLastAcc where
+    fmap f AlexNone = AlexNone
+    fmap f (AlexLastAcc x y z) = AlexLastAcc (f x) y z
+    fmap f (AlexLastSkip x y) = AlexLastSkip x y
+
+data AlexAcc a user
+  = AlexAccNone
+  | AlexAcc a
+  | AlexAccSkip
+{-# LINE 242 "templates/GenericTemplate.hs" #-}
+
+-- used by wrappers
+iUnbox (I# (i)) = i
diff --git a/dist/build/search-demo/search-demo-tmp/HaddockParse.hs b/dist/build/search-demo/search-demo-tmp/HaddockParse.hs
new file mode 100644
--- /dev/null
+++ b/dist/build/search-demo/search-demo-tmp/HaddockParse.hs
@@ -0,0 +1,732 @@
+{-# OPTIONS_GHC -w #-}
+{-# OPTIONS -fglasgow-exts -cpp #-}
+-- Disable warnings that the generated code causes
+{-# OPTIONS_GHC -fno-warn-deprecated-flags
+                -fno-warn-missing-signatures
+                -fno-warn-unused-binds
+                -fno-warn-unused-matches
+                -fno-warn-lazy-unlifted-bindings
+                -fno-warn-name-shadowing
+                -fno-warn-incomplete-patterns
+                -fno-warn-tabs #-}
+module HaddockParse (parseHaddockParagraphs) where
+
+import HaddockLex
+import HaddockHtml
+import HaddockTypes
+import Data.Char  (isSpace)
+import qualified Data.Array as Happy_Data_Array
+import qualified GHC.Exts as Happy_GHC_Exts
+
+-- parser produced by Happy Version 1.19.2
+
+newtype HappyAbsSyn  = HappyAbsSyn HappyAny
+#if __GLASGOW_HASKELL__ >= 607
+type HappyAny = Happy_GHC_Exts.Any
+#else
+type HappyAny = forall a . a
+#endif
+happyIn5 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn5 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn5 #-}
+happyOut5 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut5 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut5 #-}
+happyIn6 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn6 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn6 #-}
+happyOut6 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut6 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut6 #-}
+happyIn7 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn7 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn7 #-}
+happyOut7 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut7 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut7 #-}
+happyIn8 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn8 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn8 #-}
+happyOut8 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut8 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut8 #-}
+happyIn9 :: ((Doc RdrName, Doc RdrName)) -> (HappyAbsSyn )
+happyIn9 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn9 #-}
+happyOut9 :: (HappyAbsSyn ) -> ((Doc RdrName, Doc RdrName))
+happyOut9 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut9 #-}
+happyIn10 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn10 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn10 #-}
+happyOut10 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut10 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut10 #-}
+happyIn11 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn11 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn11 #-}
+happyOut11 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut11 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut11 #-}
+happyIn12 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn12 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn12 #-}
+happyOut12 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut12 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut12 #-}
+happyIn13 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn13 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn13 #-}
+happyOut13 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut13 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut13 #-}
+happyIn14 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn14 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn14 #-}
+happyOut14 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut14 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut14 #-}
+happyIn15 :: (Doc RdrName) -> (HappyAbsSyn )
+happyIn15 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn15 #-}
+happyOut15 :: (HappyAbsSyn ) -> (Doc RdrName)
+happyOut15 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut15 #-}
+happyIn16 :: (String) -> (HappyAbsSyn )
+happyIn16 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyIn16 #-}
+happyOut16 :: (HappyAbsSyn ) -> (String)
+happyOut16 x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOut16 #-}
+happyInTok :: (Token) -> (HappyAbsSyn )
+happyInTok x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyInTok #-}
+happyOutTok :: (HappyAbsSyn ) -> (Token)
+happyOutTok x = Happy_GHC_Exts.unsafeCoerce# x
+{-# INLINE happyOutTok #-}
+
+
+happyActOffsets :: HappyAddr
+happyActOffsets = HappyA# "\x01\x00\x27\x00\x0f\x00\x4f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x27\x00\x00\x00\x53\x00\x27\x00\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x1b\x00\x3f\x00\x00\x00\x00\x00\x30\x00\x30\x00\x23\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x26\x00\x17\x00\x21\x00\x1d\x00\x53\x00\x53\x00\x00\x00\x00\x00\x00\x00\x00\x00\x27\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
+
+happyGotoOffsets :: HappyAddr
+happyGotoOffsets = HappyA# "\x4c\x00\x79\x00\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x75\x00\x00\x00\x7d\x00\x71\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6d\x00\x67\x00\xfe\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x41\x00\x00\x00\x36\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xff\x00\x00\x00\x00\x7b\x00\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
+
+happyDefActions :: HappyAddr
+happyDefActions = HappyA# "\xfa\xff\x00\x00\x00\x00\x00\x00\xf9\xff\xf8\xff\xf7\xff\xf6\xff\xf1\xff\xf2\xff\xed\xff\xec\xff\x00\x00\x00\x00\x00\x00\xe5\xff\xe4\xff\xe3\xff\xe6\xff\x00\x00\x00\x00\xef\xff\xe2\xff\xe7\xff\x00\x00\x00\x00\xfb\xff\xfa\xff\xfc\xff\xfa\xff\xf0\xff\xf4\xff\xf5\xff\x00\x00\xe0\xff\x00\x00\x00\x00\xe8\xff\x00\x00\xee\xff\xea\xff\xe9\xff\xeb\xff\x00\x00\xdf\xff\xe1\xff\xfd\xff\xf3\xff"#
+
+happyCheck :: HappyAddr
+happyCheck = HappyA# "\xff\xff\x0b\x00\x01\x00\x02\x00\x06\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x01\x00\x02\x00\x0b\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x01\x00\x0e\x00\x01\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x03\x00\x0e\x00\x0b\x00\x0c\x00\x01\x00\x0e\x00\x04\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x0d\x00\x07\x00\x08\x00\x0c\x00\x0a\x00\x0e\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x0f\x00\x0a\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x0b\x00\x0a\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x0e\x00\x0a\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x0d\x00\x09\x00\x0a\x00\x0c\x00\x0d\x00\x0e\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x09\x00\x0a\x00\x09\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#
+
+happyTable :: HappyAddr
+happyTable = HappyA# "\x00\x00\x2c\x00\x0d\x00\x0e\x00\x1e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x1c\x00\x18\x00\x0d\x00\x0e\x00\x21\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x0d\x00\x18\x00\x2b\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x2c\x00\x23\x00\x16\x00\x17\x00\x0d\x00\x18\x00\x2e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x1e\x00\x2f\x00\x0a\x00\x17\x00\x0b\x00\x18\x00\x2e\x00\x1a\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\xff\xff\x0b\x00\x1c\x00\x1a\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x16\x00\x0b\x00\x19\x00\x1a\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x23\x00\x0b\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x1e\x00\x28\x00\x25\x00\x17\x00\x27\x00\x18\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x00\x00\x0b\x00\x1f\x00\x08\x00\x09\x00\x0a\x00\x00\x00\x0b\x00\x20\x00\x08\x00\x09\x00\x0a\x00\x00\x00\x0b\x00\x23\x00\x0a\x00\x00\x00\x0b\x00\x27\x00\x0a\x00\x00\x00\x0b\x00\x18\x00\x0a\x00\x00\x00\x0b\x00\x29\x00\x25\x00\x24\x00\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
+
+happyReduceArr = Happy_Data_Array.array (2, 32) [
+	(2 , happyReduce_2),
+	(3 , happyReduce_3),
+	(4 , happyReduce_4),
+	(5 , happyReduce_5),
+	(6 , happyReduce_6),
+	(7 , happyReduce_7),
+	(8 , happyReduce_8),
+	(9 , happyReduce_9),
+	(10 , happyReduce_10),
+	(11 , happyReduce_11),
+	(12 , happyReduce_12),
+	(13 , happyReduce_13),
+	(14 , happyReduce_14),
+	(15 , happyReduce_15),
+	(16 , happyReduce_16),
+	(17 , happyReduce_17),
+	(18 , happyReduce_18),
+	(19 , happyReduce_19),
+	(20 , happyReduce_20),
+	(21 , happyReduce_21),
+	(22 , happyReduce_22),
+	(23 , happyReduce_23),
+	(24 , happyReduce_24),
+	(25 , happyReduce_25),
+	(26 , happyReduce_26),
+	(27 , happyReduce_27),
+	(28 , happyReduce_28),
+	(29 , happyReduce_29),
+	(30 , happyReduce_30),
+	(31 , happyReduce_31),
+	(32 , happyReduce_32)
+	]
+
+happy_n_terms = 16 :: Int
+happy_n_nonterms = 12 :: Int
+
+happyReduce_2 = happySpecReduce_3  0# happyReduction_2
+happyReduction_2 happy_x_3
+	happy_x_2
+	happy_x_1
+	 =  case happyOut6 happy_x_1 of { happy_var_1 -> 
+	case happyOut5 happy_x_3 of { happy_var_3 -> 
+	happyIn5
+		 (docAppend happy_var_1 happy_var_3
+	)}}
+
+happyReduce_3 = happySpecReduce_2  0# happyReduction_3
+happyReduction_3 happy_x_2
+	happy_x_1
+	 =  case happyOut5 happy_x_2 of { happy_var_2 -> 
+	happyIn5
+		 (happy_var_2
+	)}
+
+happyReduce_4 = happySpecReduce_1  0# happyReduction_4
+happyReduction_4 happy_x_1
+	 =  case happyOut6 happy_x_1 of { happy_var_1 -> 
+	happyIn5
+		 (happy_var_1
+	)}
+
+happyReduce_5 = happySpecReduce_0  0# happyReduction_5
+happyReduction_5  =  happyIn5
+		 (DocEmpty
+	)
+
+happyReduce_6 = happySpecReduce_1  1# happyReduction_6
+happyReduction_6 happy_x_1
+	 =  case happyOut7 happy_x_1 of { happy_var_1 -> 
+	happyIn6
+		 (DocUnorderedList [happy_var_1]
+	)}
+
+happyReduce_7 = happySpecReduce_1  1# happyReduction_7
+happyReduction_7 happy_x_1
+	 =  case happyOut8 happy_x_1 of { happy_var_1 -> 
+	happyIn6
+		 (DocOrderedList [happy_var_1]
+	)}
+
+happyReduce_8 = happySpecReduce_1  1# happyReduction_8
+happyReduction_8 happy_x_1
+	 =  case happyOut9 happy_x_1 of { happy_var_1 -> 
+	happyIn6
+		 (DocDefList [happy_var_1]
+	)}
+
+happyReduce_9 = happySpecReduce_1  1# happyReduction_9
+happyReduction_9 happy_x_1
+	 =  case happyOut10 happy_x_1 of { happy_var_1 -> 
+	happyIn6
+		 (happy_var_1
+	)}
+
+happyReduce_10 = happySpecReduce_2  2# happyReduction_10
+happyReduction_10 happy_x_2
+	happy_x_1
+	 =  case happyOut10 happy_x_2 of { happy_var_2 -> 
+	happyIn7
+		 (happy_var_2
+	)}
+
+happyReduce_11 = happySpecReduce_2  3# happyReduction_11
+happyReduction_11 happy_x_2
+	happy_x_1
+	 =  case happyOut10 happy_x_2 of { happy_var_2 -> 
+	happyIn8
+		 (happy_var_2
+	)}
+
+happyReduce_12 = happyReduce 4# 4# happyReduction_12
+happyReduction_12 (happy_x_4 `HappyStk`
+	happy_x_3 `HappyStk`
+	happy_x_2 `HappyStk`
+	happy_x_1 `HappyStk`
+	happyRest)
+	 = case happyOut12 happy_x_2 of { happy_var_2 -> 
+	case happyOut12 happy_x_4 of { happy_var_4 -> 
+	happyIn9
+		 ((happy_var_2, happy_var_4)
+	) `HappyStk` happyRest}}
+
+happyReduce_13 = happySpecReduce_1  5# happyReduction_13
+happyReduction_13 happy_x_1
+	 =  case happyOut12 happy_x_1 of { happy_var_1 -> 
+	happyIn10
+		 (docParagraph happy_var_1
+	)}
+
+happyReduce_14 = happySpecReduce_1  5# happyReduction_14
+happyReduction_14 happy_x_1
+	 =  case happyOut11 happy_x_1 of { happy_var_1 -> 
+	happyIn10
+		 (DocCodeBlock happy_var_1
+	)}
+
+happyReduce_15 = happySpecReduce_2  6# happyReduction_15
+happyReduction_15 happy_x_2
+	happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokBirdTrack happy_var_1) -> 
+	case happyOut11 happy_x_2 of { happy_var_2 -> 
+	happyIn11
+		 (docAppend (DocString happy_var_1) happy_var_2
+	)}}
+
+happyReduce_16 = happySpecReduce_1  6# happyReduction_16
+happyReduction_16 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokBirdTrack happy_var_1) -> 
+	happyIn11
+		 (DocString happy_var_1
+	)}
+
+happyReduce_17 = happySpecReduce_2  7# happyReduction_17
+happyReduction_17 happy_x_2
+	happy_x_1
+	 =  case happyOut13 happy_x_1 of { happy_var_1 -> 
+	case happyOut12 happy_x_2 of { happy_var_2 -> 
+	happyIn12
+		 (docAppend happy_var_1 happy_var_2
+	)}}
+
+happyReduce_18 = happySpecReduce_1  7# happyReduction_18
+happyReduction_18 happy_x_1
+	 =  case happyOut13 happy_x_1 of { happy_var_1 -> 
+	happyIn12
+		 (happy_var_1
+	)}
+
+happyReduce_19 = happySpecReduce_1  8# happyReduction_19
+happyReduction_19 happy_x_1
+	 =  case happyOut15 happy_x_1 of { happy_var_1 -> 
+	happyIn13
+		 (happy_var_1
+	)}
+
+happyReduce_20 = happySpecReduce_3  8# happyReduction_20
+happyReduction_20 happy_x_3
+	happy_x_2
+	happy_x_1
+	 =  case happyOut14 happy_x_2 of { happy_var_2 -> 
+	happyIn13
+		 (DocMonospaced happy_var_2
+	)}
+
+happyReduce_21 = happySpecReduce_2  9# happyReduction_21
+happyReduction_21 happy_x_2
+	happy_x_1
+	 =  case happyOut14 happy_x_2 of { happy_var_2 -> 
+	happyIn14
+		 (docAppend (DocString "\n") happy_var_2
+	)}
+
+happyReduce_22 = happySpecReduce_2  9# happyReduction_22
+happyReduction_22 happy_x_2
+	happy_x_1
+	 =  case happyOut15 happy_x_1 of { happy_var_1 -> 
+	case happyOut14 happy_x_2 of { happy_var_2 -> 
+	happyIn14
+		 (docAppend happy_var_1 happy_var_2
+	)}}
+
+happyReduce_23 = happySpecReduce_1  9# happyReduction_23
+happyReduction_23 happy_x_1
+	 =  case happyOut15 happy_x_1 of { happy_var_1 -> 
+	happyIn14
+		 (happy_var_1
+	)}
+
+happyReduce_24 = happySpecReduce_1  10# happyReduction_24
+happyReduction_24 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokString happy_var_1) -> 
+	happyIn15
+		 (DocString happy_var_1
+	)}
+
+happyReduce_25 = happySpecReduce_1  10# happyReduction_25
+happyReduction_25 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokEmphasis happy_var_1) -> 
+	happyIn15
+		 (DocEmphasis (DocString happy_var_1)
+	)}
+
+happyReduce_26 = happySpecReduce_1  10# happyReduction_26
+happyReduction_26 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokURL happy_var_1) -> 
+	happyIn15
+		 (DocHyperlink (makeHyperlink happy_var_1)
+	)}
+
+happyReduce_27 = happySpecReduce_1  10# happyReduction_27
+happyReduction_27 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokPic happy_var_1) -> 
+	happyIn15
+		 (DocPic happy_var_1
+	)}
+
+happyReduce_28 = happySpecReduce_1  10# happyReduction_28
+happyReduction_28 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokAName happy_var_1) -> 
+	happyIn15
+		 (DocAName happy_var_1
+	)}
+
+happyReduce_29 = happySpecReduce_1  10# happyReduction_29
+happyReduction_29 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokIdent happy_var_1) -> 
+	happyIn15
+		 (DocIdentifier happy_var_1
+	)}
+
+happyReduce_30 = happySpecReduce_3  10# happyReduction_30
+happyReduction_30 happy_x_3
+	happy_x_2
+	happy_x_1
+	 =  case happyOut16 happy_x_2 of { happy_var_2 -> 
+	happyIn15
+		 (DocModule happy_var_2
+	)}
+
+happyReduce_31 = happySpecReduce_1  11# happyReduction_31
+happyReduction_31 happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokString happy_var_1) -> 
+	happyIn16
+		 (happy_var_1
+	)}
+
+happyReduce_32 = happySpecReduce_2  11# happyReduction_32
+happyReduction_32 happy_x_2
+	happy_x_1
+	 =  case happyOutTok happy_x_1 of { (TokString happy_var_1) -> 
+	case happyOut16 happy_x_2 of { happy_var_2 -> 
+	happyIn16
+		 (happy_var_1 ++ happy_var_2
+	)}}
+
+happyNewToken action sts stk [] =
+	happyDoAction 15# notHappyAtAll action sts stk []
+
+happyNewToken action sts stk (tk:tks) =
+	let cont i = happyDoAction i tk action sts stk tks in
+	case tk of {
+	TokSpecial '@' -> cont 1#;
+	TokDefStart -> cont 2#;
+	TokDefEnd -> cont 3#;
+	TokSpecial '\"' -> cont 4#;
+	TokURL happy_dollar_dollar -> cont 5#;
+	TokPic happy_dollar_dollar -> cont 6#;
+	TokAName happy_dollar_dollar -> cont 7#;
+	TokEmphasis happy_dollar_dollar -> cont 8#;
+	TokBullet -> cont 9#;
+	TokNumber -> cont 10#;
+	TokBirdTrack happy_dollar_dollar -> cont 11#;
+	TokIdent happy_dollar_dollar -> cont 12#;
+	TokPara -> cont 13#;
+	TokString happy_dollar_dollar -> cont 14#;
+	_ -> happyError' (tk:tks)
+	}
+
+happyError_ 15# tk tks = happyError' tks
+happyError_ _ tk tks = happyError' (tk:tks)
+
+happyThen :: () => Maybe a -> (a -> Maybe b) -> Maybe b
+happyThen = (>>=)
+happyReturn :: () => a -> Maybe a
+happyReturn = (return)
+happyThen1 m k tks = (>>=) m (\a -> k a tks)
+happyReturn1 :: () => a -> b -> Maybe a
+happyReturn1 = \a tks -> (return) a
+happyError' :: () => [(Token)] -> Maybe a
+happyError' = happyError
+
+parseHaddockParagraphs tks = happySomeParser where
+  happySomeParser = happyThen (happyParse 0# tks) (\x -> happyReturn (happyOut5 x))
+
+parseHaddockString tks = happySomeParser where
+  happySomeParser = happyThen (happyParse 1# tks) (\x -> happyReturn (happyOut12 x))
+
+happySeq = happyDontSeq
+
+
+happyError :: [Token] -> Maybe a
+happyError toks = Nothing
+
+-- | Create a `Hyperlink` from given string.
+--
+-- A hyperlink consists of a URL and an optional label.  The label is separated
+-- from the url by one or more whitespace characters.
+makeHyperlink :: String -> Hyperlink
+makeHyperlink input = case break isSpace $ strip input of
+  (url, "")    -> Hyperlink url Nothing
+  (url, label) -> Hyperlink url (Just . dropWhile isSpace $ label)
+
+-- | Remove all leading and trailing whitespace
+strip :: String -> String
+strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+{-# LINE 1 "<command-line>" #-}
+{-# LINE 1 "templates/GenericTemplate.hs" #-}
+-- Id: GenericTemplate.hs,v 1.26 2005/01/14 14:47:22 simonmar Exp 
+
+{-# LINE 13 "templates/GenericTemplate.hs" #-}
+
+
+
+
+
+-- Do not remove this comment. Required to fix CPP parsing when using GCC and a clang-compiled alex.
+#if __GLASGOW_HASKELL__ > 706
+#define LT(n,m) ((Happy_GHC_Exts.tagToEnum# (n Happy_GHC_Exts.<# m)) :: Bool)
+#define GTE(n,m) ((Happy_GHC_Exts.tagToEnum# (n Happy_GHC_Exts.>=# m)) :: Bool)
+#define EQ(n,m) ((Happy_GHC_Exts.tagToEnum# (n Happy_GHC_Exts.==# m)) :: Bool)
+#else
+#define LT(n,m) (n Happy_GHC_Exts.<# m)
+#define GTE(n,m) (n Happy_GHC_Exts.>=# m)
+#define EQ(n,m) (n Happy_GHC_Exts.==# m)
+#endif
+{-# LINE 46 "templates/GenericTemplate.hs" #-}
+
+
+data Happy_IntList = HappyCons Happy_GHC_Exts.Int# Happy_IntList
+
+
+
+
+
+{-# LINE 67 "templates/GenericTemplate.hs" #-}
+
+{-# LINE 77 "templates/GenericTemplate.hs" #-}
+
+{-# LINE 86 "templates/GenericTemplate.hs" #-}
+
+infixr 9 `HappyStk`
+data HappyStk a = HappyStk a (HappyStk a)
+
+-----------------------------------------------------------------------------
+-- starting the parse
+
+happyParse start_state = happyNewToken start_state notHappyAtAll notHappyAtAll
+
+-----------------------------------------------------------------------------
+-- Accepting the parse
+
+-- If the current token is 0#, it means we've just accepted a partial
+-- parse (a %partial parser).  We must ignore the saved token on the top of
+-- the stack in this case.
+happyAccept 0# tk st sts (_ `HappyStk` ans `HappyStk` _) =
+        happyReturn1 ans
+happyAccept j tk st sts (HappyStk ans _) = 
+        (happyTcHack j (happyTcHack st)) (happyReturn1 ans)
+
+-----------------------------------------------------------------------------
+-- Arrays only: do the next action
+
+
+
+happyDoAction i tk st
+        = {- nothing -}
+
+
+          case action of
+                0#           -> {- nothing -}
+                                     happyFail i tk st
+                -1#          -> {- nothing -}
+                                     happyAccept i tk st
+                n | LT(n,(0# :: Happy_GHC_Exts.Int#)) -> {- nothing -}
+
+                                                   (happyReduceArr Happy_Data_Array.! rule) i tk st
+                                                   where rule = (Happy_GHC_Exts.I# ((Happy_GHC_Exts.negateInt# ((n Happy_GHC_Exts.+# (1# :: Happy_GHC_Exts.Int#))))))
+                n                 -> {- nothing -}
+
+
+                                     happyShift new_state i tk st
+                                     where new_state = (n Happy_GHC_Exts.-# (1# :: Happy_GHC_Exts.Int#))
+   where off    = indexShortOffAddr happyActOffsets st
+         off_i  = (off Happy_GHC_Exts.+# i)
+         check  = if GTE(off_i,(0# :: Happy_GHC_Exts.Int#))
+                  then EQ(indexShortOffAddr happyCheck off_i, i)
+                  else False
+         action
+          | check     = indexShortOffAddr happyTable off_i
+          | otherwise = indexShortOffAddr happyDefActions st
+
+
+indexShortOffAddr (HappyA# arr) off =
+        Happy_GHC_Exts.narrow16Int# i
+  where
+        i = Happy_GHC_Exts.word2Int# (Happy_GHC_Exts.or# (Happy_GHC_Exts.uncheckedShiftL# high 8#) low)
+        high = Happy_GHC_Exts.int2Word# (Happy_GHC_Exts.ord# (Happy_GHC_Exts.indexCharOffAddr# arr (off' Happy_GHC_Exts.+# 1#)))
+        low  = Happy_GHC_Exts.int2Word# (Happy_GHC_Exts.ord# (Happy_GHC_Exts.indexCharOffAddr# arr off'))
+        off' = off Happy_GHC_Exts.*# 2#
+
+
+
+
+
+data HappyAddr = HappyA# Happy_GHC_Exts.Addr#
+
+
+
+
+-----------------------------------------------------------------------------
+-- HappyState data type (not arrays)
+
+{-# LINE 170 "templates/GenericTemplate.hs" #-}
+
+-----------------------------------------------------------------------------
+-- Shifting a token
+
+happyShift new_state 0# tk st sts stk@(x `HappyStk` _) =
+     let i = (case Happy_GHC_Exts.unsafeCoerce# x of { (Happy_GHC_Exts.I# (i)) -> i }) in
+--     trace "shifting the error token" $
+     happyDoAction i tk new_state (HappyCons (st) (sts)) (stk)
+
+happyShift new_state i tk st sts stk =
+     happyNewToken new_state (HappyCons (st) (sts)) ((happyInTok (tk))`HappyStk`stk)
+
+-- happyReduce is specialised for the common cases.
+
+happySpecReduce_0 i fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happySpecReduce_0 nt fn j tk st@((action)) sts stk
+     = happyGoto nt j tk st (HappyCons (st) (sts)) (fn `HappyStk` stk)
+
+happySpecReduce_1 i fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happySpecReduce_1 nt fn j tk _ sts@((HappyCons (st@(action)) (_))) (v1`HappyStk`stk')
+     = let r = fn v1 in
+       happySeq r (happyGoto nt j tk st sts (r `HappyStk` stk'))
+
+happySpecReduce_2 i fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happySpecReduce_2 nt fn j tk _ (HappyCons (_) (sts@((HappyCons (st@(action)) (_))))) (v1`HappyStk`v2`HappyStk`stk')
+     = let r = fn v1 v2 in
+       happySeq r (happyGoto nt j tk st sts (r `HappyStk` stk'))
+
+happySpecReduce_3 i fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happySpecReduce_3 nt fn j tk _ (HappyCons (_) ((HappyCons (_) (sts@((HappyCons (st@(action)) (_))))))) (v1`HappyStk`v2`HappyStk`v3`HappyStk`stk')
+     = let r = fn v1 v2 v3 in
+       happySeq r (happyGoto nt j tk st sts (r `HappyStk` stk'))
+
+happyReduce k i fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happyReduce k nt fn j tk st sts stk
+     = case happyDrop (k Happy_GHC_Exts.-# (1# :: Happy_GHC_Exts.Int#)) sts of
+         sts1@((HappyCons (st1@(action)) (_))) ->
+                let r = fn stk in  -- it doesn't hurt to always seq here...
+                happyDoSeq r (happyGoto nt j tk st1 sts1 r)
+
+happyMonadReduce k nt fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happyMonadReduce k nt fn j tk st sts stk =
+      case happyDrop k (HappyCons (st) (sts)) of
+        sts1@((HappyCons (st1@(action)) (_))) ->
+          let drop_stk = happyDropStk k stk in
+          happyThen1 (fn stk tk) (\r -> happyGoto nt j tk st1 sts1 (r `HappyStk` drop_stk))
+
+happyMonad2Reduce k nt fn 0# tk st sts stk
+     = happyFail 0# tk st sts stk
+happyMonad2Reduce k nt fn j tk st sts stk =
+      case happyDrop k (HappyCons (st) (sts)) of
+        sts1@((HappyCons (st1@(action)) (_))) ->
+         let drop_stk = happyDropStk k stk
+
+             off = indexShortOffAddr happyGotoOffsets st1
+             off_i = (off Happy_GHC_Exts.+# nt)
+             new_state = indexShortOffAddr happyTable off_i
+
+
+
+          in
+          happyThen1 (fn stk tk) (\r -> happyNewToken new_state sts1 (r `HappyStk` drop_stk))
+
+happyDrop 0# l = l
+happyDrop n (HappyCons (_) (t)) = happyDrop (n Happy_GHC_Exts.-# (1# :: Happy_GHC_Exts.Int#)) t
+
+happyDropStk 0# l = l
+happyDropStk n (x `HappyStk` xs) = happyDropStk (n Happy_GHC_Exts.-# (1#::Happy_GHC_Exts.Int#)) xs
+
+-----------------------------------------------------------------------------
+-- Moving to a new state after a reduction
+
+
+happyGoto nt j tk st = 
+   {- nothing -}
+   happyDoAction j tk new_state
+   where off = indexShortOffAddr happyGotoOffsets st
+         off_i = (off Happy_GHC_Exts.+# nt)
+         new_state = indexShortOffAddr happyTable off_i
+
+
+
+
+-----------------------------------------------------------------------------
+-- Error recovery (0# is the error token)
+
+-- parse error if we are in recovery and we fail again
+happyFail 0# tk old_st _ stk@(x `HappyStk` _) =
+     let i = (case Happy_GHC_Exts.unsafeCoerce# x of { (Happy_GHC_Exts.I# (i)) -> i }) in
+--      trace "failing" $ 
+        happyError_ i tk
+
+{-  We don't need state discarding for our restricted implementation of
+    "error".  In fact, it can cause some bogus parses, so I've disabled it
+    for now --SDM
+
+-- discard a state
+happyFail  0# tk old_st (HappyCons ((action)) (sts)) 
+                                                (saved_tok `HappyStk` _ `HappyStk` stk) =
+--      trace ("discarding state, depth " ++ show (length stk))  $
+        happyDoAction 0# tk action sts ((saved_tok`HappyStk`stk))
+-}
+
+-- Enter error recovery: generate an error token,
+--                       save the old token and carry on.
+happyFail  i tk (action) sts stk =
+--      trace "entering error recovery" $
+        happyDoAction 0# tk action sts ( (Happy_GHC_Exts.unsafeCoerce# (Happy_GHC_Exts.I# (i))) `HappyStk` stk)
+
+-- Internal happy errors:
+
+notHappyAtAll :: a
+notHappyAtAll = error "Internal Happy error\n"
+
+-----------------------------------------------------------------------------
+-- Hack to get the typechecker to accept our action functions
+
+
+happyTcHack :: Happy_GHC_Exts.Int# -> a -> a
+happyTcHack x y = y
+{-# INLINE happyTcHack #-}
+
+
+-----------------------------------------------------------------------------
+-- Seq-ing.  If the --strict flag is given, then Happy emits 
+--      happySeq = happyDoSeq
+-- otherwise it emits
+--      happySeq = happyDontSeq
+
+happyDoSeq, happyDontSeq :: a -> b -> b
+happyDoSeq   a b = a `seq` b
+happyDontSeq a b = b
+
+-----------------------------------------------------------------------------
+-- Don't inline any functions from the template.  GHC has a nasty habit
+-- of deciding to inline happyGoto everywhere, which increases the size of
+-- the generated parser quite a bit.
+
+
+{-# NOINLINE happyDoAction #-}
+{-# NOINLINE happyTable #-}
+{-# NOINLINE happyCheck #-}
+{-# NOINLINE happyActOffsets #-}
+{-# NOINLINE happyGotoOffsets #-}
+{-# NOINLINE happyDefActions #-}
+
+{-# NOINLINE happyShift #-}
+{-# NOINLINE happySpecReduce_0 #-}
+{-# NOINLINE happySpecReduce_1 #-}
+{-# NOINLINE happySpecReduce_2 #-}
+{-# NOINLINE happySpecReduce_3 #-}
+{-# NOINLINE happyReduce #-}
+{-# NOINLINE happyMonadReduce #-}
+{-# NOINLINE happyGoto #-}
+{-# NOINLINE happyFail #-}
+
+-- end of Happy Template.
diff --git a/full-text-search.cabal b/full-text-search.cabal
--- a/full-text-search.cabal
+++ b/full-text-search.cabal
@@ -1,11 +1,13 @@
 name:                full-text-search
-version:             0.2.0.0
+version:             0.2.1.0
 synopsis:            In-memory full text search engine
 description:         An in-memory full text search engine library. It lets you
                      run full-text queries on a collection of your documents.
                      .
                      Features:
                      .
+                     * Keyword queries and auto-complete\/auto-suggest queries.
+                     .
                      * Can search over any type of \"document\".
                        (You explain how to extract search terms from them.)
                      .
@@ -23,46 +25,111 @@
                      * In-memory but quite compact. It does not keep a copy of
                        your original documents.
                      .
+                     * Quick incremental index updates, making it possible to
+                       keep your text search in-sync with your data.
+                     .
                      It is independent of the document type, so you have to
                      write the document-specific parts: extracting search terms
-                     and any case-normalisation or stemming. This is quite
-                     easy using libraries such as
+                     and any stop words, case-normalisation or stemming. This
+                     is quite easy using libraries such as
                      <http://hackage.haskell.org/package/tokenize tokenize> and
                      <http://hackage.haskell.org/package/snowball snowball>.
                      .
-                     For an example, see the code for the
+                     The source package includes a demo to illustrate how to
+                     use the library. The demo is a simplified version of how
+                     the library is used in the
                      <http://hackage.haskell.org/package/hackage-server hackage-server>
-                     where it is used for the package search feature.
+                     where it provides the backend for the package search feature.
 
 license:             BSD3
 license-file:        LICENSE
 author:              Duncan Coutts
 maintainer:          Duncan Coutts <duncan@well-typed.com>
-copyright:           2013-2014 Duncan Coutts, 2014 Well-Typed LLP
-category:            Data, Text, NLP
+copyright:           2013-2014 Duncan Coutts, 2014 Well-Typed LLP,
+                     2014 IRIS Connect Ltd.
+category:            Data, Text, Search
 build-type:          Simple
 cabal-version:       >=1.10
+extra-source-files:  changelog
 
 source-repository head
   type:              darcs
   location:          http://code.haskell.org/full-text-search/
 
+flag build-search-demo
+  default:           False
+  description:       Build a little program illustrating the use of the library
+
 library
   exposed-modules:     Data.SearchEngine,
                        Data.SearchEngine.BM25F
-  other-modules:       Data.SearchEngine.DocFeatVals,
+  other-modules:       Data.SearchEngine.Types,
+                       Data.SearchEngine.Update,
+                       Data.SearchEngine.Query,
+                       Data.SearchEngine.Autosuggest,
+                       Data.SearchEngine.SearchIndex,
+                       Data.SearchEngine.DocFeatVals,
                        Data.SearchEngine.TermBag,
                        Data.SearchEngine.DocTermIds,
-                       Data.SearchEngine.SearchIndex,
                        Data.SearchEngine.DocIdSet
   other-extensions:    BangPatterns,
                        NamedFieldPuns,
                        RecordWildCards,
-                       GeneralizedNewtypeDeriving
+                       GeneralizedNewtypeDeriving,
+                       ScopedTypeVariables
   build-depends:       base       >=4.5  && <4.7,
                        array      >=0.4  && <0.5,
                        vector     >=0.10 && <0.11,
                        containers >=0.4  && <0.6,
                        text       >=0.11 && <1.2
   default-language:    Haskell2010
+  ghc-options:         -Wall -funbox-strict-fields
+
+
+executable search-demo
+  main-is:             Main.hs
+  other-modules:       PackageSearch
+                       ExtractNameTerms
+                       ExtractDescriptionTerms
+                       PackageIndexUtils
+                       -- support code for package descriptions:
+                       HaddockHtml
+                       HaddockLex
+                       HaddockParse
+                       HaddockTypes
+  hs-source-dirs:      demo
+  if !flag(build-search-demo)
+    buildable:         False
+  else
+    build-depends:     full-text-search,
+                       base, text, containers, array,
+                       tokenize      == 0.1.*,
+                       snowball      == 1.0.*,
+                       transformers,
+                       split         == 0.2.*,
+                       Cabal         >= 1.14 && < 1.20,
+                       bytestring, filepath, directory, tar, time, mtl
+  build-tools:         alex, happy
+  default-language:    Haskell2010
+  other-extensions:    GeneralizedNewtypeDeriving
   ghc-options:         -Wall
+
+test-suite qc-props
+  type:                exitcode-stdio-1.0
+  main-is:             Main.hs
+  hs-source-dirs:      . tests
+  build-depends:       base,
+                       array,
+                       vector,
+                       containers,
+                       text,
+                       QuickCheck       ==2.*,
+                       tasty            ==0.8.*,
+                       tasty-quickcheck ==0.8.*
+  other-modules:       Test.Data.SearchEngine.TermBag,
+                       Test.Data.SearchEngine.DocIdSet,
+                       Data.SearchEngine.DocTermIds,
+                       Data.SearchEngine.DocIdSet
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,38 @@
+
+import qualified Test.Data.SearchEngine.DocIdSet as DocIdSet
+import qualified Test.Data.SearchEngine.TermBag  as TermBag
+
+import Test.Tasty
+import Test.Tasty.QuickCheck
+
+
+main :: IO ()
+main = defaultMain $
+         testGroup ""
+           [ docIdSetTests
+           , termBagTests
+           ]
+
+docIdSetTests :: TestTree
+docIdSetTests =
+    testGroup "TermIdSet"
+      [ testProperty "prop_insert"  DocIdSet.prop_insert
+      , testProperty "prop_delete"  DocIdSet.prop_delete
+      , testProperty "prop_delete'" DocIdSet.prop_delete'
+      , testProperty "prop_union"   DocIdSet.prop_union
+      , testProperty "prop_union'"  DocIdSet.prop_union'
+      ]
+
+termBagTests :: TestTree
+termBagTests =
+    testGroup "TermBag"
+      [ testProperty "prop_invariant"   TermBag.prop_invariant
+      , testProperty "prop_elems"       TermBag.prop_elems
+      , testProperty "prop_fromList"    TermBag.prop_fromList
+      , testProperty "prop_size"        TermBag.prop_size
+      , testProperty "prop_termCount"   TermBag.prop_termCount
+      , testProperty "prop_denseTable1" TermBag.prop_denseTable1
+      , testProperty "prop_denseTable2" TermBag.prop_denseTable2
+      ]
+
+
diff --git a/tests/Test/Data/SearchEngine/DocIdSet.hs b/tests/Test/Data/SearchEngine/DocIdSet.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Data/SearchEngine/DocIdSet.hs
@@ -0,0 +1,55 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Test.Data.SearchEngine.DocIdSet where
+
+import Data.SearchEngine.DocIdSet
+
+import qualified Data.Vector.Unboxed as Vec
+import qualified Data.List as List
+import Test.QuickCheck
+
+
+instance Arbitrary DocIdSet where
+  arbitrary = fromList `fmap` (listOf arbitrary)
+
+instance Arbitrary DocId where
+  arbitrary = DocId `fmap` choose (0,15)
+
+
+prop_insert :: DocIdSet -> DocId -> Bool
+prop_insert dset x =
+    let dset' = insert x dset
+     in invariant dset && invariant dset'
+     && all (`member` dset') (x : toList dset)
+
+prop_delete :: DocIdSet -> DocId -> Bool
+prop_delete dset x =
+    let dset' = delete x dset
+     in invariant dset && invariant dset'
+     && all (`member` dset') (List.delete x (toList dset))
+     && not (x `member` dset')
+
+prop_delete' :: DocIdSet -> Bool
+prop_delete' dset =
+    all (prop_delete dset) (toList dset)
+
+prop_union :: DocIdSet -> DocIdSet -> Bool
+prop_union dset1 dset2 =
+    let dset  = union dset1 dset2
+        dset' = fromList (List.union (toList dset1) (toList dset2))
+
+     in invariant dset && invariant dset'
+     && dset == dset'
+
+prop_union' :: DocIdSet -> DocIdSet -> Bool
+prop_union' dset1 dset2 =
+    let dset   = union dset1 dset2
+        dset'  = List.foldl' (\s i -> insert i s) dset1 (toList dset2)
+        dset'' = List.foldl' (\s i -> insert i s) dset2 (toList dset1)
+     in invariant dset && invariant dset' && invariant dset''
+     && dset == dset'
+     && dset' == dset''
+
+member :: DocId -> DocIdSet -> Bool
+member x (DocIdSet vec) =
+   x `List.elem` Vec.toList vec
+
diff --git a/tests/Test/Data/SearchEngine/TermBag.hs b/tests/Test/Data/SearchEngine/TermBag.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Data/SearchEngine/TermBag.hs
@@ -0,0 +1,55 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Test.Data.SearchEngine.TermBag where
+
+import Data.SearchEngine.TermBag
+
+import qualified Data.Vector.Unboxed as Vec
+import qualified Data.List as List
+import Test.QuickCheck
+
+
+instance Arbitrary TermBag where
+  arbitrary = fromList `fmap` (listOf arbitrary)
+
+instance Arbitrary TermId where
+  arbitrary = TermId `fmap` choose (0,5)
+
+prop_invariant :: TermBag -> Bool
+prop_invariant = invariant
+
+prop_elems :: [TermId] -> Bool
+prop_elems tids =
+    (map head . List.group . List.sort) tids
+ == (elems . fromList) tids
+
+prop_fromList :: [TermId] -> Bool
+prop_fromList tids =
+    (map (\g -> (head g, fromIntegral (length g `min` 255)))
+     . List.group . List.sort) tids
+ == (toList . fromList) tids
+
+prop_size :: [TermId] -> Bool
+prop_size tids =
+    (size . fromList) tids == length tids
+
+prop_termCount :: [TermId] -> Bool
+prop_termCount tids =
+    and [ termCount bag tid == count 
+        | let bag = fromList tids 
+        , (tid, count) <- toList bag
+        ]
+
+prop_denseTable1 :: [TermBag] -> Bool
+prop_denseTable1 bags =
+    Vec.toList terms == (List.sort . foldr List.union [] . map elems) bags
+  where
+    (terms, _) = denseTable bags
+
+prop_denseTable2 :: [TermBag] -> Bool
+prop_denseTable2 bags =
+    and [ termCount bag (terms Vec.! t) == counts Vec.! (b * numTerms + t)
+        | let (terms, counts) = denseTable bags
+              numTerms        = Vec.length terms
+        , (b, bag) <- zip [0..] bags
+        ,  t       <- [0..Vec.length terms - 1]
+        ] 
