packages feed

full-text-search 0.2.0.0 → 0.2.1.0

raw patch · 27 files changed

+3692/−465 lines, 27 filesdep +Cabaldep +QuickCheckdep +bytestringdep ~arraydep ~basedep ~containersnew-component:exe:search-demo

Dependencies added: Cabal, QuickCheck, bytestring, directory, filepath, full-text-search, mtl, snowball, split, tar, tasty, tasty-quickcheck, time, tokenize, transformers

Dependency ranges changed: array, base, containers, text, vector

Files

Data/SearchEngine.hs view
@@ -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 
+ Data/SearchEngine/Autosuggest.hs view
@@ -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+    ]+
Data/SearchEngine/BM25F.hs view
@@ -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) ]   ------------------
Data/SearchEngine/DocIdSet.hs view
@@ -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--}
Data/SearchEngine/DocTermIds.hs view
@@ -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
+ Data/SearchEngine/Query.hs view
@@ -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+
Data/SearchEngine/SearchIndex.hs view
@@ -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) 
Data/SearchEngine/TermBag.hs view
@@ -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)
+ Data/SearchEngine/Types.hs view
@@ -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"+
+ Data/SearchEngine/Update.hs view
@@ -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+
LICENSE view
@@ -1,4 +1,5 @@ Copyright (c) 2013-2014 Duncan Coutts, 2014 Well-Typed LLP+              2014 IRIS Connect Ltd.  All rights reserved. 
+ changelog view
@@ -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)+
+ demo/ExtractDescriptionTerms.hs view
@@ -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) ]+-}
+ demo/ExtractNameTerms.hs view
@@ -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 ]+-}
+ demo/HaddockHtml.hs view
@@ -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
+ demo/HaddockLex.x view
@@ -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)+}
+ demo/HaddockParse.y view
@@ -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+}
+ demo/HaddockTypes.hs view
@@ -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
+ demo/Main.hs view
@@ -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+
+ demo/PackageIndexUtils.hs view
@@ -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
+ demo/PackageSearch.hs view
@@ -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"]+
+ dist/build/search-demo/search-demo-tmp/HaddockLex.hs view
@@ -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
+ dist/build/search-demo/search-demo-tmp/HaddockParse.hs view
@@ -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.
full-text-search.cabal view
@@ -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+
+ tests/Main.hs view
@@ -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+      ]++
+ tests/Test/Data/SearchEngine/DocIdSet.hs view
@@ -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+
+ tests/Test/Data/SearchEngine/TermBag.hs view
@@ -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]+        ]