seqloc (empty) → 0.1
raw patch · 12 files changed
+1370/−0 lines, 12 filesdep +QuickCheckdep +attoparsecdep +basesetup-changed
Dependencies added: QuickCheck, attoparsec, base, bytestring, haskell98, random
Files
- LICENSE +21/−0
- Setup.hs +2/−0
- seqloc.cabal +43/−0
- src/Bio/SeqLoc/LocRepr.hs +25/−0
- src/Bio/SeqLoc/Location.hs +237/−0
- src/Bio/SeqLoc/OnSeq.hs +71/−0
- src/Bio/SeqLoc/Position.hs +72/−0
- src/Bio/SeqLoc/SeqLike.hs +127/−0
- src/Bio/SeqLoc/SpliceLocation.hs +188/−0
- src/Bio/SeqLoc/Strand.hs +76/−0
- src/Bio/SeqLoc/Transcript.hs +116/−0
- test/TestMain.hs +392/−0
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License++Copyright (c) 2008-2011 Nicholas Ingolia++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ seqloc.cabal view
@@ -0,0 +1,43 @@+Name: seqloc+Version: 0.1+Cabal-Version: >= 1.2+Synopsis: Handle sequence locations for bioinformatics+Description: Handle sequence locations for bioinformatics+Homepage: http://www.ingolia-lab.org/software/seqloc/+License: BSD3+License-file: LICENSE+Author: Nick Ingolia+Maintainer: nick@ingolia.org+Build-Type: Simple+Cabal-Version: >= 1.2++Category: Bioinformatics++Flag Tests+ Description: Build test program+ Default: False++Library+ Exposed-modules: Bio.SeqLoc.Strand, Bio.SeqLoc.Position,+ Bio.SeqLoc.SeqLike, Bio.SeqLoc.Location,+ Bio.SeqLoc.SpliceLocation,+ Bio.SeqLoc.OnSeq,+ Bio.SeqLoc.LocRepr,+ Bio.SeqLoc.Transcript+ Build-depends: base >= 4.3 && < 5, bytestring, haskell98, attoparsec >= 0.8.5+ Hs-Source-Dirs: src+ Ghc-options: -Wall++Executable test-seqloc+ if !flag(Tests)+ Buildable: False+ Main-is: TestMain.hs+ Other-modules: Bio.SeqLoc.Strand, Bio.SeqLoc.Position,+ Bio.SeqLoc.SeqLike, Bio.SeqLoc.Location,+ Bio.SeqLoc.SpliceLocation,+ Bio.SeqLoc.OnSeq,+ Bio.SeqLoc.LocRepr+ Build-depends: base >= 4.3 && < 5, bytestring, haskell98, attoparsec >= 0.8.5,+ QuickCheck, random+ Hs-Source-Dirs: src, test+ Ghc-options: -Wall
+ src/Bio/SeqLoc/LocRepr.hs view
@@ -0,0 +1,25 @@+module Bio.SeqLoc.LocRepr+ ( LocRepr(..), reprStr+ , unreprMaybe, unreprEither, unreprErr+ ) + where++import qualified Data.ByteString.Char8 as BS++import qualified Data.Attoparsec.Zepto as ZP++class LocRepr l where+ repr :: l -> BS.ByteString+ unrepr :: ZP.Parser l+ +reprStr :: (LocRepr l) => l -> String+reprStr = BS.unpack . repr+ +unreprMaybe :: (LocRepr l) => BS.ByteString -> Maybe l+unreprMaybe = either (const Nothing) Just . ZP.parse unrepr++unreprEither :: (LocRepr l) => BS.ByteString -> Either String l+unreprEither = ZP.parse unrepr++unreprErr :: (LocRepr l) => BS.ByteString -> l+unreprErr = either error id . ZP.parse unrepr
+ src/Bio/SeqLoc/Location.hs view
@@ -0,0 +1,237 @@+{-# LANGUAGE TypeFamilies, FlexibleContexts #-}+{-| Data type for a sequence location consiting of a contiguous range+of positions on the sequence.++Throughout, /sequence position/ refers to a 'Pos.Pos' which includes a+strand, as opposed to an /offset/, which refers to a 'Pos.Offset' with+no strand.++ -}++module Bio.SeqLoc.Location + ( -- * Sequence locations+ Location(..), overlaps+ -- * Contiguous sequence locations+ , ContigLoc, offset5, fromStartEnd, fromPosLen, fromBoundsStrand+ -- * Transforming locations+ , slide+ ) + where++import Prelude hiding (length)++import Control.Applicative+import Control.Monad+import qualified Data.ByteString.Char8 as BS++import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr+import qualified Bio.SeqLoc.Position as Pos+import Bio.SeqLoc.Strand+import qualified Bio.SeqLoc.SeqLike as SeqLike++class Location l where+ strand :: l -> Strand+ length :: l -> Pos.Offset+ + -- | The bounds of a sequence location. This is a pair consisting+ -- of the lowest and highest sequence offsets covered by the region.+ -- The bounds ignore the strand of the sequence location, and the+ -- first element of the pair will always be lower than the second.+ bounds :: l -> (Pos.Offset, Pos.Offset)++ -- | Sequence position of the start of the location. This is the 5'+ -- end on the location strand, which will have a higher offset than+ -- 'endPos' if the location is on the 'RevCompl' strand.+ startPos :: l -> Pos.Pos++ -- | Sequence position of the end of the location, as described in+ -- 'startPos'.+ endPos :: l -> Pos.Pos++ -- | Extract 'Just' the nucleotide 'SeqLike' for the sequence+ -- location, or 'Nothing' if f any part of the location lies outside+ -- the bounds of the sequence.+ seqData :: (SeqLike.SeqLike s, Stranded s) => s -> l -> Maybe s+ + -- | As 'seqData', extract the nucleotide subsequence for the+ -- location, but any positions in the location lying outside the+ -- bounds of the sequence are returned as @N@.+ seqDataPad :: (SeqLike.SeqLike s, Stranded s) => s -> l -> s+ + -- | Given a sequence position and a sequence location relative to+ -- the same sequence, compute a new position representing the+ -- original position relative to the subsequence defined by the+ -- location. If the sequence position lies outside of the sequence+ -- location, @Nothing@ is returned; thus, the offset of the new+ -- position will always be in the range @[0, length l - 1]@.+ posInto :: Pos.Pos -> l -> Maybe Pos.Pos+ + -- | Given a sequence location and a sequence position within that+ -- location, compute a new position representing the original+ -- position relative to the outer sequence. If the sequence+ -- position lies outside the location, @Nothing@ is returned.+ -- + -- This function inverts 'posInto' when the sequence position lies+ -- within the position is actually within the location.+ posOutof :: Pos.Pos -> l -> Maybe Pos.Pos + + -- | For an enclosing location and a sublocation in the same+ -- coordinate system, find the image of the sublocation relative to+ -- the enclosing location. For example, if the enclosing location+ -- spans (100, 150) and the sublocation is (110, 120) then+ -- 'clocInto' will be (10, 20).+ clocInto :: ContigLoc -> l -> Maybe ContigLoc+ + -- | Returns a sequence location produced by finding the inverse+ -- image of a sublocation, with coordinates given relative to an+ -- enclosing location, in the coordinate system of the enclosing+ -- location. For example, if the enclosing location spans (100,+ -- 150) and the sublocation is (10, 20) then 'clocOutof' will be+ -- (110, 120).+ clocOutof :: ContigLoc -> l -> Maybe l++ -- | Returns a sequence location produced by extending the original+ -- location on each end, based on a pair of (/5\' extension/, /3\'+ -- extension/). The 5\' extension is applied to the 5\' end of the+ -- location on the location strand; if the location is on the+ -- 'RevCompl' strand, the 5\' end will have a higher offset than the+ -- 3\' end and this offset will increase by the amount of the 5\'+ -- extension. Similarly, the 3\' extension is applied to the 3\'+ -- end of the location.+ extend :: (Pos.Offset, Pos.Offset) -> l -> l++ -- | Returns @True@ when a sequence offset lies within a sequence+ -- location on the same sequence+ offsetWithin :: Pos.Offset -> l -> Bool++ -- | Returns @True@ when a sequence position lies within a sequence+ -- location on the same sequence, and occupies the same strand.+ posWithin :: Pos.Pos -> l -> Bool++ -- | Returns @True@ when two sequence locations overlap at any+ -- position.+ contigOverlaps :: ContigLoc -> l -> Bool++ -- | Contigs that comprise the location+ toContigs :: l -> [ContigLoc]+++overlaps :: (Location l1, Location l2) => l1 -> l2 -> Bool+overlaps l1 = any (\c2 -> contigOverlaps c2 l1) . toContigs++-- | Contiguous sequence location defined by a span of sequence+-- positions, lying on a specific strand of the sequence.+data ContigLoc = ContigLoc { offset5 :: !Pos.Offset -- ^ The offset of the 5\' end of the location, as a 0-based index+ , clocLength :: !Pos.Offset -- ^ The length of the location+ , clocStrand :: !Strand -- ^ The strand of the location+ } deriving (Eq, Ord, Show)++instance Stranded ContigLoc where+ revCompl (ContigLoc seq5 len str) = ContigLoc seq5 len $ revCompl str++to :: BS.ByteString+to = BS.pack "to"++instance LocRepr ContigLoc where+ repr cloc = let (seq5, seq3) = bounds cloc + in BS.concat [ repr seq5, to, repr seq3, repr . strand $ cloc ]+ unrepr = fromBoundsStrand <$> unrepr <* ZP.string to <*> unrepr <*> unrepr++instance Location ContigLoc where+ strand = clocStrand+ length = clocLength+ seqData sequ (ContigLoc seq5 len str) = liftM (stranded str) . (SeqLike.subseq seq5 len) $ sequ+ seqDataPad sequ (ContigLoc seq5 len str) = (stranded str) . (SeqLike.subseqPad seq5 len) $ sequ+ posInto = clocPosInto+ posOutof = clocPosOutof+ bounds (ContigLoc seq5 len _) = (seq5, seq5 + len - 1)+ startPos (ContigLoc seq5 len str) + = case str of+ Fwd -> Pos.Pos seq5 str+ RevCompl -> Pos.Pos (seq5 + len - 1) str+ endPos (ContigLoc seq5 len str) + = case str of+ Fwd -> Pos.Pos (seq5 + len - 1) str+ RevCompl -> Pos.Pos seq5 str+ clocInto = clocClocInto+ clocOutof = clocClocOutof+ extend = clocExtend+ offsetWithin off (ContigLoc seq5 len _)+ = (off >= seq5) && (off < seq5 + len)+ posWithin (Pos.Pos pos pStrand) (ContigLoc seq5 len cStrand) + = (pos >= seq5) && (pos < seq5 + len) && (cStrand == pStrand)+ contigOverlaps = clocOverlaps+ toContigs = (: [])++-- | Create a sequence location between 0-based starting and ending+-- bounds with a specified strand.+fromBoundsStrand :: Pos.Offset -> Pos.Offset -> Strand -> ContigLoc+fromBoundsStrand seq5 seq3 _ | seq3 < seq5 = error "Bio.SeqLoc.Location.fromBoundsStrand: seq3 < seq5"+fromBoundsStrand seq5 seq3 str = ContigLoc seq5 (1 + seq3 - seq5) str++-- | Create a sequence location lying between 0-based starting and+-- ending offsets. When @start < end@, the location+-- be on the forward strand, otherwise it will be on the+-- reverse complement strand.+fromStartEnd :: Pos.Offset -> Pos.Offset -> ContigLoc+fromStartEnd start end+ | start < end = ContigLoc start (1 + end - start) Fwd+ | otherwise = ContigLoc end (1 + start - end) RevCompl++-- | Create a sequence location from the sequence position of the+-- start of the location and the length of the position. The strand+-- of the location, and the direction it extends from the starting+-- position, are determined by the strand of the starting position.+fromPosLen :: Pos.Pos -> Pos.Offset -> ContigLoc+fromPosLen _ len | len < 0 = error "Bio.SeqLoc.Location.fromPosLen: len < 0"+fromPosLen (Pos.Pos off5 Fwd) len = ContigLoc off5 len Fwd+fromPosLen (Pos.Pos off3 RevCompl) len = ContigLoc (off3 - (len - 1)) len RevCompl++-- | Returns a location resulting from sliding the original location+-- along the sequence by a specified offset. A positive offset will+-- move the location away from the 5\' end of the forward stand of the+-- sequence regardless of the strand of the location itself. Thus,+-- +-- > slide (revCompl cloc) off == revCompl (slide cloc off)+slide :: Pos.Offset -> ContigLoc -> ContigLoc+slide dpos (ContigLoc seq5 len str) = ContigLoc (seq5 + dpos) len str++clocPosInto :: Pos.Pos -> ContigLoc -> Maybe Pos.Pos+clocPosInto (Pos.Pos pos pStrand) (ContigLoc seq5 len cStrand)+ | pos < seq5 || pos >= seq5 + len = Nothing+ | otherwise = Just $ case cStrand of+ Fwd -> Pos.Pos (pos - seq5) pStrand+ RevCompl -> Pos.Pos (seq5 + len - (pos + 1)) (revCompl pStrand)++clocPosOutof :: Pos.Pos -> ContigLoc -> Maybe Pos.Pos+clocPosOutof (Pos.Pos pos pStrand) (ContigLoc seq5 len cStrand)+ | pos < 0 || pos >= len = Nothing+ | otherwise = Just $ case cStrand of+ Fwd -> Pos.Pos (pos + seq5) pStrand+ RevCompl -> Pos.Pos (seq5 + len - (pos + 1)) (revCompl pStrand)++clocExtend :: (Pos.Offset, Pos.Offset) -> ContigLoc -> ContigLoc+clocExtend (ext5, ext3) (ContigLoc seq5 len str)+ = case str of+ Fwd -> ContigLoc (seq5 - ext5) (len + ext5 + ext3) str+ RevCompl -> ContigLoc (seq5 - ext3) (len + ext5 + ext3) str++clocClocInto :: ContigLoc -> ContigLoc -> Maybe ContigLoc+clocClocInto subcloc tocloc+ = case (posInto (startPos subcloc) tocloc, posInto (endPos subcloc) tocloc) of+ (Just start, Just _) -> Just (fromPosLen start (length subcloc))+ _ -> Nothing++clocClocOutof :: ContigLoc -> ContigLoc -> Maybe ContigLoc+clocClocOutof subcloc fromcloc+ = case (posOutof (startPos subcloc) fromcloc, posOutof (endPos subcloc) fromcloc) of+ (Just start, Just _) -> Just (fromPosLen start (length subcloc))+ _ -> Nothing++clocOverlaps :: ContigLoc -> ContigLoc -> Bool+clocOverlaps contig1 contig2+ = case (bounds contig1, bounds contig2) of+ ((low1, high1),(low2, high2)) -> (strand contig1 == strand contig2)+ && (low1 <= high2) && (low2 <= high1)
+ src/Bio/SeqLoc/OnSeq.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE FlexibleInstances, TypeFamilies, FlexibleContexts, GeneralizedNewtypeDeriving #-}++{-| ++Data types for sequence locations and sequence positions associated+with specific, named sequences.++-}++module Bio.SeqLoc.OnSeq ( + SeqName(..)+ + , OnSeq(..)+ + -- * Positions on named sequences+ , SeqOffset, SeqPos+ + -- * Contiguous location spans on named sequences+ , ContigSeqLoc++ -- * Arbitrary location spans on named sequences+ , SpliceSeqLoc+ + , andSameSeq+ + )+ where ++import Control.Applicative+import qualified Data.ByteString.Char8 as BS+import Data.ByteString.Internal (c2w)+import Data.String++import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr+import qualified Bio.SeqLoc.Location as Loc+import qualified Bio.SeqLoc.Position as Pos+import qualified Bio.SeqLoc.SpliceLocation as SpLoc+import Bio.SeqLoc.Strand++newtype SeqName = SeqName { unSeqName :: BS.ByteString } deriving (Show, Read, Eq, Ord, IsString)++data OnSeq s = OnSeq { onSeqName :: !SeqName, unOnSeq :: !s } deriving (Show, Read, Eq, Ord)++at :: BS.ByteString+at = BS.singleton '@'++instance Stranded s => Stranded (OnSeq s) where+ revCompl (OnSeq name obj) = OnSeq name (revCompl obj)++instance LocRepr s => LocRepr (OnSeq s) where+ repr (OnSeq name obj) = BS.concat [ unSeqName name, at, repr obj ]+ unrepr = OnSeq <$> (SeqName <$> ZP.takeWhile (/= c2w '@')) <* ZP.string at <*> unrepr++type SeqOffset = OnSeq Pos.Offset++-- | A position on a named sequence+type SeqPos = OnSeq Pos.Pos++-- | A location consisting of a contiguous span of positions on a+-- named sequence.+type ContigSeqLoc = OnSeq Loc.ContigLoc++-- | A general location, consisting of spans of sequence positions on+-- a specific, named sequence.+type SpliceSeqLoc = OnSeq SpLoc.SpliceLoc++andSameSeq :: (a -> b -> Bool) -> OnSeq a -> OnSeq b -> Bool+andSameSeq p (OnSeq n1 x) (OnSeq n2 y) | n1 == n2 = p x y+ | otherwise = False
+ src/Bio/SeqLoc/Position.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving, OverloadedStrings #-}++{-| Data type for a sequence position.++Zero-based 'Offset'indices are used throughout, to facilitate direct+use of indexing functions on 'SeqData'. ++-}++module Bio.SeqLoc.Position ( + -- * Sequence positions+ Offset(..)+ , Pos(..)++ -- * Manipulating positions+ , slide++ -- * Extracting sequences+ , atPos + )+ where ++import Control.Applicative+import Control.Monad (liftM)+import qualified Data.ByteString as BSW+import qualified Data.ByteString.Char8 as BS++import qualified Data.Attoparsec.Char8 as AP (isDigit_w8)+import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr+import Bio.SeqLoc.SeqLike+import Bio.SeqLoc.Strand++-- | Unstranded offset in a sequence+newtype Offset = Offset { unOffset :: Int } deriving (Eq, Ord, Show, Read, Num, Real, Enum, Integral)++instance LocRepr Offset where+ repr = BS.pack . show . unOffset+ unrepr = (negate <$> (ZP.string "-" *> decimal)) <|> (ZP.string "+" *> decimal) <|> decimal+ where decimal = Offset . BSW.foldl' step 0 <$> ZP.takeWhile AP.isDigit_w8+ step a w = a * 10 + fromIntegral (w - 48)++-- | Stranded position in a sequence+data Pos = Pos { offset :: !Offset -- ^ 0-based index of the position+ , strand :: !Strand -- ^ Strand of the position+ }+ deriving (Eq, Ord, Show, Read)+ +instance Stranded Pos where+ revCompl (Pos off str) = Pos off (revCompl str)+ +instance LocRepr Pos where+ repr (Pos off str) = BS.concat [ repr off, repr str ]+ unrepr = Pos <$> unrepr <*> unrepr++-- | Returns a position resulting from sliding the original position+-- along the sequence by a specified offset. A positive offset will+-- move the position away from the 5\' end of the forward stand of the+-- sequence regardless of the strand of the position itself. Thus,+-- +-- > slide (revCompl pos) off == revCompl (slide pos off)+slide :: Pos -> Offset -> Pos+slide (Pos off str) doff = Pos (off + doff) str++-- | Extract 'Just' the item at a specific sequence position, or+-- 'Nothing' if the position lies outside the bounds of the sequence.+atPos :: (SeqLike s) => s -> Pos -> Maybe Char+atPos sequ (Pos off str) = liftM (stranded str) . ntAt sequ $ off++{-# SPECIALIZE atPos :: String -> Pos -> Maybe Char #-}+{-# SPECIALIZE atPos :: BS.ByteString -> Pos -> Maybe Char #-}
+ src/Bio/SeqLoc/SeqLike.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE FlexibleInstances #-}++module Bio.SeqLoc.SeqLike+ ( SeqLike(..)+ )+ where ++import Prelude hiding (length, concat)++import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as LBS+import qualified Data.List as L+import Data.Maybe++class SeqLike s where+ -- | Length of sequence data+ length :: (Integral n) => s -> n+ + -- | 'Just' the nucleotide at a specified sequence data offset,+ -- given in 0-based coordinates, or 'Nothing' if the offset is+ -- beyond the bounds of the data+ ntAt :: (Integral n) => s -> n -> Maybe Char+ + -- | 'Just' the nucleotides in subsequence of the sequence data, or+ -- 'Nothing' if the region extends beyond the bounds of the+ -- sequence.+ subseq :: (Integral n, Integral m) + => n -- ^ Starting position in 0-based coordinates+ -> m -- ^ Length+ -> s -- ^ Sequence data+ -> Maybe s+ + -- | Nucleotides in a subsequence of the sequence data, padded with+ -- @N@ when the region extends beyond the bounds of the sequence.+ subseqPad :: (Integral n, Integral m)+ => n -- ^ Starting position in 0-based coordinates+ -> m -- ^ Length+ -> s -- ^ Sequence data+ -> s+ + concat :: [s] -> s++instance SeqLike [Char] where+ length = L.genericLength+ ntAt l p | p < 0 = Nothing+ | otherwise = listToMaybe . drop (fromIntegral p) $ l+ subseq = listSubseq+ subseqPad = listSubseqPad+ concat = L.concat+ +listSubseq :: (Integral n, Integral m) => n -> m -> [Char] -> Maybe [Char]+listSubseq start len l | start < 0 = Nothing+ | otherwise = case take (fromIntegral len) . drop (fromIntegral start) $ l of + ss | L.genericLength ss == len -> Just ss+ | otherwise -> Nothing++listSubseqPad :: (Integral n, Integral m) => n -> m -> [Char] -> [Char]+listSubseqPad istart ilen sequ+ | start + len <= 0 = replicate len 'N'+ | start < 0 = replicate (negate start) 'N' ++ takePadded (len + start) sequ+ | otherwise = takePadded len . drop start $ sequ+ where takePadded sublen subsequ = case take sublen subsequ of+ subsubsequ | length subsubsequ == sublen -> subsubsequ+ | otherwise -> subsubsequ ++ replicate (sublen - length subsubsequ) 'N'+ start = fromIntegral istart+ len = fromIntegral ilen++instance SeqLike BS.ByteString where + length = fromIntegral . BS.length+ ntAt sequ ipos | pos >= 0 && pos < BS.length sequ = Just $ BS.index sequ pos+ | otherwise = Nothing+ where pos = fromIntegral ipos+ subseq = bsSubseq+ subseqPad = bsSubseqPad+ concat = BS.concat+ +bsSubseq :: (Integral n, Integral m) => n -> m -> BS.ByteString -> Maybe BS.ByteString+bsSubseq istart ilen sequ+ | start < 0 || start + len > BS.length sequ = Nothing+ | otherwise = Just . BS.take len . BS.drop start $ sequ+ where start = fromIntegral istart+ len = fromIntegral ilen++bsSubseqPad :: (Integral n, Integral m) => n -> m -> BS.ByteString -> BS.ByteString +bsSubseqPad istart ilen sequ+ | start + len <= 0 = BS.replicate len 'N'+ | start >= BS.length sequ = BS.replicate len 'N'+ | start < 0 = BS.replicate (negate start) 'N' `BS.append` takePadded (len + start) sequ+ | otherwise = takePadded len $ BS.drop start sequ+ where takePadded sublen subsequ+ | sublen <= BS.length subsequ = BS.take sublen subsequ+ | otherwise = subsequ `BS.append` BS.replicate (sublen - BS.length subsequ) 'N'+ start = fromIntegral istart+ len = fromIntegral ilen++instance SeqLike LBS.ByteString where+ length = fromIntegral . LBS.length+ ntAt = lbsNtAt+ subseq = lbsSubseq+ subseqPad = lbsSubseqPad+ concat = LBS.concat+ +lbsNtAt :: (Integral n) => LBS.ByteString -> n -> Maybe Char+lbsNtAt sequ pos | pos < 0 = Nothing+ | otherwise = case LBS.drop (fromIntegral pos) sequ of+ trimmed | LBS.null trimmed -> Nothing+ | otherwise -> Just . LBS.head $ trimmed++lbsSubseq :: (Integral n, Integral m) => n -> m -> LBS.ByteString -> Maybe LBS.ByteString+lbsSubseq istart ilen sequ+ | start < 0 = Nothing+ | otherwise = case LBS.take len . LBS.drop start $ sequ of+ subsequ | LBS.length subsequ < len -> Nothing+ | otherwise -> Just subsequ+ where start = fromIntegral istart+ len = fromIntegral ilen+ +lbsSubseqPad :: (Integral n, Integral m) => n -> m -> LBS.ByteString -> LBS.ByteString+lbsSubseqPad istart ilen sequ+ | start + len <= 0 = LBS.replicate len 'N'+ | start < 0 = LBS.replicate (negate start) 'N' `LBS.append` takePadded (len + start) sequ+ | otherwise = takePadded len $ LBS.drop start sequ+ where takePadded sublen subsequ = case LBS.take sublen subsequ of+ subsubsequ | LBS.length subsubsequ == sublen -> subsubsequ+ | otherwise -> subsubsequ `LBS.append` LBS.replicate (sublen - LBS.length subsubsequ) 'N'+ start = fromIntegral istart+ len = fromIntegral ilen
+ src/Bio/SeqLoc/SpliceLocation.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE TypeFamilies, FlexibleContexts, OverloadedStrings #-}++{-| Data type for a more general sequence location consiting of+disjoint ranges of positions on a sequence.++Throughout, /sequence position/ refers to a 'Pos.Pos' which includes a+strand, as opposed to an /offset/, which refers to a 'Pos.Offset' with+no strand.+ -}++module Bio.SeqLoc.SpliceLocation ( + -- * Sequence locations+ SpliceLoc+ , fromContigs+ , locOutof, locInto+ , mergeContigs, mergeAdjContigs+ ) where ++import Prelude hiding (length)++import Control.Applicative+import Control.Arrow ((***))+import Control.Monad+import qualified Data.ByteString.Char8 as BS+import Data.List (foldl')+import Data.Maybe++import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr+import Bio.SeqLoc.Location+import qualified Bio.SeqLoc.Position as Pos+import Bio.SeqLoc.Strand+import qualified Bio.SeqLoc.SeqLike as SeqLike++-- | General (disjoint) sequence region consisting of a concatenated+-- set of one or more contiguous regions.+data SpliceLoc = SpliceLocLast { contig :: !ContigLoc } + | SpliceLocPrev { contig :: !ContigLoc+ , _next :: !SpliceLoc+ }+ deriving (Eq, Ord, Show)++singleton :: ContigLoc -> SpliceLoc+singleton = SpliceLocLast++consContig :: ContigLoc -> SpliceLoc -> Maybe SpliceLoc+consContig c sploc | goodJunction c (contig sploc) = Just $! SpliceLocPrev c sploc+ | otherwise = Nothing+ +goodJunction :: ContigLoc -> ContigLoc -> Bool+goodJunction c5 c3 = sameStrand && inOrder+ where sameStrand = strand c5 == strand c3+ c5end = snd . bounds $ c5+ c3start = fst . bounds $ c3+ inOrder = case strand c5 of+ Fwd -> c5end < c3start+ RevCompl -> c5end > c3start++fromContigs :: [ContigLoc] -> Maybe SpliceLoc+fromContigs [] = Nothing+fromContigs [l] = Just $! singleton l+fromContigs (l:rest) = fromContigs rest >>= consContig l++tails :: SpliceLoc -> [SpliceLoc]+tails sll@(SpliceLocLast _) = [sll]+tails slp@(SpliceLocPrev _ n) = slp : (tails n)++instance Stranded SpliceLoc where+ revCompl sll = fromMaybe badRevCompl . foldl' addprev newlast $ slrest+ where (sl0:slrest) = tails sll+ newlast = Just $! singleton . revCompl . contig $ sl0+ addprev slnew slold = slnew >>= consContig (revCompl . contig $ slold)+ badRevCompl = error $ "Bad junction doing reverse complement on " ++ (BS.unpack . repr) sll++instance LocRepr SpliceLoc where+ repr = BS.intercalate (BS.singleton ';') . map repr . contigs+ unrepr = (fromContigs <$> scan) >>= maybe (fail "bad contig order") return+ where scan = liftA2 (:) unrepr ((ZP.string ";" *> scan) <|> pure [])++instance Location SpliceLoc where+ strand = strand . contig+ length = foldl' (\len c -> len + length c) 0 . contigs+ bounds = (minimum *** maximum) . unzip . map bounds . contigs + seqData sequ = liftM SeqLike.concat . mapM (seqData sequ) . contigs+ seqDataPad sequ = SeqLike.concat . map (seqDataPad sequ) . contigs+ posInto pos = posIntoContigs pos . contigs+ posOutof pos = posOutofContigs pos . contigs+ startPos = startPos . firstContig+ endPos = endPos . lastContig+ clocInto = slocClocInto+ clocOutof = slocClocOutof+ extend = slocExtend+ offsetWithin off = or . map (offsetWithin off) . contigs+ posWithin pos = or . map (posWithin pos) . contigs+ contigOverlaps c = any (contigOverlaps c ) . contigs+ toContigs = contigs++locOutof :: (Location l) => SpliceLoc -> l -> Maybe SpliceLoc+locOutof sploc outer = mapM (flip clocOutof outer) (toContigs sploc) >>= + fromContigs . concat . map toContigs++locInto :: (Location l) => SpliceLoc -> l -> Maybe SpliceLoc+locInto sploc outer = mapM (flip clocInto outer) (toContigs sploc) >>=+ fromContigs . mergeContigs . concat . map toContigs++mergeContigs :: [ContigLoc] -> [ContigLoc]+mergeContigs [] = []+mergeContigs [clast] = [clast]+mergeContigs (cprev:rest@(_:_)) = case mergeContigs rest of+ [] -> error $ "mergeContigs: empty rest"+ mergerest@(cnext:afternext) -> case mergeAdjContigs cprev cnext of+ Just cmerge -> cmerge : afternext+ Nothing -> cprev : mergerest++mergeAdjContigs :: ContigLoc -> ContigLoc -> Maybe ContigLoc+mergeAdjContigs clocprev clocnext + | startPos clocnext == endPos (extend (0, 1) clocprev)+ = Just $! fromPosLen (startPos clocprev) (length clocprev + length clocnext)+ | otherwise = Nothing++contigs :: SpliceLoc -> [ContigLoc]+contigs (SpliceLocLast c) = [c]+contigs (SpliceLocPrev c n) = c : contigs n++firstContig :: SpliceLoc -> ContigLoc+firstContig = contig++lastContig :: SpliceLoc -> ContigLoc+lastContig (SpliceLocLast c) = c+lastContig (SpliceLocPrev _ n) = lastContig n++--reloffsets :: SpliceLoc -> [Pos.Offset]+--reloffsets = init . scanl (\off c -> off + length c) 0 . contigs++contigsAndOffsets :: SpliceLoc -> [(ContigLoc, Pos.Offset)]+contigsAndOffsets = go 0+ where go off (SpliceLocLast c) = [(c, off)]+ go off (SpliceLocPrev c n) = (c, off) : (go (off + length c) n)++posIntoContigs :: Pos.Pos -> [ContigLoc] -> Maybe Pos.Pos+posIntoContigs pos = go 0+ where go _ [] = Nothing+ go dlen (c:rest) = maybe onRest onContig . posInto pos $ c+ where onContig pin = Just $! pin `Pos.slide` dlen+ onRest = go (dlen + length c) rest++posOutofContigs :: Pos.Pos -> [ContigLoc] -> Maybe Pos.Pos+posOutofContigs _ [] = Nothing+posOutofContigs pos (c:rest) = maybe onRest onContig . posOutof pos $ c+ where onRest = posOutofContigs (Pos.slide pos . negate . length $ c) rest+ onContig = Just++slocClocInto :: ContigLoc -> SpliceLoc -> Maybe ContigLoc+slocClocInto subcloc = listToMaybe . catMaybes . map into . contigsAndOffsets+ where into (cloc, off) = liftM (slide off) . clocInto subcloc $ cloc++slocClocOutof :: ContigLoc -> SpliceLoc -> Maybe SpliceLoc+slocClocOutof cloc+ = liftM (stranded (strand cloc) . fromContigsErr) . regionOutof (offset5 cloc) (length cloc) . contigs+ where fromContigsErr ctgs = fromMaybe badContigs $! fromContigs ctgs+ where badContigs = error . unwords $ + [ "bad contig order in slocClocOutof" ] +++ map (BS.unpack . repr) ctgs+ + +regionOutof :: Pos.Offset -> Pos.Offset -> [ContigLoc] -> Maybe [ContigLoc]+regionOutof _ _ [] = Nothing+regionOutof pos5 len (cloc0:rest)+ | pos5 < 0 = Nothing+ | len < 0 = error $ "Bio.SeqLoc.SpliceLocation.regionOutof: input, " ++ show (pos5, len, cloc0)+ | pos5 >= len0 = regionOutof (pos5 - len0) len rest+ | (pos5 + len <= len0) = let subcloc = fromPosLen (Pos.Pos pos5 Fwd) len+ in case clocOutof subcloc cloc0 of+ Just out0 -> Just [out0]+ Nothing -> error $ "regionOutof: final bounds failure, " ++ (BS.unpack . repr) subcloc+ | otherwise = let outlen0 = len0 - pos5+ subcloc = fromPosLen (Pos.Pos pos5 Fwd) outlen0+ in case clocOutof subcloc cloc0 of+ Just out0 -> liftM (out0 :) . regionOutof 0 (len - outlen0) $ rest+ Nothing -> error $ "regionOutof: internal bounds failure, " ++ (BS.unpack . repr) subcloc+ where len0 = length cloc0++slocExtend :: (Pos.Offset, Pos.Offset) -> SpliceLoc -> SpliceLoc+slocExtend (ext5, ext3) sloc = extended3 { contig = extend (ext5, 0) . contig $ extended3 }+ where extend3 (SpliceLocPrev c n) = SpliceLocPrev c (extend3 n) + extend3 (SpliceLocLast c) = SpliceLocLast $ extend (0, ext3) c+ extended3 = extend3 sloc
+ src/Bio/SeqLoc/Strand.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings, TypeSynonymInstances #-}++{-| Utilities for manipulating nucleotide sequences and locations on+nucleotide sequences that occur on a forward or a reverse-complement+strand.++-}++module Bio.SeqLoc.Strand ( Strand(..)+ , compl+ , Stranded(..), stranded+ )+ where ++import Control.Applicative+import Data.ByteString.Internal (c2w, w2c)+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as LBS+import Data.Word (Word8)++import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr++-- | Complement of a nucleotide character, swap A/T and G/C preserving+-- case and leave all other characters unchanged.+compl :: Char -> Char+compl 'a' = 't'+compl 'c' = 'g'+compl 'g' = 'c'+compl 't' = 'a'+compl 'A' = 'T'+compl 'C' = 'G'+compl 'G' = 'C'+compl 'T' = 'A'+compl ch = ch++-- | Sequence strand+data Strand = Fwd | RevCompl deriving (Eq, Ord, Show, Read, Bounded, Enum)++instance LocRepr Strand where+ repr Fwd = BS.pack "(+)"+ repr RevCompl = BS.pack "(-)"+ unrepr = (ZP.string "(+)" *> return Fwd) <|>+ (ZP.string "(-)" *> return RevCompl)+ +-- | A nucleotide sequence or location on a nucleotide sequence that+-- lies on a specific strand and has an orientation.+class Stranded s where+ revCompl :: s -> s++-- | Convert the orientation of a 'Stranded' thing based on a+-- specified 'Strand'+stranded :: (Stranded s) => Strand -> s -> s+stranded Fwd = id+stranded RevCompl = revCompl++instance Stranded Strand where+ revCompl Fwd = RevCompl+ revCompl RevCompl = Fwd++instance Stranded Char where+ revCompl = compl++instance Stranded Word8 where+ revCompl = c2w . compl . w2c++instance Stranded String where+ revCompl = reverse . map compl++instance Stranded LBS.ByteString where+ revCompl = LBS.reverse . LBS.map compl++instance Stranded BS.ByteString where+ revCompl = BS.reverse . BS.map compl+
+ src/Bio/SeqLoc/Transcript.hs view
@@ -0,0 +1,116 @@+module Bio.SeqLoc.Transcript+ (+ -- * Type for splice junctions+ Junction (..)+ , fromDonorAcceptor, donor, acceptor+ , junctions+ -- * Representation of transcript+ , Transcript(..), utr5, utr3+ , cdsLocation+ , sortContigs+ )+ where ++import Control.Applicative+import Control.Monad+import qualified Data.ByteString.Char8 as BS+import Data.List+import Data.Ord++import qualified Data.Attoparsec.Zepto as ZP++import Bio.SeqLoc.LocRepr+import qualified Bio.SeqLoc.Location as Loc+import Bio.SeqLoc.OnSeq+import qualified Bio.SeqLoc.Position as Pos+import qualified Bio.SeqLoc.SpliceLocation as SpLoc+import Bio.SeqLoc.Strand++-- | Splice junctions, which are isomorphic to the introns they span,+-- but which support other biologically relevant constructors and+-- accessors.+newtype Junction = Junction { intron :: Loc.ContigLoc } deriving (Show)++slash :: BS.ByteString+slash = BS.pack "/"++instance LocRepr Junction where+ repr j = BS.concat [ repr . donor $ j, slash, repr . acceptor $ j ]+ unrepr = fromDonorAcceptor <$> unrepr <*> (ZP.string slash *> unrepr) ++-- | Create a splice junction from a donor position (the last position+-- in the 5' exon) and the acceptor position (the first position in+-- the 3' exon).+fromDonorAcceptor :: Pos.Pos -> Pos.Pos -> Junction+fromDonorAcceptor d a = let len = 1 + abs (Pos.offset a - Pos.offset d)+ in case Pos.strand d of+ Fwd -> Junction $! Loc.fromPosLen (Pos.slide d 1) len+ RevCompl -> Junction $! Loc.fromPosLen (Pos.slide d (-1)) len++-- | Donor position, i.e., the last position in the 5' exon around a+-- junction.+donor :: Junction -> Pos.Pos+donor = Loc.startPos . Loc.extend (1, 0) . intron++-- | Acceptor position, i.e., the first position in the 3' exon around+-- a junction.+acceptor :: Junction -> Pos.Pos+acceptor = Loc.endPos . Loc.extend (0, 1) . intron++-- | List of splice junctions from a spliced location, in order.+junctions :: SpLoc.SpliceLoc -> [Junction]+junctions sploc = zipWith junction contigs (drop 1 contigs)+ where contigs = Loc.toContigs sploc+ junction c5 c3 = let p5 = Loc.endPos . Loc.extend (0, 1) $ c5+ p3 = Loc.startPos . Loc.extend (1, 0) $ c3+ len = 1 + abs (Pos.offset p3 - Pos.offset p5)+ in Junction $ Loc.fromPosLen p5 len++++-- | Representation of a genomic transcript, with a gene and a+-- transcript identifier, along with the genomic location of the+-- processed transcript and an optional coding sequence on that+-- transcript.+data Transcript = Transcript { geneId :: !SeqName -- ^ Gene or locus name for a collection of transcripts+ , trxId :: !SeqName -- ^ Specific transcript identifier+ , location :: !SpliceSeqLoc -- ^ Sequence location of processed transcript+ , cds :: !(Maybe Loc.ContigLoc) -- ^ Location of CDS on the transcript+ } deriving (Show)+ +-- | 'Just' the location of the 5' UTR on the transcript, or 'Nothing'+-- if there is no 'cds' on the transcript or if the 'cds' location+-- begins at the first nucleotide of the transcript--if a region is+-- returned it will have positive length.+utr5 :: Transcript -> Maybe Loc.ContigLoc+utr5 trx = cds trx >>= utr5loc+ where utr5loc cdsloc = case Loc.startPos cdsloc of+ (Pos.Pos startoff Fwd) | startoff > 0 -> Just $! Loc.fromBoundsStrand 0 (startoff - 1) Fwd+ _ -> Nothing+ +-- | 'Just' the location of the 3' UTR on the transcript, or 'Nothing'+-- if there is no 'cds' on the transcript or if the 'cds' location+-- ends at the last nucleotide of the transcript--if a region is+-- returned it will have positive length.+utr3 :: Transcript -> Maybe Loc.ContigLoc+utr3 trx = cds trx >>= utr3loc+ where utr3loc cdsloc = case Loc.endPos cdsloc of+ (Pos.Pos endoff Fwd) | endoff < trxlast -> Just $! Loc.fromBoundsStrand (endoff + 1) trxlast Fwd+ _ -> Nothing+ trxlast = snd . Loc.bounds . unOnSeq . location $ trx++-- | Genomic location of CDS within the transcript+cdsLocation :: Transcript -> Maybe SpliceSeqLoc+cdsLocation trx = cds trx >>= liftM (OnSeq name) . flip Loc.clocOutof loc+ where (OnSeq name loc) = location trx++-- | 'Just' the input contigs sorted in stranded order, when all lie+-- on the same strand, or 'Nothing' if they are not all on the same+-- strand.+sortContigs :: [Loc.ContigLoc] -> Maybe [Loc.ContigLoc]+sortContigs [] = Nothing+sortContigs cs@(c0:_)= liftM sortStrand contigStrand+ where contigStrand | all ((== Loc.strand c0) . Loc.strand) cs = Just . Loc.strand $ c0+ | otherwise = Nothing+ sortStrand Fwd = sortBy (comparing Loc.offset5) cs+ sortStrand RevCompl = sortBy (comparing (negate . Loc.offset5)) cs
+ test/TestMain.hs view
@@ -0,0 +1,392 @@+{-# LANGUAGE ExistentialQuantification #-}+module Main+ where++import Control.Monad+import qualified Data.ByteString as BSW+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as LBS+import Data.ByteString.Internal (c2w, w2c)+import Data.Char+import Data.Either+import Data.Ix (inRange)+import Data.List+import Data.Maybe+import System.Random++import Test.QuickCheck++import Bio.SeqLoc.LocRepr+import qualified Bio.SeqLoc.Location as Loc+import Bio.SeqLoc.OnSeq+import qualified Bio.SeqLoc.Position as Pos+import qualified Bio.SeqLoc.SpliceLocation as SpLoc+import Bio.SeqLoc.Strand+import qualified Bio.SeqLoc.SeqLike as SeqLike++main :: IO ()+main = mapM_ runTest tests++tests :: [Test]+tests = [ T "Strand revCompl" test_Strand_revCompl + , T "Char revCompl" property_Char_revCompl+ , T "ByteString revCompl" property_ByteString_revCompl+ , T "Sequence revCompl" property_Sequence_revCompl++ , T "Pos revCompl" test_Pos_revCompl+ , T "Pos atPos" property_Pos_atPos+ , T "Pos atPos2" property_Pos_atPos2+ , T "Pos repr" test_Pos_repr+ + , T "Contig revCompl" test_Contig_RevCompl+ , T "Contig pos into/outof inverse" property_ContigIntoOutof+ , T "Contig pos outof/into inverse" property_ContigOutofInto+ , T "Contig loc into/outof inverse" property_ContigLocIntoOutof+ , T "Contig loc outof/into inverse" property_ContigLocOutofInto+ , T "Contig into based on bounds" test_Contig_IntoBounds+ , T "Contig outof based on bounds" test_Contig_OutofBounds+ , T "Contig seqData" property_Contig_seqData+ , T "Contig seqDataPadded" property_Contig_seqDataPadded+ , T "Contig seqData2" property_Contig_seqData2+ , T "Contig extend/revCompl" property_Contig_extendRevCompl+ , T "Contig fromStartEnd" property_Contig_fromStartEnd+ , T "Contig fromBoundsStrand" property_Contig_fromBoundsStrand+ , T "Contig overlaps" property_Contig_overlaps + , T "Contig repr" test_Contig_repr+ + , T "Loc revCompl" test_Loc_RevCompl+ , T "Loc pos into/outof inverse" property_LocIntoOutof+ , T "Loc pos outof/into inverse" property_LocOutofInto+ , T "Loc outof based on bounds" test_Loc_OutofBounds+ , T "Loc loc outof/into inverse" property_LocCLocOutofInto+ , T "Loc outof association" property_LocOutofAssoc+ , T "Loc locOutof" property_SpLocOutof+ , T "Loc locOutof valid" property_SpLocOutofGood+ , T "Loc within" property_Loc_Within+ , T "Loc seqData" property_Loc_seqData+ , T "Loc seqDataPadded" property_Loc_seqDataPadded+ , T "SpLoc seqData2" property_SpLoc_seqData2+ , T "SpLoc repr" test_SpLoc_repr+ , T "SpLoc termini/revCompl" property_SpLoc_terminiRevCompl+ , T "SpLoc termini/extend" property_SpLoc_terminiExtend+ ]+++-- Bio.BioSeq.Stranded++genNtByteString :: Int -> Gen BS.ByteString+genNtByteString = liftM BS.pack . flip replicateM (elements "ACGT")++genName :: Gen SeqName+genName = liftM (SeqName . BS.pack) $ genNameLength >>= flip replicateM genNameChar+ where genNameLength = choose (1, 20)+ genNameChar = elements $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "-_"++instance Arbitrary SeqName where+ arbitrary = genName++test_revCompl :: (Eq s, Stranded s) => s -> Bool+test_revCompl s = (revCompl . revCompl) s == s++test_repr :: (LocRepr l, Eq l) => l -> Bool+test_repr l = (unreprMaybe . repr $ l) == Just l++test_Strand_revCompl :: Strand -> Bool+test_Strand_revCompl = test_revCompl++property_Char_revCompl :: Property+property_Char_revCompl = forAll (elements "ACGTacgtnN") test_revCompl++property_ByteString_revCompl :: Property+property_ByteString_revCompl = forAll (sized genNtByteString) test_revCompl++property_Sequence_revCompl :: Property+property_Sequence_revCompl+ = forAll arbitrary $ \name ->+ let mkSeq = OnSeq name+ in forAll (sized genNtByteString) $ \sequ ->+ (unOnSeq . revCompl . mkSeq) sequ == revCompl sequ++-- Bio.BioSeq.Position++test_Pos_revCompl :: Pos.Pos -> Bool+test_Pos_revCompl = test_revCompl++property_Pos_atPos :: Pos.Pos -> Property+property_Pos_atPos pos+ = forAll genPositiveOffset $ \seqlen ->+ forAll (genNtByteString $ fromIntegral seqlen) $ \sequ ->+ let actual = Pos.atPos sequ pos+ in if and [ Pos.offset pos >= 0, Pos.offset pos < seqlen ]+ then let fwdNt = BS.index sequ (fromIntegral . Pos.offset $ pos)+ in case Pos.strand pos of+ Fwd -> actual == Just fwdNt+ RevCompl -> actual == Just (compl $ fwdNt)+ else actual == Nothing++property_Pos_atPos2 :: Pos.Pos -> Property+property_Pos_atPos2 pos+ = forAll genPositiveOffset $ \seqlen ->+ forAll (genNtByteString $ fromIntegral seqlen) $ \sequ -> + and [ Pos.atPos sequ pos == Pos.atPos (LBS.fromChunks [sequ]) pos+ , Pos.atPos sequ pos == Pos.atPos (BS.unpack sequ) pos+ ]++test_Pos_repr :: Pos.Pos -> Bool+test_Pos_repr = test_repr++-- Bio.BioSeq.Location++instance Arbitrary Strand where+ arbitrary = elements [Fwd, RevCompl]++instance Arbitrary Pos.Pos where+ arbitrary = liftM2 Pos.Pos genOffset arbitrary++instance Arbitrary Loc.ContigLoc where+ arbitrary = liftM2 Loc.fromPosLen arbitrary genPositiveOffset++test_Contig_RevCompl :: Loc.ContigLoc -> Bool+test_Contig_RevCompl = test_revCompl++property_ContigIntoOutof :: Loc.ContigLoc -> Pos.Pos -> Property+property_ContigIntoOutof contig pos+ = let !mInpos = Loc.posInto pos contig+ !mOutpos = mInpos >>= flip Loc.posOutof contig + in (isJust mInpos) ==> mOutpos == (Just pos)++property_ContigOutofInto :: Loc.ContigLoc -> Pos.Pos -> Property+property_ContigOutofInto contig pos+ = let !mOutpos = Loc.posOutof pos contig+ !mInpos = mOutpos >>= flip Loc.posInto contig+ in (isJust mOutpos) ==> mInpos == (Just pos)++property_ContigLocIntoOutof :: Loc.ContigLoc -> Loc.ContigLoc -> Property+property_ContigLocIntoOutof subcloc supercloc+ = let !mIncloc = Loc.clocInto subcloc supercloc+ !mOutcloc = mIncloc >>= flip Loc.clocOutof supercloc+ in (isJust mIncloc) ==> mOutcloc == (Just subcloc)++property_ContigLocOutofInto :: Loc.ContigLoc -> Loc.ContigLoc -> Property+property_ContigLocOutofInto subcloc supercloc+ = let !mOutcloc = Loc.clocOutof subcloc supercloc+ !mIncloc = mOutcloc >>= flip Loc.clocInto supercloc+ in (isJust mOutcloc) ==> mIncloc == (Just subcloc)++test_Contig_IntoBounds :: Loc.ContigLoc -> Pos.Pos -> Bool+test_Contig_IntoBounds contig pos+ = let !mInpos = Loc.posInto pos contig+ !offset = Pos.offset pos+ !(cstart, cend) = Loc.bounds contig+ in (isJust mInpos) == (offset >= cstart && offset <= cend)++test_Contig_OutofBounds :: Loc.ContigLoc -> Pos.Pos -> Bool+test_Contig_OutofBounds contig pos+ = let !offset = Pos.offset pos+ in (isJust $ Loc.posOutof pos contig) == (offset >= 0 && offset < Loc.length contig)++property_Contig_seqData :: Loc.ContigLoc -> Property+property_Contig_seqData contig+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ let seqData = Loc.seqData sequ contig+ padded = Loc.seqDataPad sequ contig+ in case seqData of+ (Just subsequ) -> and [ padded == subsequ, 'N' `BS.notElem` padded ]+ Nothing -> 'N' `BS.elem` padded++property_Contig_seqDataPadded :: Loc.ContigLoc -> Property+property_Contig_seqDataPadded contig+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ (BS.pack $ map (fromMaybe 'N' . Pos.atPos sequ) contigPoses) == Loc.seqDataPad sequ contig+ where contigPoses = mapMaybe (flip Loc.posOutof contig . flip Pos.Pos Fwd) [0..(Loc.length contig - 1)]++property_Contig_seqData2 :: Loc.ContigLoc -> Property+property_Contig_seqData2 contig+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ let toLBS = LBS.fromChunks . (: [])+ fromLBS = BS.concat . LBS.toChunks+ in and [ Loc.seqData sequ contig == liftM fromLBS (Loc.seqData (toLBS sequ) contig)+ , Loc.seqDataPad sequ contig == fromLBS (Loc.seqDataPad (toLBS sequ) contig) + , Loc.seqData sequ contig == liftM BS.pack (Loc.seqData (BS.unpack sequ) contig)+ , Loc.seqDataPad sequ contig == BS.pack (Loc.seqDataPad (BS.unpack sequ) contig)+ ]++property_Contig_extendRevCompl :: Loc.ContigLoc -> Property+property_Contig_extendRevCompl contig+ = forAll (liftM2 (,) genNonNegOffset genNonNegOffset) $ \(ext5, ext3) ->+ (revCompl $ Loc.extend (ext5, ext3) contig) == (Loc.extend (ext3, ext5) $ revCompl contig)++property_Contig_fromStartEnd :: Loc.ContigLoc -> Property+property_Contig_fromStartEnd contig+ = (Loc.length contig > 1) ==>+ (Loc.fromStartEnd (Pos.offset $ Loc.startPos contig) (Pos.offset $ Loc.endPos contig)) == contig++property_Contig_fromBoundsStrand :: Loc.ContigLoc -> Property+property_Contig_fromBoundsStrand contig+ = (Loc.length contig > 1) ==>+ (Loc.fromBoundsStrand (fst . Loc.bounds $ contig) (snd . Loc.bounds $ contig) (Loc.strand contig)) == contig++property_Contig_overlaps :: Loc.ContigLoc -> Loc.ContigLoc -> Bool+property_Contig_overlaps cloc1 cloc2+ = (cloc1 `Loc.contigOverlaps` cloc2) ==+ and [ Loc.strand cloc1 == Loc.strand cloc2+ , or [ isJust . Loc.posInto (Loc.startPos cloc1) $ cloc2+ , isJust . Loc.posInto (Loc.startPos cloc2) $ cloc1+ , isJust . Loc.posInto (Loc.endPos cloc1) $ cloc2+ , isJust . Loc.posInto (Loc.endPos cloc2) $ cloc1+ ]+ ]++test_Contig_repr :: Loc.ContigLoc -> Bool+test_Contig_repr = test_repr++-- Bio.BioSeq.Location++genInvertibleLoc :: Gen SpLoc.SpliceLoc+genInvertibleLoc = sized $ \sz -> do ncontigs <- choose (1, sz + 1)+ fwdloc <- liftM (fromJust . SpLoc.fromContigs) + $ genContigs ncontigs+ rc <- arbitrary+ if rc then return $ revCompl fwdloc else return fwdloc+ where genContigs = liftM (reverse . foldl' intervalsToContigs []) . genIntervals+ genIntervals nints = replicateM nints $ liftM2 (,) genPositiveOffset genPositiveOffset+ intervalsToContigs [] (init5, len) = [Loc.fromPosLen (Pos.Pos init5 Fwd) len]+ intervalsToContigs prevs@(prev:_) (nextoffset, nextlen)+ = let !prevend = Loc.offset5 prev + Loc.length prev+ in (Loc.fromPosLen (Pos.Pos (prevend + nextoffset) Fwd) nextlen):prevs++instance Arbitrary SpLoc.SpliceLoc where+ arbitrary = genInvertibleLoc++test_Loc_RevCompl :: SpLoc.SpliceLoc -> Bool+test_Loc_RevCompl = test_revCompl++property_LocIntoOutof :: SpLoc.SpliceLoc -> Pos.Pos -> Property+property_LocIntoOutof loc pos+ = let !mInpos = Loc.posInto pos loc+ !mOutpos = mInpos >>= flip Loc.posOutof loc+ in (isJust mInpos) ==> mOutpos == (Just pos)++property_LocOutofInto :: Pos.Pos -> Property+property_LocOutofInto pos+ = forAll genInvertibleLoc $ \loc ->+ let !mOutpos = Loc.posOutof pos loc+ !mInpos = mOutpos >>= flip Loc.posInto loc+ in (isJust mOutpos) ==> mInpos == (Just pos)++test_Loc_OutofBounds :: SpLoc.SpliceLoc -> Pos.Pos -> Bool+test_Loc_OutofBounds loc pos+ = let !offset = Pos.offset pos+ in (isJust $ Loc.posOutof pos loc) == (offset >= 0 && offset < Loc.length loc)++property_LocCLocOutofInto :: Loc.ContigLoc -> Property+property_LocCLocOutofInto cloc+ = forAll genInvertibleLoc $ \loc ->+ let !mOutloc = Loc.clocOutof cloc loc+ !mInloc = mOutloc >>= mapM (flip Loc.clocInto loc) . Loc.toContigs >>= return . fromJust . SpLoc.fromContigs+ in (isJust mOutloc) ==> and [ liftM Loc.length mInloc == Just (Loc.length cloc)+ , liftM Loc.bounds mInloc == Just (Loc.bounds cloc)+ ]++property_LocOutofAssoc :: SpLoc.SpliceLoc -> Loc.ContigLoc -> Pos.Pos -> Property+property_LocOutofAssoc loc cloc pos+ = let !mOutloc = Loc.clocOutof cloc loc+ !mOutpos = mOutloc >>= \outloc -> Loc.posOutof pos outloc+ in (isJust mOutpos) ==> mOutpos == (Loc.posOutof pos cloc >>= \outpos -> Loc.posOutof outpos loc)++property_SpLocOutof :: SpLoc.SpliceLoc -> SpLoc.SpliceLoc -> Bool+property_SpLocOutof subloc outerloc =+ let !mOutofContigs = liftM Loc.toContigs $ SpLoc.locOutof subloc outerloc+ !mContigsOutof = liftM (concat . map Loc.toContigs) $+ mapM (flip Loc.clocOutof outerloc) $+ Loc.toContigs subloc+ in mOutofContigs == mContigsOutof++property_SpLocOutofGood :: SpLoc.SpliceLoc -> SpLoc.SpliceLoc -> Property+property_SpLocOutofGood subloc outerloc =+ let !mOutofContigs = liftM Loc.toContigs $ SpLoc.locOutof subloc outerloc+ !mContigsOutof = liftM (concat . map Loc.toContigs) $+ mapM (flip Loc.clocOutof outerloc) $+ Loc.toContigs subloc+ in (isJust mOutofContigs) ==> mOutofContigs == mContigsOutof++property_Loc_seqData :: SpLoc.SpliceLoc -> Property+property_Loc_seqData loc+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ let seqData = Loc.seqData sequ loc+ padded = Loc.seqDataPad sequ loc+ in case seqData of+ (Just subsequ) -> and [ padded == subsequ, 'N' `BS.notElem` padded ]+ Nothing -> 'N' `BS.elem` padded++property_Loc_seqDataPadded :: SpLoc.SpliceLoc -> Property+property_Loc_seqDataPadded loc+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ (BS.pack $ map (fromMaybe 'N' . Pos.atPos sequ) locPoses) == Loc.seqDataPad sequ loc+ where locPoses = mapMaybe (flip Loc.posOutof loc . flip Pos.Pos Fwd) [0..(Loc.length loc - 1)]++property_SpLoc_seqData2 :: SpLoc.SpliceLoc -> Property+property_SpLoc_seqData2 sploc+ = forAll (genNonNegOffset >>= genNtByteString . fromIntegral) $ \sequ ->+ let toLBS = LBS.fromChunks . (: [])+ fromLBS = BS.concat . LBS.toChunks+ in and [ Loc.seqData sequ sploc == liftM fromLBS (Loc.seqData (toLBS sequ) sploc)+ , Loc.seqDataPad sequ sploc == fromLBS (Loc.seqDataPad (toLBS sequ) sploc) + , Loc.seqData sequ sploc == liftM BS.pack (Loc.seqData (BS.unpack sequ) sploc)+ , Loc.seqDataPad sequ sploc == BS.pack (Loc.seqDataPad (BS.unpack sequ) sploc)+ ]++property_Loc_Within :: Pos.Pos -> Property+property_Loc_Within pos+ = forAll genInvertibleLoc $ \loc ->+ and [ (pos `Loc.posWithin` loc) == (maybe False ((/= RevCompl) . Pos.strand) $ Loc.posInto pos loc)+ , ((Pos.offset pos) `Loc.offsetWithin` loc) == (isJust . Loc.posInto pos $ loc)+ ]++test_SpLoc_repr :: SpLoc.SpliceLoc -> Bool+test_SpLoc_repr = test_repr++property_SpLoc_terminiRevCompl :: SpLoc.SpliceLoc -> Bool+property_SpLoc_terminiRevCompl loc+ = and [ revCompl (Loc.startPos loc) == Loc.endPos (revCompl loc)+ , revCompl (Loc.endPos loc) == Loc.startPos (revCompl loc)+ ]++property_SpLoc_terminiExtend :: SpLoc.SpliceLoc -> Property+property_SpLoc_terminiExtend loc + = forAll genNonNegOffset $ \ext5 ->+ forAll genNonNegOffset $ \ext3 ->+ let extloc = Loc.extend (ext5, ext3) loc+ strandSlide pos doff = case Pos.strand pos of+ Fwd -> Pos.slide pos doff+ RevCompl -> Pos.slide pos (negate doff)+ in and [ Loc.startPos extloc == strandSlide (Loc.startPos loc) (negate ext5)+ , Loc.endPos extloc == strandSlide (Loc.endPos loc) ext3+ ]++-- Utilities++data Test = forall t . Testable t => T String t++runTest :: Test -> IO ()+runTest (T name test) = do+ putStr $ name ++ replicate (40 - length name) '.' ++ " "+ quickCheckWith args test+ where args = stdArgs { maxDiscard = 100000 }++-- | Constrained position generators++genOffset :: Gen Pos.Offset+genOffset = do isneg <- arbitrary+ nnoff <- genNonNegOffset+ return $ (if isneg then negate else id) nnoff++genNonNegOffset :: Gen Pos.Offset+genNonNegOffset = liftM (subtract 1) genPositiveOffset++genPositiveOffset :: Gen Pos.Offset+genPositiveOffset = do scale <- chooseInteger (1, 10)+ liftM fromIntegral $ chooseInteger (1, 2^scale)+ where chooseInteger :: (Integer, Integer) -> Gen Integer+ chooseInteger = choose+