packages feed

suffixtree (empty) → 0.1

raw patch · 4 files changed

+273/−0 lines, 4 filesdep +QuickCheckdep +basebuild-type:Customsetup-changed

Dependencies added: QuickCheck, base

Files

+ Data/SuffixTree.hs view
@@ -0,0 +1,231 @@+{- Fastest when compiled as follows: ghc -O2 -optc-O3 -funbox-strict-fields -}++-----------------------------------------------------------------------------+-- |+-- Module      :  Data.SuffixTree+-- Copyright   :  (c) Bryan O'Sullivan 2007+-- License     :  BSD-style+-- Maintainer  :  bos@serpentine.com+-- Stability   :  experimental+-- Portability :  portable+--+-- A lazy, efficient suffix tree implementation.+--+-- Since many function names (but not the type name) clash with+-- "Prelude" names, this module is usually imported @qualified@, e.g.+--+-- >  import Data.SuffixTree (STree)+-- >  import qualified Data.SuffixTree as T+--+-- The implementation is based on the first of those described in the+-- following paper:+-- +--   * Robert Giegerich and Stefan Kurtz, \"/A comparison of+--     imperative and purely functional suffix tree constructions/\",+--     Science of Computer Programming 25(2-3):187-218, 1995,+--     <http://citeseer.ist.psu.edu/giegerich95comparison.html>+-- +-- This implementation constructs the suffix tree lazily, so subtrees+-- are not created until they are traversed.  Two construction+-- functions are provided, 'constructWith' for sequences composed of+-- small alphabets, and 'construct' for larger alphabets.++module Data.SuffixTree+    (+    -- * Types+      Alphabet+    , Prefix+    , STree(..)++    -- * Construction+    , constructWith+    , construct++    -- * Querying+    , elem+    , find++    -- * Traversal+    , fold+    , fold'++    -- * Other useful functions+    , prefix+    , suffixes+    ) where+                   +import Prelude hiding (elem)+import qualified Data.Map as M+import Data.List (foldl')+import Control.Arrow (second)+import qualified Data.ByteString as SB+import qualified Data.ByteString.Lazy as LB+import Data.Maybe (listToMaybe, mapMaybe)++-- | The length of a prefix list.  This type is formulated to do cheap+-- work eagerly (to avoid constructing a pile of deferred thunks),+-- while deferring potentially expensive work.+data Length a = Exactly {-# UNPACK #-} !Int+              | Sum {-# UNPACK #-} !Int [a]+              deriving (Show)++-- | The list of symbols that 'constructWith' can possibly see in its+-- input.+type Alphabet a = [a]++-- | The prefix string associated with an 'Edge'.+newtype Prefix a = Prefix ([a], Length a)+    deriving (Show)++instance (Eq a) => Eq (Prefix a) where+    a == b = prefix a == prefix b++type EdgeFunction a = [[a]] -> (Length a, [[a]])++-- | A suffix tree.  The implementation is exposed to ease the+-- development of custom traversal functions.  Note that @('Prefix' a,+-- 'STree' a)@ pairs are not stored in any order.+data STree a = Node [(Prefix a, STree a)]+             | Leaf+               deriving (Show)++-- | Obtain the list stored in a 'Prefix'.+prefix :: Prefix a -> [a]+prefix (Prefix (ys, Exactly n)) = take n ys+prefix (Prefix (ys, Sum n xs)) = tk n ys+    where tk 0 ys = zipWith (const id) xs ys+          tk n (y:ys) = y : tk (n-1) ys++-- | /O(n)/. Fold the edges in a tree, from bottom to top.  Suitable+-- for lazy use.+fold :: (Prefix a -> b -> b) -> b -> STree a -> b+fold _ z Leaf = z+fold f z (Node es) = foldr (\(e, t) v -> f e (fold f v t)) z es++-- | /O(n)/. Fold the edges in a tree, from bottom to top.  Suitable+-- for strict use.+fold' :: (a -> Prefix b -> a) -> a -> STree b -> a+fold' _ z Leaf = z+fold' f z (Node es) = foldl' (\v (e, t) -> f (fold' f v t) e) z es++-- | Increment the length of a prefix.+inc :: Length a -> Length a+inc (Exactly n) = Exactly (n+1)+inc (Sum n xs)  = Sum (n+1) xs++lazyTreeWith :: (Eq a) => EdgeFunction a -> Alphabet a -> [a] -> STree a+lazyTreeWith edge alphabet = suf . suffixes+    where suf [[]] = Leaf+          suf ss = Node [(Prefix (a:sa, inc cpl), suf ssr)+                         | a <- alphabet,+                           n@(sa:_) <- [ss `clusterBy` a],+                           (cpl,ssr) <- [edge n]]+          clusterBy ss a = [cs | c:cs <- ss, c == a]++-- | Return all non-empty suffixes of the argument, longest first.+-- Behaves as follows:+--+-- >suffixes xs == init (tails xs)+suffixes :: [a] -> [[a]]+suffixes xs@(_:xs') = xs : suffixes xs'+suffixes _ = []++lazyTree :: (Ord a) => EdgeFunction a -> [a] -> STree a+lazyTree edge = suf . suffixes+    where suf [[]] = Leaf+          suf ss = Node [(Prefix (a:sa, inc cpl), suf ssr)+                         | (a, n@(sa:_)) <- suffixMap ss,+                           (cpl,ssr) <- [edge n]]++suffixMap :: Ord a => [[a]] -> [(a, [[a]])]+suffixMap = map (second reverse) . M.toList . foldl' step M.empty+    where step m (x:xs) = M.alter (f xs) x m+          step m _ = m+          f x Nothing = Just [x]+          f x (Just xs) = Just (x:xs)++cst :: Eq a => EdgeFunction a+cst [s] = (Sum 0 s, [[]])+cst awss@((a:w):ss)+    | null [c | c:_ <- ss, a /= c] = let cpl' = inc cpl+                                     in seq cpl' (cpl', rss)+    | otherwise = (Exactly 0, awss)+    where (cpl, rss) = cst (w:[u | _:u <- ss])++pst :: Eq a => EdgeFunction a+pst = g . dropNested+    where g [s] = (Sum 0 s, [[]])+          g ss  = (Exactly 0, ss)+          dropNested ss@[_] = ss+          dropNested awss@((a:w):ss)+              | null [c | c:_ <- ss, a /= c] = [a:s | s <- rss]+              | otherwise = awss+              where rss = dropNested (w:[u | _:u <- ss])++{-# SPECIALISE constructWith :: [Char] -> [Char] -> STree Char #-}+{-# SPECIALISE constructWith :: [[Char]] -> [[Char]] -> STree [Char] #-}+{-# SPECIALISE constructWith :: [SB.ByteString] -> [SB.ByteString]+                             -> STree SB.ByteString #-}+{-# SPECIALISE constructWith :: [LB.ByteString] -> [LB.ByteString]+                             -> STree LB.ByteString #-}+{-# SPECIALISE constructWith :: (Eq a) => [[a]] -> [[a]] -> STree [a] #-}++-- | /O(k n log n)/.  Construct a suffix tree using the given+-- alphabet.  The performance of this function is linear in the size+-- /k/ of the alphabet.  That makes this function suitable for small+-- alphabets, such as DNA nucleotides.  For an alphabet containing+-- more than a few symbols, 'construct' is usually several orders of+-- magnitude faster.+constructWith :: (Eq a) => Alphabet a -> [a] -> STree a+constructWith = lazyTreeWith cst++{-# SPECIALISE construct :: [Char] -> STree Char #-}+{-# SPECIALISE construct :: [[Char]] -> STree [Char] #-}+{-# SPECIALISE construct :: [SB.ByteString] -> STree SB.ByteString #-}+{-# SPECIALISE construct :: [LB.ByteString] -> STree LB.ByteString #-}+{-# SPECIALISE construct :: (Ord a) => [[a]] -> STree [a] #-}++-- | /O(n log n)/.  Construct a suffix tree.+construct :: (Ord a) => [a] -> STree a+construct = lazyTree cst++suffix :: (Eq a) => [a] -> [a] -> Maybe [a]+suffix (l:ls) (x:xs) | l == x = suffix ls xs+                     | otherwise = Nothing+suffix _ xs = Just xs++{-# SPECIALISE elem :: [Char] -> STree Char -> Bool #-}+{-# SPECIALISE elem :: [[Char]] -> STree [Char] -> Bool #-}+{-# SPECIALISE elem :: [SB.ByteString] -> STree SB.ByteString -> Bool #-}+{-# SPECIALISE elem :: [LB.ByteString] -> STree LB.ByteString -> Bool #-}+{-# SPECIALISE elem :: (Eq a) => [[a]] -> STree [a] -> Bool #-}++-- | /O(n)/.  Indicate the suffix tree contains the given subsequence.+-- Performance is linear in the length of the subsequence.+elem :: (Eq a) => [a] -> STree a -> Bool+elem [] _ = True+elem _ Leaf = False+elem xs (Node es) = any pfx es+    where pfx (e, t) = maybe False (`elem` t) (suffix (prefix e) xs)++{-# SPECIALISE find :: [Char] -> STree Char+                    -> Maybe (Prefix Char, STree Char) #-}+{-# SPECIALISE find :: [[Char]] -> STree [Char]+                    -> Maybe (Prefix [Char], STree [Char]) #-}+{-# SPECIALISE find :: [SB.ByteString] -> STree SB.ByteString+                    -> Maybe (Prefix SB.ByteString, STree SB.ByteString) #-}+{-# SPECIALISE find :: [LB.ByteString] -> STree LB.ByteString+                    -> Maybe (Prefix LB.ByteString, STree LB.ByteString) #-}+{-# SPECIALISE find :: (Eq a) => [[a]] -> STree [a]+                    -> Maybe (Prefix [a], STree [a]) #-}++-- | /O(n)/.  Return the portion of the suffix tree at which the given+-- subsequence is located.  If the subsequence is not found, return+-- 'Nothing'.+find :: (Eq a) => [a] -> STree a -> Maybe (Prefix a, STree a)+find _ Leaf = Nothing+find xs (Node es) = listToMaybe (mapMaybe pfx es)+    where pfx p@(e, t) = suffix (prefix e) xs >>= \suf ->+            case suf of+              [] -> return p+              s -> find s t
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) Bryan O'Sullivan 2007.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ suffixtree.cabal view
@@ -0,0 +1,12 @@+Name:                suffixtree+Version:             0.1+Description:         An efficient, lazy suffix tree implementation+License:             BSD3+License-File:        LICENSE+Author:              Bryan O'Sullivan <bos@serpentine.com>+Maintainer:          bos@serpentine.com+Build-Depends:       base, QuickCheck+-- add fps for ghc 6.4.2+Exposed-modules:     Data.SuffixTree+ghc-options:         -W -O2 -optc-O3 -funbox-strict-fields -fno-warn-incomplete-patterns+