packages feed

biohazard 1.1.1 → 2.0

raw patch · 74 files changed

+7685/−9935 lines, 74 filesdep +bifunctorsdep +directorydep +streamingdep −asyncdep −vector-th-unboxPVP ok

version bump matches the API change (PVP)

Dependencies added: bifunctors, directory, streaming

Dependencies removed: async, vector-th-unbox

API changes (from Hackage documentation)

- Bio.Adna: [stats_more] :: DmgStats a -> a
- Bio.Adna: instance GHC.Base.Monoid a => GHC.Base.Monoid (Bio.Adna.DmgStats a)
- Bio.Adna: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Bio.Adna.DmgStats a)
- Bio.Adna: instance GHC.Show.Show a => GHC.Show.Show (Bio.Adna.DmgStats a)
- Bio.Bam.Fastq: parseFastq' :: Monad m => (Bytes -> BamRec -> BamRec) -> Enumeratee Bytes [BamRec] m a
- Bio.Bam.Header: GroupSorted :: BamSorting
- Bio.Bam.Header: noRefs :: Refs
- Bio.Bam.Header: parseBamMetaLine :: Parser (BamMeta -> BamMeta)
- Bio.Bam.Header: type Refs = Seq BamSQ
- Bio.Bam.Index: eneeBamRefseq :: Monad m => BamIndex b -> Refseq -> Enumeratee [BamRaw] [BamRaw] m a
- Bio.Bam.Index: eneeBamRegions :: Monad m => BamIndex b -> [Region] -> Enumeratee [BamRaw] [BamRaw] m a
- Bio.Bam.Index: eneeBamSubseq :: Monad m => BamIndex b -> Refseq -> Subsequence -> Enumeratee [BamRaw] [BamRaw] m a
- Bio.Bam.Index: eneeBamUnaligned :: Monad m => BamIndex b -> Enumeratee [BamRaw] [BamRaw] m a
- Bio.Bam.Pileup: Empty :: Heap
- Bio.Bam.Pileup: Node :: {-# UNPACK #-} !Int -> PrimBase -> Heap -> Heap -> Heap
- Bio.Bam.Pileup: PileM :: (forall r. (a -> PileF m r) -> PileF m r) -> PileM m a
- Bio.Bam.Pileup: [runPileM] :: PileM m a -> forall r. (a -> PileF m r) -> PileF m r
- Bio.Bam.Pileup: data Heap
- Bio.Bam.Pileup: decompose :: DmgToken -> BamRaw -> [PosPrimChunks]
- Bio.Bam.Pileup: getMinKeyH :: Heap -> Maybe Int
- Bio.Bam.Pileup: get_pos :: PileM m Int
- Bio.Bam.Pileup: get_refseq :: PileM m Refseq
- Bio.Bam.Pileup: newtype PileM m a
- Bio.Bam.Pileup: p'check_waiting :: PileM m ()
- Bio.Bam.Pileup: p'feed_input :: PileM m ()
- Bio.Bam.Pileup: p'scan_active :: PileM m ((CallStats, BasePile), (CallStats, BasePile), (CallStats, IndelPile), (CallStats, IndelPile))
- Bio.Bam.Pileup: pileup' :: PileM m ()
- Bio.Bam.Pileup: pileup'' :: PileM m ()
- Bio.Bam.Pileup: type PileF m r = Refseq -> Int -> ([PrimBase], [PrimBase]) -> (Heap, Heap) -> (Stream [Pile] -> Iteratee [Pile] m r) -> Stream [PosPrimChunks] -> Iteratee [PosPrimChunks] m (Iteratee [Pile] m r)
- Bio.Bam.Pileup: unionH :: Heap -> Heap -> Heap
- Bio.Bam.Pileup: upd_pos :: (Int -> Int) -> PileM m ()
- Bio.Bam.Pileup: viewMinH :: Heap -> Maybe (Int, PrimBase, Heap)
- Bio.Bam.Pileup: yieldPile :: CallStats -> BasePile -> BasePile -> CallStats -> IndelPile -> IndelPile -> PileM m ()
- Bio.Bam.Reader: Block :: {-# UNPACK #-} !FileOffset -> {-# UNPACK #-} !Bytes -> Block
- Bio.Bam.Reader: [block_contents] :: Block -> {-# UNPACK #-} !Bytes
- Bio.Bam.Reader: [block_offset] :: Block -> {-# UNPACK #-} !FileOffset
- Bio.Bam.Reader: combineCoordinates :: Monad m => BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a
- Bio.Bam.Reader: combineNames :: Monad m => BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a
- Bio.Bam.Reader: compressBgzf :: MonadIO m => Enumeratee BgzfChunk Bytes m a
- Bio.Bam.Reader: concatDefaultInputs :: MonadBracketIO m => Enumerator' BamMeta [BamRaw] m a
- Bio.Bam.Reader: data Block
- Bio.Bam.Reader: decodeAnyBam :: MonadIO m => BamrawEnumeratee m a
- Bio.Bam.Reader: decodeAnyBamFile :: MonadBracketIO m => FilePath -> (BamMeta -> Iteratee [BamRaw] m a) -> m (Iteratee [BamRaw] m a)
- Bio.Bam.Reader: decodeAnyBamOrSam :: MonadIO m => BamEnumeratee m a
- Bio.Bam.Reader: decodeAnyBamOrSamFile :: MonadBracketIO m => FilePath -> (BamMeta -> Iteratee [BamRec] m a) -> m (Iteratee [BamRec] m a)
- Bio.Bam.Reader: decodeSam :: Monad m => (BamMeta -> Iteratee [BamRec] m a) -> Iteratee Bytes m (Iteratee [BamRec] m a)
- Bio.Bam.Reader: decodeSam' :: Monad m => Refs -> Enumeratee Bytes [BamRec] m a
- Bio.Bam.Reader: decompressBgzf :: MonadIO m => Enumeratee Bytes Bytes m a
- Bio.Bam.Reader: decompressBgzfBlocks :: MonadIO m => Enumeratee Bytes Block m a
- Bio.Bam.Reader: isBam :: MonadIO m => Iteratee Bytes m (Maybe (BamrawEnumeratee m a))
- Bio.Bam.Reader: isBamOrSam :: MonadIO m => Iteratee Bytes m (BamEnumeratee m a)
- Bio.Bam.Reader: isBgzfBam :: MonadIO m => Iteratee Bytes m (Maybe (BamrawEnumeratee m a))
- Bio.Bam.Reader: isGzipBam :: MonadIO m => Iteratee Bytes m (Maybe (BamrawEnumeratee m a))
- Bio.Bam.Reader: isPlainBam :: MonadIO m => Iteratee Bytes m (Maybe (BamrawEnumeratee m a))
- Bio.Bam.Reader: mergeDefaultInputs :: MonadBracketIO m => (BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a) -> Enumerator' BamMeta [BamRaw] m a
- Bio.Bam.Reader: mergeInputs :: MonadBracketIO m => (BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a) -> [FilePath] -> Enumerator' BamMeta [BamRaw] m a
- Bio.Bam.Reader: type BamEnumeratee m b = Enumeratee' BamMeta Bytes [BamRec] m b
- Bio.Bam.Reader: type BamrawEnumeratee m b = Enumeratee' BamMeta Bytes [BamRaw] m b
- Bio.Bam.Rec: data Word32
- Bio.Bam.Rec: progressBam :: MonadIO m => String -> Refs -> Int -> (String -> IO ()) -> Enumeratee [BamRaw] [BamRaw] m a
- Bio.Bam.Regions: add :: Region -> Regions -> Regions
- Bio.Bam.Regions: addInt :: Int -> Int -> Subsequence -> Subsequence
- Bio.Base: c2w :: Char -> Word8
- Bio.Base: findAuxFile :: FilePath -> IO FilePath
- Bio.Base: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Bio.Base.Nucleotide
- Bio.Base: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Bio.Base.Nucleotides
- Bio.Base: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Bio.Base.Qual
- Bio.Base: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Bio.Base.Nucleotide
- Bio.Base: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Bio.Base.Nucleotides
- Bio.Base: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Bio.Base.Qual
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox Bio.Base.Nucleotide
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox Bio.Base.Nucleotides
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox Bio.Base.Qual
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox a => Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector (Bio.Base.Prob' a)
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox a => Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector (Bio.Base.Prob' a)
- Bio.Base: instance Data.Vector.Unboxed.Base.Unbox a => Data.Vector.Unboxed.Base.Unbox (Bio.Base.Prob' a)
- Bio.Base: type Seqid = ByteString
- Bio.Base: w2c :: Word8 -> Char
- Bio.Iteratee: ($==) :: Monad m => Enumerator' hdr input m (Iteratee output m result) -> Enumeratee input output m result -> Enumerator' hdr output m result
- Bio.Iteratee: Equal :: a -> Ordering' a
- Bio.Iteratee: Less :: Ordering' a
- Bio.Iteratee: NotLess :: Ordering' a
- Bio.Iteratee: ParseError :: [String] -> String -> ParseError
- Bio.Iteratee: QQ :: !Int -> [a] -> [a] -> QQ a
- Bio.Iteratee: [errorContexts] :: ParseError -> [String]
- Bio.Iteratee: [errorMessage] :: ParseError -> String
- Bio.Iteratee: cancelAll :: MonadIO m => QQ (Async a) -> m ()
- Bio.Iteratee: class Monad m => MonadIO (m :: Type -> Type)
- Bio.Iteratee: class MonadCatch m => MonadMask (m :: Type -> Type)
- Bio.Iteratee: data Fd
- Bio.Iteratee: data Ordering' a
- Bio.Iteratee: data ParseError
- Bio.Iteratee: data QQ a
- Bio.Iteratee: emptyQ :: QQ a
- Bio.Iteratee: enumAuxFile :: MonadBracketIO m => FilePath -> Iteratee ByteString m a -> m a
- Bio.Iteratee: enumDefaultInputs :: MonadBracketIO m => Enumerator ByteString m a
- Bio.Iteratee: enumInputs :: MonadBracketIO m => [FilePath] -> Enumerator ByteString m a
- Bio.Iteratee: iGetString :: Int -> Iteratee ByteString m ByteString
- Bio.Iteratee: iLookAhead :: Monoid s => Iteratee s m a -> Iteratee s m a
- Bio.Iteratee: infixl 1 $==
- Bio.Iteratee: instance GHC.Exception.Type.Exception Bio.Iteratee.ParseError
- Bio.Iteratee: instance GHC.Show.Show Bio.Iteratee.ParseError
- Bio.Iteratee: iterLoop :: (Nullable s, Monad m) => (a -> Iteratee s m a) -> a -> Iteratee s m a
- Bio.Iteratee: lengthQ :: QQ a -> Int
- Bio.Iteratee: lift :: (MonadTrans t, Monad m) => m a -> t m a
- Bio.Iteratee: liftIO :: MonadIO m => IO a -> m a
- Bio.Iteratee: mergeEnums' :: (Nullable s2, Nullable s1, Monad m) => Enumerator' hi s1 m a -> Enumerator' ho s2 (Iteratee s1 m) a -> (ho -> Enumeratee s2 s1 (Iteratee s1 m) a) -> Enumerator' hi s1 m a
- Bio.Iteratee: mergeSortStreams :: Monad m => (a -> a -> Ordering' a) -> Enumeratee [a] [a] (Iteratee [a] m) b
- Bio.Iteratee: parMapChunksIO :: (MonadIO m, Nullable s) => Int -> (s -> IO t) -> Enumeratee s t m a
- Bio.Iteratee: parRunIO :: MonadIO m => Int -> Enumeratee [IO a] a m b
- Bio.Iteratee: parserToIteratee :: Parser a -> Iteratee ByteString m a
- Bio.Iteratee: popQ :: QQ a -> Maybe (a, QQ a)
- Bio.Iteratee: progressGen :: MonadIO m => (Int -> a -> String) -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b
- Bio.Iteratee: progressNum :: MonadIO m => String -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b
- Bio.Iteratee: progressPos :: MonadIO m => (a -> (Refseq, Int)) -> String -> Refs -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b
- Bio.Iteratee: protectTerm :: (Nullable s, MonadIO m) => Iteratee s m a -> Iteratee s m a
- Bio.Iteratee: pushQ :: a -> QQ a -> QQ a
- Bio.Iteratee: stderr :: Handle
- Bio.Iteratee: stdin :: Handle
- Bio.Iteratee: stdout :: Handle
- Bio.Iteratee: stream2vector :: (MonadIO m, Vector v a) => Iteratee [a] m (v a)
- Bio.Iteratee: stream2vectorN :: (MonadIO m, Vector v a) => Int -> Iteratee [a] m (v a)
- Bio.Iteratee: type Enumeratee' h ei eo m b = (h -> Iteratee eo m b) -> Iteratee ei m (Iteratee eo m b)
- Bio.Iteratee: type Enumerator' h eo m b = (h -> Iteratee eo m b) -> m (Iteratee eo m b)
- Bio.Iteratee: withFileFd :: (MonadIO m, MonadMask m) => FilePath -> (Fd -> m a) -> m a
- Bio.Iteratee.Base: Chunk :: c -> Stream c
- Bio.Iteratee.Base: DataRemaining :: StreamStatus
- Bio.Iteratee.Base: EOF :: Maybe SomeException -> Stream c
- Bio.Iteratee.Base: EofError :: SomeException -> StreamStatus
- Bio.Iteratee.Base: EofNoError :: StreamStatus
- Bio.Iteratee.Base: Iteratee :: (forall r. (a -> Stream s -> m r) -> ((Stream s -> Iteratee s m a) -> Maybe SomeException -> m r) -> m r) -> Iteratee s m a
- Bio.Iteratee.Base: [runIter] :: Iteratee s m a -> forall r. (a -> Stream s -> m r) -> ((Stream s -> Iteratee s m a) -> Maybe SomeException -> m r) -> m r
- Bio.Iteratee.Base: class NullPoint c
- Bio.Iteratee.Base: class NullPoint c => Nullable c
- Bio.Iteratee.Base: data Stream c
- Bio.Iteratee.Base: data StreamStatus
- Bio.Iteratee.Base: emptyP :: NullPoint c => c
- Bio.Iteratee.Base: icont :: (Stream s -> Iteratee s m a) -> Maybe SomeException -> Iteratee s m a
- Bio.Iteratee.Base: icontM :: Monad m => (Stream s -> Iteratee s m a) -> Maybe SomeException -> m (Iteratee s m a)
- Bio.Iteratee.Base: idone :: a -> Stream s -> Iteratee s m a
- Bio.Iteratee.Base: idoneM :: Monad m => a -> Stream s -> m (Iteratee s m a)
- Bio.Iteratee.Base: ifold :: (Monad m, Monad n) => (forall r. m r -> acc -> n (r, acc)) -> acc -> Iteratee s m a -> Iteratee s n (a, acc)
- Bio.Iteratee.Base: ilift :: (Monad m, Monad n) => (forall r. m r -> n r) -> Iteratee s m a -> Iteratee s n a
- Bio.Iteratee.Base: instance (Control.Monad.Catch.MonadCatch m, Bio.Iteratee.Base.Nullable s, Bio.Iteratee.Base.NullPoint s) => Control.Monad.Catch.MonadCatch (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance (Control.Monad.Catch.MonadThrow m, Bio.Iteratee.Base.Nullable s, Bio.Iteratee.Base.NullPoint s) => Control.Monad.Catch.MonadThrow (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance (Control.Monad.IO.Class.MonadIO m, Bio.Iteratee.Base.Nullable s, Bio.Iteratee.Base.NullPoint s) => Control.Monad.IO.Class.MonadIO (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance (GHC.Base.Functor m, GHC.Base.Monad m, Bio.Iteratee.Base.Nullable s) => GHC.Base.Applicative (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance (GHC.Base.Monad m, Bio.Iteratee.Base.Nullable s) => GHC.Base.Monad (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.NullPoint (Data.Semigroup.Internal.Endo a)
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.NullPoint Data.ByteString.Internal.ByteString
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.NullPoint Data.ByteString.Lazy.Internal.ByteString
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.NullPoint [a]
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.NullPoint s => Control.Monad.Trans.Class.MonadTrans (Bio.Iteratee.Base.Iteratee s)
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.Nullable Data.ByteString.Internal.ByteString
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.Nullable Data.ByteString.Lazy.Internal.ByteString
- Bio.Iteratee.Base: instance Bio.Iteratee.Base.Nullable [a]
- Bio.Iteratee.Base: instance GHC.Base.Functor Bio.Iteratee.Base.Stream
- Bio.Iteratee.Base: instance GHC.Base.Functor m => GHC.Base.Functor (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Base: instance GHC.Base.Monoid c => GHC.Base.Monoid (Bio.Iteratee.Base.Stream c)
- Bio.Iteratee.Base: instance GHC.Base.Semigroup c => GHC.Base.Semigroup (Bio.Iteratee.Base.Stream c)
- Bio.Iteratee.Base: instance GHC.Classes.Eq c => GHC.Classes.Eq (Bio.Iteratee.Base.Stream c)
- Bio.Iteratee.Base: instance GHC.Show.Show Bio.Iteratee.Base.StreamStatus
- Bio.Iteratee.Base: instance GHC.Show.Show c => GHC.Show.Show (Bio.Iteratee.Base.Stream c)
- Bio.Iteratee.Base: liftI :: (Stream s -> Iteratee s m a) -> Iteratee s m a
- Bio.Iteratee.Base: newtype Iteratee s m a
- Bio.Iteratee.Base: nullC :: Nullable c => c -> Bool
- Bio.Iteratee.Base: run :: Monad m => Iteratee s m a -> m a
- Bio.Iteratee.Base: setEOF :: Stream c -> SomeException
- Bio.Iteratee.Base: tryRun :: (Exception e, Monad m) => Iteratee s m a -> m (Either e a)
- Bio.Iteratee.Bgzf: Block :: {-# UNPACK #-} !FileOffset -> {-# UNPACK #-} !Bytes -> Block
- Bio.Iteratee.Bgzf: CompressParams :: Int -> Int -> CompressParams
- Bio.Iteratee.Bgzf: LeftoverChunk :: !Bytes -> BgzfChunk -> BgzfChunk
- Bio.Iteratee.Bgzf: NoChunk :: BgzfChunk
- Bio.Iteratee.Bgzf: RecordChunk :: !Bytes -> BgzfChunk -> BgzfChunk
- Bio.Iteratee.Bgzf: SpecialChunk :: !Bytes -> BgzfChunk -> BgzfChunk
- Bio.Iteratee.Bgzf: [block_contents] :: Block -> {-# UNPACK #-} !Bytes
- Bio.Iteratee.Bgzf: [block_offset] :: Block -> {-# UNPACK #-} !FileOffset
- Bio.Iteratee.Bgzf: [compression_level] :: CompressParams -> Int
- Bio.Iteratee.Bgzf: [queue_depth] :: CompressParams -> Int
- Bio.Iteratee.Bgzf: bgzfEofMarker :: Bytes
- Bio.Iteratee.Bgzf: compressBgzf :: MonadIO m => Enumeratee BgzfChunk Bytes m a
- Bio.Iteratee.Bgzf: compressBgzf' :: MonadIO m => CompressParams -> Enumeratee BgzfChunk Bytes m a
- Bio.Iteratee.Bgzf: compressBgzfLv :: MonadIO m => Int -> Enumeratee BgzfChunk Bytes m a
- Bio.Iteratee.Bgzf: compressChunk :: Int -> Ptr Word8 -> CUInt -> IO Bytes
- Bio.Iteratee.Bgzf: data BgzfChunk
- Bio.Iteratee.Bgzf: data Block
- Bio.Iteratee.Bgzf: data CompressParams
- Bio.Iteratee.Bgzf: decompressBgzf :: MonadIO m => Enumeratee Bytes Bytes m a
- Bio.Iteratee.Bgzf: decompressBgzfBlocks :: MonadIO m => Enumeratee Bytes Block m a
- Bio.Iteratee.Bgzf: decompressBgzfBlocks' :: MonadIO m => Int -> Enumeratee Bytes Block m a
- Bio.Iteratee.Bgzf: decompressPlain :: MonadIO m => Enumeratee Bytes Block m a
- Bio.Iteratee.Bgzf: getOffset :: Iteratee Block m FileOffset
- Bio.Iteratee.Bgzf: instance Bio.Iteratee.Base.NullPoint Bio.Iteratee.Bgzf.BgzfChunk
- Bio.Iteratee.Bgzf: instance Bio.Iteratee.Base.NullPoint Bio.Iteratee.Bgzf.Block
- Bio.Iteratee.Bgzf: instance Bio.Iteratee.Base.Nullable Bio.Iteratee.Bgzf.BgzfChunk
- Bio.Iteratee.Bgzf: instance Bio.Iteratee.Base.Nullable Bio.Iteratee.Bgzf.Block
- Bio.Iteratee.Bgzf: instance GHC.Base.Monoid Bio.Iteratee.Bgzf.Block
- Bio.Iteratee.Bgzf: instance GHC.Base.Semigroup Bio.Iteratee.Bgzf.Block
- Bio.Iteratee.Bgzf: instance GHC.Show.Show Bio.Iteratee.Bgzf.CompressParams
- Bio.Iteratee.Bgzf: isBgzf :: Monad m => Iteratee Bytes m Bool
- Bio.Iteratee.Bgzf: isGzip :: Monad m => Iteratee Bytes m Bool
- Bio.Iteratee.Bgzf: liftBlock :: Monad m => Iteratee Bytes m a -> Iteratee Block m a
- Bio.Iteratee.Bgzf: maxBlockSize :: Int
- Bio.Iteratee.Bgzf: parMapChunksIO :: (MonadIO m, Nullable s) => Int -> (s -> IO t) -> Enumeratee s t m a
- Bio.Iteratee.Builder: BB :: {-# UNPACK #-} !ForeignPtr Word8 -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> BB
- Bio.Iteratee.Builder: BclArgs :: BclSpecialType -> {-# UNPACK #-} !Vector Word8 -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> BclArgs
- Bio.Iteratee.Builder: BclNucsAsc :: BclSpecialType
- Bio.Iteratee.Builder: BclNucsAscRev :: BclSpecialType
- Bio.Iteratee.Builder: BclNucsBin :: BclSpecialType
- Bio.Iteratee.Builder: BclNucsWide :: BclSpecialType
- Bio.Iteratee.Builder: BclQualsAsc :: BclSpecialType
- Bio.Iteratee.Builder: BclQualsAscRev :: BclSpecialType
- Bio.Iteratee.Builder: BclQualsBin :: BclSpecialType
- Bio.Iteratee.Builder: TkBclSpecial :: !BclArgs -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkDecimal :: {-# UNPACK #-} !Int -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkDouble :: {-# UNPACK #-} !Double -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkEnd :: BgzfTokens
- Bio.Iteratee.Builder: TkEndRecord :: BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkEndRecordPart1 :: BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkEndRecordPart2 :: BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkFloat :: {-# UNPACK #-} !Float -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkLowLevel :: {-# UNPACK #-} !Int -> (BB -> IO BB) -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkSetMark :: BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkString :: {-# UNPACK #-} !ByteString -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkWord16 :: {-# UNPACK #-} !Word16 -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkWord32 :: {-# UNPACK #-} !Word32 -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: TkWord8 :: {-# UNPACK #-} !Word8 -> BgzfTokens -> BgzfTokens
- Bio.Iteratee.Builder: [buffer] :: BB -> {-# UNPACK #-} !ForeignPtr Word8
- Bio.Iteratee.Builder: [mark2] :: BB -> {-# UNPACK #-} !Int
- Bio.Iteratee.Builder: [mark] :: BB -> {-# UNPACK #-} !Int
- Bio.Iteratee.Builder: [off] :: BB -> {-# UNPACK #-} !Int
- Bio.Iteratee.Builder: [size] :: BB -> {-# UNPACK #-} !Int
- Bio.Iteratee.Builder: [used] :: BB -> {-# UNPACK #-} !Int
- Bio.Iteratee.Builder: data BB
- Bio.Iteratee.Builder: data BclArgs
- Bio.Iteratee.Builder: data BclSpecialType
- Bio.Iteratee.Builder: data BgzfTokens
- Bio.Iteratee.Builder: encodeBgzf :: MonadIO m => Int -> Enumeratee (Endo BgzfTokens) ByteString m b
- Bio.Iteratee.Builder: expandBuffer :: Int -> BB -> IO BB
- Bio.Iteratee.Builder: fillBuffer :: BB -> BgzfTokens -> IO (BB, BgzfTokens)
- Bio.Iteratee.Builder: instance Bio.Iteratee.Base.Nullable (Data.Semigroup.Internal.Endo Bio.Iteratee.Builder.BgzfTokens)
- Bio.Iteratee.Builder: int_loop :: Ptr Word8 -> Int -> IO Int
- Bio.Iteratee.Builder: loop_bcl_special :: Ptr Word8 -> BclArgs -> IO Int
- Bio.Iteratee.Builder: newBuffer :: Int -> IO BB
- Bio.Iteratee.Bytes: LSB :: Endian
- Bio.Iteratee.Bytes: MSB :: Endian
- Bio.Iteratee.Bytes: data Endian
- Bio.Iteratee.Bytes: dropStreamBS :: Int -> Iteratee Bytes m ()
- Bio.Iteratee.Bytes: dropWhileStreamBS :: (Word8 -> Bool) -> Iteratee Bytes m ()
- Bio.Iteratee.Bytes: endianRead2 :: Endian -> Iteratee Bytes m Word16
- Bio.Iteratee.Bytes: endianRead3 :: Endian -> Iteratee Bytes m Word32
- Bio.Iteratee.Bytes: endianRead3i :: Monad m => Endian -> Iteratee Bytes m Int32
- Bio.Iteratee.Bytes: endianRead4 :: Endian -> Iteratee Bytes m Word32
- Bio.Iteratee.Bytes: endianRead8 :: Endian -> Iteratee Bytes m Word64
- Bio.Iteratee.Bytes: enumLinesBS :: Monad m => Enumeratee Bytes [Bytes] m a
- Bio.Iteratee.Bytes: enumWordsBS :: Monad m => Enumeratee Bytes [Bytes] m a
- Bio.Iteratee.Bytes: headStreamBS :: Iteratee Bytes m Word8
- Bio.Iteratee.Bytes: instance GHC.Classes.Eq Bio.Iteratee.Bytes.Endian
- Bio.Iteratee.Bytes: instance GHC.Classes.Ord Bio.Iteratee.Bytes.Endian
- Bio.Iteratee.Bytes: instance GHC.Enum.Enum Bio.Iteratee.Bytes.Endian
- Bio.Iteratee.Bytes: instance GHC.Show.Show Bio.Iteratee.Bytes.Endian
- Bio.Iteratee.Bytes: peekStreamBS :: Iteratee Bytes m (Maybe Word8)
- Bio.Iteratee.Bytes: takeStreamBS :: Monad m => Int -> Enumeratee Bytes Bytes m a
- Bio.Iteratee.Bytes: tryHeadBS :: Iteratee Bytes m (Maybe Word8)
- Bio.Iteratee.Exception: DivergentException :: DivergentException
- Bio.Iteratee.Exception: EnumException :: e -> EnumException
- Bio.Iteratee.Exception: EnumStringException :: String -> EnumStringException
- Bio.Iteratee.Exception: EnumUnhandledIterException :: IterException -> EnumUnhandledIterException
- Bio.Iteratee.Exception: EofException :: EofException
- Bio.Iteratee.Exception: IFException :: e -> IFException
- Bio.Iteratee.Exception: IterException :: e -> IterException
- Bio.Iteratee.Exception: IterStringException :: String -> IterStringException
- Bio.Iteratee.Exception: SeekException :: FileOffset -> SeekException
- Bio.Iteratee.Exception: class (Typeable e, Show e) => Exception e
- Bio.Iteratee.Exception: class Exception e => IException e
- Bio.Iteratee.Exception: data DivergentException
- Bio.Iteratee.Exception: data EnumException
- Bio.Iteratee.Exception: data EofException
- Bio.Iteratee.Exception: data IFException
- Bio.Iteratee.Exception: data IterException
- Bio.Iteratee.Exception: displayException :: Exception e => e -> String
- Bio.Iteratee.Exception: enStrExc :: String -> EnumException
- Bio.Iteratee.Exception: fromException :: Exception e => SomeException -> Maybe e
- Bio.Iteratee.Exception: fromIterException :: IException e => IterException -> Maybe e
- Bio.Iteratee.Exception: instance Bio.Iteratee.Exception.IException Bio.Iteratee.Exception.EofException
- Bio.Iteratee.Exception: instance Bio.Iteratee.Exception.IException Bio.Iteratee.Exception.IterException
- Bio.Iteratee.Exception: instance Bio.Iteratee.Exception.IException Bio.Iteratee.Exception.IterStringException
- Bio.Iteratee.Exception: instance Bio.Iteratee.Exception.IException Bio.Iteratee.Exception.SeekException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.DivergentException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.EnumException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.EnumStringException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.EnumUnhandledIterException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.EofException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.IFException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.IterException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.IterStringException
- Bio.Iteratee.Exception: instance GHC.Exception.Type.Exception Bio.Iteratee.Exception.SeekException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.DivergentException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.EnumException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.EnumStringException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.EnumUnhandledIterException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.EofException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.IFException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.IterException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.IterStringException
- Bio.Iteratee.Exception: instance GHC.Show.Show Bio.Iteratee.Exception.SeekException
- Bio.Iteratee.Exception: iterExceptionFromException :: Exception e => SomeException -> Maybe e
- Bio.Iteratee.Exception: iterExceptionToException :: Exception e => e -> SomeException
- Bio.Iteratee.Exception: iterStrExc :: String -> SomeException
- Bio.Iteratee.Exception: newtype EnumStringException
- Bio.Iteratee.Exception: newtype EnumUnhandledIterException
- Bio.Iteratee.Exception: newtype IterStringException
- Bio.Iteratee.Exception: newtype SeekException
- Bio.Iteratee.Exception: toException :: Exception e => e -> SomeException
- Bio.Iteratee.Exception: toIterException :: IException e => e -> IterException
- Bio.Iteratee.Exception: type FileOffset = COff
- Bio.Iteratee.Exception: wrapIterExc :: IterException -> EnumException
- Bio.Iteratee.IO: defaultBufSize :: Int
- Bio.Iteratee.IO: enumFd :: MonadIO m => Int -> Fd -> Enumerator Bytes m a
- Bio.Iteratee.IO: enumFdRandom :: MonadIO m => Int -> Fd -> Enumerator Bytes m a
- Bio.Iteratee.IO: enumFile :: MonadBracketIO m => Int -> FilePath -> Enumerator Bytes m a
- Bio.Iteratee.IO: enumFileRandom :: MonadBracketIO m => Int -> FilePath -> Enumerator Bytes m a
- Bio.Iteratee.Iteratee: ($=) :: Monad m => (forall a. Enumerator s m a) -> Enumeratee s s' m b -> Enumerator s' m b
- Bio.Iteratee.Iteratee: (<><) :: (Nullable s1, Monad m) => Enumeratee s2 s3 m a -> (forall x. Enumeratee s1 s2 m x) -> Enumeratee s1 s3 m a
- Bio.Iteratee.Iteratee: (=$) :: (Nullable s, Monad m) => Enumeratee s s' m a -> Iteratee s' m a -> Iteratee s m a
- Bio.Iteratee.Iteratee: (><>) :: (Nullable s1, Monad m) => (forall x. Enumeratee s1 s2 m x) -> Enumeratee s2 s3 m a -> Enumeratee s1 s3 m a
- Bio.Iteratee.Iteratee: altmask :: MonadBracketIO m => ((forall a. m a -> m a) -> m b) -> m b
- Bio.Iteratee.Iteratee: bracketIO :: MonadBracketIO m => IO a -> (a -> IO b) -> (a -> m c) -> m c
- Bio.Iteratee.Iteratee: checkErr :: NullPoint s => Iteratee s m a -> Iteratee s m (Either SomeException a)
- Bio.Iteratee.Iteratee: class (MonadCatch m, MonadIO m) => MonadBracketIO m
- Bio.Iteratee.Iteratee: convStream :: (Monad m, Nullable s) => Iteratee s m s' -> Enumeratee s s' m a
- Bio.Iteratee.Iteratee: eneeCheckIfDone :: (Monad m, NullPoint elo) => ((Stream eli -> Iteratee eli m a) -> Iteratee elo m (Iteratee eli m a)) -> Enumeratee elo eli m a
- Bio.Iteratee.Iteratee: eneeCheckIfDoneHandle :: NullPoint elo => EnumerateeHandler eli elo m a -> ((Stream eli -> Iteratee eli m a) -> Maybe SomeException -> Iteratee elo m (Iteratee eli m a)) -> Enumeratee elo eli m a
- Bio.Iteratee.Iteratee: eneeCheckIfDoneIgnore :: NullPoint elo => ((Stream eli -> Iteratee eli m a) -> Maybe SomeException -> Iteratee elo m (Iteratee eli m a)) -> Enumeratee elo eli m a
- Bio.Iteratee.Iteratee: eneeCheckIfDonePass :: NullPoint elo => ((Stream eli -> Iteratee eli m a) -> Maybe SomeException -> Iteratee elo m (Iteratee eli m a)) -> Enumeratee elo eli m a
- Bio.Iteratee.Iteratee: enumCheckIfDone :: Monad m => Iteratee s m a -> m (Bool, Iteratee s m a)
- Bio.Iteratee.Iteratee: enumChunk :: Monad m => Stream s -> Enumerator s m a
- Bio.Iteratee.Iteratee: enumEof :: Monad m => Enumerator s m a
- Bio.Iteratee.Iteratee: enumErr :: (Exception e, Monad m) => e -> Enumerator s m a
- Bio.Iteratee.Iteratee: enumFromCallback :: (Monad m, NullPoint s) => (st -> m (Either SomeException ((Bool, st), s))) -> st -> Enumerator s m a
- Bio.Iteratee.Iteratee: enumFromCallbackCatch :: (IException e, Monad m, NullPoint s) => (st -> m (Either SomeException ((Bool, st), s))) -> (e -> m (Maybe EnumException)) -> st -> Enumerator s m a
- Bio.Iteratee.Iteratee: enumList :: Monad m => [s] -> Enumerator s m a
- Bio.Iteratee.Iteratee: enumPure1Chunk :: Monad m => s -> Enumerator s m a
- Bio.Iteratee.Iteratee: foldChunksM :: (Monad m, Nullable s) => (a -> s -> m a) -> a -> Iteratee s m a
- Bio.Iteratee.Iteratee: getChunk :: Nullable s => Iteratee s m s
- Bio.Iteratee.Iteratee: getChunks :: Nullable s => Iteratee s m [s]
- Bio.Iteratee.Iteratee: infixl 1 $=
- Bio.Iteratee.Iteratee: infixr 0 =$
- Bio.Iteratee.Iteratee: instance (Bio.Iteratee.Iteratee.MonadBracketIO m, Bio.Iteratee.Base.Nullable s) => Bio.Iteratee.Iteratee.MonadBracketIO (Bio.Iteratee.Base.Iteratee s m)
- Bio.Iteratee.Iteratee: instance Bio.Iteratee.Iteratee.MonadBracketIO GHC.Types.IO
- Bio.Iteratee.Iteratee: ioBind :: MonadIO m => IO a -> (a -> Iteratee s m b) -> Iteratee s m b
- Bio.Iteratee.Iteratee: ioBind_ :: MonadIO m => IO a -> Iteratee s m b -> Iteratee s m b
- Bio.Iteratee.Iteratee: isStreamFinished :: Nullable s => Iteratee s m (Maybe SomeException)
- Bio.Iteratee.Iteratee: joinI :: (Monad m, Nullable s) => Iteratee s m (Iteratee s' m a) -> Iteratee s m a
- Bio.Iteratee.Iteratee: joinIM :: Monad m => m (Iteratee s m a) -> Iteratee s m a
- Bio.Iteratee.Iteratee: mBind :: Monad m => m a -> (a -> Iteratee s m b) -> Iteratee s m b
- Bio.Iteratee.Iteratee: mBind_ :: Monad m => m a -> Iteratee s m b -> Iteratee s m b
- Bio.Iteratee.Iteratee: mapChunks :: NullPoint s => (s -> s') -> Enumeratee s s' m a
- Bio.Iteratee.Iteratee: mapChunksM :: (Monad m, NullPoint s) => (s -> m s') -> Enumeratee s s' m a
- Bio.Iteratee.Iteratee: mapChunksM_ :: (Monad m, Nullable s) => (s -> m b) -> Iteratee s m ()
- Bio.Iteratee.Iteratee: mergeEnums :: (Nullable s2, Nullable s1, Monad m) => Enumerator s1 m a -> Enumerator s2 (Iteratee s1 m) a -> Enumeratee s2 s1 (Iteratee s1 m) a -> Enumerator s1 m a
- Bio.Iteratee.Iteratee: seek :: Nullable s => FileOffset -> Iteratee s m ()
- Bio.Iteratee.Iteratee: skipToEof :: Iteratee s m ()
- Bio.Iteratee.Iteratee: throwErr :: SomeException -> Iteratee s m a
- Bio.Iteratee.Iteratee: throwRecoverableErr :: SomeException -> (Stream s -> Iteratee s m a) -> Iteratee s m a
- Bio.Iteratee.Iteratee: type Enumeratee sFrom sTo m a = Iteratee sTo m a -> Iteratee sFrom m (Iteratee sTo m a)
- Bio.Iteratee.Iteratee: type EnumerateeHandler eli elo m a = (Stream eli -> Iteratee eli m a) -> SomeException -> Iteratee elo m (Iteratee eli m a)
- Bio.Iteratee.Iteratee: type Enumerator s m a = Iteratee s m a -> m (Iteratee s m a)
- Bio.Iteratee.Iteratee: unfoldConvStream :: (Monad m, Nullable s) => (acc -> Iteratee s m (acc, s')) -> acc -> Enumeratee s s' m a
- Bio.Iteratee.Iteratee: unfoldConvStreamCheck :: (Monad m, Nullable elo) => (((Stream eli -> Iteratee eli m a) -> Maybe SomeException -> Iteratee elo m (Iteratee eli m a)) -> Enumeratee elo eli m a) -> (acc -> Iteratee elo m (acc, eli)) -> acc -> Enumeratee elo eli m a
- Bio.Iteratee.List: breakE :: (el -> Bool) -> Enumeratee [el] [el] m a
- Bio.Iteratee.List: breakStream :: (el -> Bool) -> Iteratee [el] m [el]
- Bio.Iteratee.List: chunkLength :: Iteratee [el] m (Maybe Int)
- Bio.Iteratee.List: concatMapStream :: Monoid t => (a -> t) -> Enumeratee [a] t m r
- Bio.Iteratee.List: concatMapStreamM :: Monad m => (a -> m t) -> Enumeratee [a] t m r
- Bio.Iteratee.List: countConsumed :: (Monad m, Integral n) => Iteratee [el] m a -> Iteratee [el] m (a, n)
- Bio.Iteratee.List: dropStream :: Int -> Iteratee [el] m ()
- Bio.Iteratee.List: dropWhileStream :: (el -> Bool) -> Iteratee [el] m ()
- Bio.Iteratee.List: enumPureNChunk :: Monad m => [el] -> Int -> Enumerator [el] m a
- Bio.Iteratee.List: enumWith :: Monad m => Iteratee [el] m a -> Iteratee [el] m b -> Iteratee [el] m (a, b)
- Bio.Iteratee.List: filterStream :: (el -> Bool) -> Enumeratee [el] [el] m a
- Bio.Iteratee.List: filterStreamM :: Monad m => (a -> m Bool) -> Enumeratee [a] [a] m r
- Bio.Iteratee.List: foldStream :: (a -> el -> a) -> a -> Iteratee [el] m a
- Bio.Iteratee.List: foldStreamM :: Monad m => (b -> a -> m b) -> b -> Iteratee [a] m b
- Bio.Iteratee.List: groupStreamBy :: Monad m => (t -> t -> Bool) -> m (Iteratee [t] m t2) -> Enumeratee [t] [t2] m a
- Bio.Iteratee.List: groupStreamOn :: (Monad m, Eq t1) => (e -> t1) -> (t1 -> m (Iteratee [e] m t2)) -> Enumeratee [e] [(t1, t2)] m a
- Bio.Iteratee.List: headStream :: Iteratee [el] m el
- Bio.Iteratee.List: heads :: (Monad m, Eq el) => [el] -> Iteratee [el] m Int
- Bio.Iteratee.List: isFinished :: Nullable s => Iteratee s m Bool
- Bio.Iteratee.List: lastStream :: Iteratee [el] m el
- Bio.Iteratee.List: lengthStream :: Num a => Iteratee [el] m a
- Bio.Iteratee.List: mapMaybeStream :: (a -> Maybe b) -> Enumeratee [a] [b] m r
- Bio.Iteratee.List: mapStream :: (el -> el') -> Enumeratee [el] [el'] m a
- Bio.Iteratee.List: mapStreamM :: Monad m => (el -> m el') -> Enumeratee [el] [el'] m a
- Bio.Iteratee.List: mapStreamM_ :: Monad m => (el -> m b) -> Iteratee [el] m ()
- Bio.Iteratee.List: mergeByChunks :: Monad m => ([el1] -> [el2] -> c3) -> ([el1] -> c3) -> ([el2] -> c3) -> Enumeratee [el2] c3 (Iteratee [el1] m) a
- Bio.Iteratee.List: mergeStreams :: Monad m => (el1 -> el2 -> b) -> Enumeratee [el2] b (Iteratee [el1] m) a
- Bio.Iteratee.List: peekStream :: Iteratee [el] m (Maybe el)
- Bio.Iteratee.List: roll :: Monad m => Int -> Int -> Iteratee [el] m [[el]]
- Bio.Iteratee.List: sequenceStreams_ :: Monad m => [Iteratee [el] m a] -> Iteratee [el] m ()
- Bio.Iteratee.List: stream2list :: Monad m => Iteratee [el] m [el]
- Bio.Iteratee.List: stream2stream :: (Monad m, Nullable s, Monoid s) => Iteratee s m s
- Bio.Iteratee.List: takeFromChunk :: Int -> Iteratee [el] m [el]
- Bio.Iteratee.List: takeStream :: Monad m => Int -> Enumeratee [el] [el] m a
- Bio.Iteratee.List: takeUpTo :: Monad m => Int -> Enumeratee [el] [el] m a
- Bio.Iteratee.List: takeWhileE :: (el -> Bool) -> Enumeratee [el] [el] m a
- Bio.Iteratee.List: tryHead :: Iteratee [el] m (Maybe el)
- Bio.Iteratee.List: zipStreams :: Monad m => Iteratee [el] m a -> Iteratee [el] m b -> Iteratee [el] m (a, b)
- Bio.Iteratee.List: zipStreams3 :: Monad m => Iteratee [el] m a -> Iteratee [el] m b -> Iteratee [el] m c -> Iteratee [el] m (a, b, c)
- Bio.Iteratee.List: zipStreams4 :: Monad m => Iteratee [el] m a -> Iteratee [el] m b -> Iteratee [el] m c -> Iteratee [el] m d -> Iteratee [el] m (a, b, c, d)
- Bio.Iteratee.List: zipStreams5 :: Monad m => Iteratee [el] m a -> Iteratee [el] m b -> Iteratee [el] m c -> Iteratee [el] m d -> Iteratee [el] m e -> Iteratee [el] m (a, b, c, d, e)
- Bio.Iteratee.ZLib: BestCompression :: CompressionLevel
- Bio.Iteratee.ZLib: BestSpeed :: CompressionLevel
- Bio.Iteratee.ZLib: BufferError :: ZLibException
- Bio.Iteratee.ZLib: CompressParams :: !CompressionLevel -> !Method -> !WindowBits -> !MemoryLevel -> !CompressionStrategy -> !Int -> !Maybe ByteString -> CompressParams
- Bio.Iteratee.ZLib: CompressionLevel :: Int -> CompressionLevel
- Bio.Iteratee.ZLib: DataError :: ZLibException
- Bio.Iteratee.ZLib: DecompressParams :: !WindowBits -> !Int -> !Maybe ByteString -> DecompressParams
- Bio.Iteratee.ZLib: DefaultCompression :: CompressionLevel
- Bio.Iteratee.ZLib: DefaultMemoryLevel :: MemoryLevel
- Bio.Iteratee.ZLib: DefaultStrategy :: CompressionStrategy
- Bio.Iteratee.ZLib: DefaultWindowBits :: WindowBits
- Bio.Iteratee.ZLib: Deflated :: Method
- Bio.Iteratee.ZLib: Filtered :: CompressionStrategy
- Bio.Iteratee.ZLib: GZip :: Format
- Bio.Iteratee.ZLib: GZipOrZlib :: Format
- Bio.Iteratee.ZLib: HuffmanOnly :: CompressionStrategy
- Bio.Iteratee.ZLib: IncorrectCompressionLevel :: !Int -> ZLibParamsException
- Bio.Iteratee.ZLib: IncorrectMemoryLevel :: !Int -> ZLibParamsException
- Bio.Iteratee.ZLib: IncorrectState :: ZLibException
- Bio.Iteratee.ZLib: IncorrectWindowBits :: !Int -> ZLibParamsException
- Bio.Iteratee.ZLib: MaxMemoryLevel :: MemoryLevel
- Bio.Iteratee.ZLib: MemoryError :: ZLibException
- Bio.Iteratee.ZLib: MemoryLevel :: Int -> MemoryLevel
- Bio.Iteratee.ZLib: MinMemoryLevel :: MemoryLevel
- Bio.Iteratee.ZLib: NeedDictionary :: ZLibException
- Bio.Iteratee.ZLib: NoCompression :: CompressionLevel
- Bio.Iteratee.ZLib: Raw :: Format
- Bio.Iteratee.ZLib: StreamError :: ZLibException
- Bio.Iteratee.ZLib: Unexpected :: !CInt -> ZLibException
- Bio.Iteratee.ZLib: VersionError :: ZLibException
- Bio.Iteratee.ZLib: WindowBits :: Int -> WindowBits
- Bio.Iteratee.ZLib: Zlib :: Format
- Bio.Iteratee.ZLib: [compressBufferSize] :: CompressParams -> !Int
- Bio.Iteratee.ZLib: [compressDictionary] :: CompressParams -> !Maybe ByteString
- Bio.Iteratee.ZLib: [compressLevel] :: CompressParams -> !CompressionLevel
- Bio.Iteratee.ZLib: [compressMemoryLevel] :: CompressParams -> !MemoryLevel
- Bio.Iteratee.ZLib: [compressMethod] :: CompressParams -> !Method
- Bio.Iteratee.ZLib: [compressStrategy] :: CompressParams -> !CompressionStrategy
- Bio.Iteratee.ZLib: [compressWindowBits] :: CompressParams -> !WindowBits
- Bio.Iteratee.ZLib: [decompressBufferSize] :: DecompressParams -> !Int
- Bio.Iteratee.ZLib: [decompressDictionary] :: DecompressParams -> !Maybe ByteString
- Bio.Iteratee.ZLib: [decompressWindowBits] :: DecompressParams -> !WindowBits
- Bio.Iteratee.ZLib: data CompressParams
- Bio.Iteratee.ZLib: data CompressionLevel
- Bio.Iteratee.ZLib: data CompressionStrategy
- Bio.Iteratee.ZLib: data DecompressParams
- Bio.Iteratee.ZLib: data Format
- Bio.Iteratee.ZLib: data MemoryLevel
- Bio.Iteratee.ZLib: data Method
- Bio.Iteratee.ZLib: data WindowBits
- Bio.Iteratee.ZLib: data ZLibException
- Bio.Iteratee.ZLib: data ZLibParamsException
- Bio.Iteratee.ZLib: defaultCompressParams :: CompressParams
- Bio.Iteratee.ZLib: defaultDecompressParams :: DecompressParams
- Bio.Iteratee.ZLib: enumBlockFlush :: Monad m => Enumerator ByteString m a
- Bio.Iteratee.ZLib: enumDeflate :: MonadIO m => Format -> CompressParams -> Enumeratee ByteString ByteString m a
- Bio.Iteratee.ZLib: enumFullFlush :: Monad m => Enumerator ByteString m a
- Bio.Iteratee.ZLib: enumInflate :: MonadIO m => Format -> DecompressParams -> Enumeratee ByteString ByteString m a
- Bio.Iteratee.ZLib: enumInflateAny :: MonadIO m => Enumeratee ByteString ByteString m a
- Bio.Iteratee.ZLib: enumSyncFlush :: Monad m => Enumerator ByteString m a
- Bio.Iteratee.ZLib: instance GHC.Classes.Eq Bio.Iteratee.ZLib.Format
- Bio.Iteratee.ZLib: instance GHC.Classes.Eq Bio.Iteratee.ZLib.ZLibException
- Bio.Iteratee.ZLib: instance GHC.Classes.Eq Bio.Iteratee.ZLib.ZLibParamsException
- Bio.Iteratee.ZLib: instance GHC.Classes.Eq Bio.Iteratee.ZLib.ZlibFlush
- Bio.Iteratee.ZLib: instance GHC.Exception.Type.Exception Bio.Iteratee.ZLib.ZLibException
- Bio.Iteratee.ZLib: instance GHC.Exception.Type.Exception Bio.Iteratee.ZLib.ZLibParamsException
- Bio.Iteratee.ZLib: instance GHC.Exception.Type.Exception Bio.Iteratee.ZLib.ZlibFlush
- Bio.Iteratee.ZLib: instance GHC.Show.Show Bio.Iteratee.ZLib.ZLibException
- Bio.Iteratee.ZLib: instance GHC.Show.Show Bio.Iteratee.ZLib.ZLibParamsException
- Bio.Iteratee.ZLib: instance GHC.Show.Show Bio.Iteratee.ZLib.ZlibFlush
- Bio.Prelude: (:!:) :: !a -> !b -> Pair a b
- Bio.Prelude: [Handler] :: forall a e. Exception e => (e -> IO a) -> Handler a
- Bio.Prelude: bracket :: () => IO a -> (a -> IO b) -> (a -> IO c) -> IO c
- Bio.Prelude: bracketOnError :: () => IO a -> (a -> IO b) -> (a -> IO c) -> IO c
- Bio.Prelude: bracket_ :: () => IO a -> IO b -> IO c -> IO c
- Bio.Prelude: catch :: Exception e => IO a -> (e -> IO a) -> IO a
- Bio.Prelude: catchIOError :: () => IO a -> (IOError -> IO a) -> IO a
- Bio.Prelude: catchJust :: Exception e => (e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
- Bio.Prelude: catches :: () => IO a -> [Handler a] -> IO a
- Bio.Prelude: class Unpack s
- Bio.Prelude: data Handler a
- Bio.Prelude: data Pair a b
- Bio.Prelude: decodeBytes :: Bytes -> Text
- Bio.Prelude: encodeBytes :: Text -> Bytes
- Bio.Prelude: fdGet :: Int -> Fd -> IO Bytes
- Bio.Prelude: fdPut :: Fd -> Bytes -> IO ()
- Bio.Prelude: fdPutLazy :: Fd -> LazyBytes -> IO ()
- Bio.Prelude: finally :: () => IO a -> IO b -> IO a
- Bio.Prelude: handle :: Exception e => (e -> IO a) -> IO a -> IO a
- Bio.Prelude: handleJust :: Exception e => (e -> Maybe b) -> (b -> IO a) -> IO a -> IO a
- Bio.Prelude: infixl 2 :!:
- Bio.Prelude: instance (GHC.Arr.Ix a, GHC.Arr.Ix b) => GHC.Arr.Ix (Bio.Prelude.Pair a b)
- Bio.Prelude: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Bio.Prelude.Pair a b)
- Bio.Prelude: instance (GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (Bio.Prelude.Pair a b)
- Bio.Prelude: instance (GHC.Enum.Bounded a, GHC.Enum.Bounded b) => GHC.Enum.Bounded (Bio.Prelude.Pair a b)
- Bio.Prelude: instance (GHC.Read.Read a, GHC.Read.Read b) => GHC.Read.Read (Bio.Prelude.Pair a b)
- Bio.Prelude: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Bio.Prelude.Pair a b)
- Bio.Prelude: instance Bio.Prelude.Unpack Data.ByteString.Internal.ByteString
- Bio.Prelude: instance Bio.Prelude.Unpack Data.Text.Internal.Text
- Bio.Prelude: instance Bio.Prelude.Unpack GHC.Base.String
- Bio.Prelude: mask :: () => ((forall a. () => IO a -> IO a) -> IO b) -> IO b
- Bio.Prelude: mask_ :: () => IO a -> IO a
- Bio.Prelude: onException :: () => IO a -> IO b -> IO a
- Bio.Prelude: try :: Exception e => IO a -> IO (Either e a)
- Bio.Prelude: tryJust :: Exception e => (e -> Maybe b) -> IO a -> IO (Either b a)
- Bio.Prelude: uninterruptibleMask :: () => ((forall a. () => IO a -> IO a) -> IO b) -> IO b
- Bio.Prelude: uninterruptibleMask_ :: () => IO a -> IO a
- Bio.Prelude: unpack :: Unpack s => s -> String
- Bio.Prelude: withFd :: FilePath -> OpenMode -> Maybe FileMode -> OpenFileFlags -> (Fd -> IO a) -> IO a
- Bio.Util.MMap: c_mmap :: CSize -> CInt -> IO (Ptr Word8)
- Bio.Util.MMap: c_munmap :: FunPtr (Ptr () -> Ptr Word8 -> IO ())
- Bio.Util.Zlib: decompressGzip :: ByteString -> ByteString
+ Bio.Adna: instance GHC.Base.Monoid Bio.Adna.DmgStats
+ Bio.Adna: instance GHC.Base.Semigroup Bio.Adna.DmgStats
+ Bio.Adna: instance GHC.Show.Show Bio.Adna.DmgStats
+ Bio.Bam.Fastq: parseFastqWith :: Monad m => (Bytes -> BamRec -> BamRec) -> ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
+ Bio.Bam.Header: Refs :: Vector BamSQ -> Refs
+ Bio.Bam.Header: [meta_pgs] :: BamMeta -> [Fix BamPG]
+ Bio.Bam.Header: [unRefs] :: Refs -> Vector BamSQ
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable Bio.Bam.Header.BamKey
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable Bio.Bam.Header.BamSQ
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable1 Bio.Bam.Header.BamPG
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable1 f => Data.Hashable.Class.Hashable (Bio.Bam.Header.Fix f)
+ Bio.Bam.Header: instance GHC.Base.Monoid Bio.Bam.Header.Refs
+ Bio.Bam.Header: instance GHC.Base.Semigroup Bio.Bam.Header.BamSorting
+ Bio.Bam.Header: instance GHC.Base.Semigroup Bio.Bam.Header.Refs
+ Bio.Bam.Header: instance GHC.Classes.Eq (f (Bio.Bam.Header.Fix f)) => GHC.Classes.Eq (Bio.Bam.Header.Fix f)
+ Bio.Bam.Header: instance GHC.Classes.Eq pp => GHC.Classes.Eq (Bio.Bam.Header.BamPG pp)
+ Bio.Bam.Header: instance GHC.Enum.Bounded Bio.Bam.Header.Refseq
+ Bio.Bam.Header: instance GHC.Generics.Generic Bio.Bam.Header.BamKey
+ Bio.Bam.Header: instance GHC.Generics.Generic Bio.Bam.Header.BamMeta
+ Bio.Bam.Header: instance GHC.Generics.Generic Bio.Bam.Header.BamSQ
+ Bio.Bam.Header: instance GHC.Generics.Generic1 Bio.Bam.Header.BamPG
+ Bio.Bam.Header: instance GHC.Show.Show (f (Bio.Bam.Header.Fix f)) => GHC.Show.Show (Bio.Bam.Header.Fix f)
+ Bio.Bam.Header: instance GHC.Show.Show Bio.Bam.Header.Refs
+ Bio.Bam.Header: instance GHC.Show.Show pp => GHC.Show.Show (Bio.Bam.Header.BamPG pp)
+ Bio.Bam.Header: newtype Refs
+ Bio.Bam.Index: instance GHC.Exception.Type.Exception Bio.Bam.Index.IndexFormatError
+ Bio.Bam.Index: instance GHC.Show.Show Bio.Bam.Index.IndexFormatError
+ Bio.Bam.Index: streamBamRefseq :: MonadIO m => BamIndex b -> Handle -> Refseq -> Stream (Of BamRaw) m ()
+ Bio.Bam.Index: streamBamRegions :: MonadIO m => BamIndex b -> Handle -> [Region] -> Stream (Of BamRaw) m ()
+ Bio.Bam.Index: streamBamSubseq :: MonadIO m => BamIndex b -> Handle -> Refseq -> Subsequence -> Stream (Of BamRaw) m () -> Stream (Of BamRaw) m (Stream (Of BamRaw) m ())
+ Bio.Bam.Index: streamBamUnaligned :: MonadIO m => BamIndex b -> Handle -> Stream (Of BamRaw) m ()
+ Bio.Bam.Index: withIndexedBam :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> BamIndex () -> Handle -> m r) -> m r
+ Bio.Bam.Pileup: dissect :: DmgToken -> BamRaw -> [PosPrimChunks]
+ Bio.Bam.Reader: coordinates :: BamRaw -> (Refseq, Int)
+ Bio.Bam.Reader: decodeBamFile :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
+ Bio.Bam.Reader: decodeBamFiles :: (MonadMask m, MonadIO m) => [FilePath] -> ([(BamMeta, Stream (Of BamRaw) m ())] -> m r) -> m r
+ Bio.Bam.Reader: decodePlainBam :: MonadIO m => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
+ Bio.Bam.Reader: decodePlainSam :: MonadIO m => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
+ Bio.Bam.Reader: getBamMeta :: Monad m => Parser r m BamMeta
+ Bio.Bam.Reader: getSamRec :: (Bytes -> Refseq) -> Bytes -> Either ParseError BamRec
+ Bio.Bam.Reader: guardRefCompat :: MonadThrow m => (FilePath, BamMeta) -> (FilePath, BamMeta) -> m ()
+ Bio.Bam.Reader: instance GHC.Exception.Type.Exception Bio.Bam.Reader.IncompatibleRefs
+ Bio.Bam.Reader: instance GHC.Exception.Type.Exception Bio.Bam.Reader.ShortRecord
+ Bio.Bam.Reader: instance GHC.Show.Show Bio.Bam.Reader.IncompatibleRefs
+ Bio.Bam.Reader: instance GHC.Show.Show Bio.Bam.Reader.ShortRecord
+ Bio.Bam.Reader: mergeInputsOn :: (Ord x, MonadIO m, MonadMask m) => (BamRaw -> x) -> [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
+ Bio.Bam.Reader: qnames :: BamRaw -> Bytes
+ Bio.Bam.Writer: unpackBamRec :: IsBamRec a => a -> BamRec
+ Bio.Base: (:!:) :: !a -> !b -> Pair a b
+ Bio.Base: data Pair a b
+ Bio.Base: infixl 2 :!:
+ Bio.Base: instance (GHC.Arr.Ix a, GHC.Arr.Ix b) => GHC.Arr.Ix (Bio.Base.Pair a b)
+ Bio.Base: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Bio.Base.Pair a b)
+ Bio.Base: instance (GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (Bio.Base.Pair a b)
+ Bio.Base: instance (GHC.Enum.Bounded a, GHC.Enum.Bounded b) => GHC.Enum.Bounded (Bio.Base.Pair a b)
+ Bio.Base: instance (GHC.Read.Read a, GHC.Read.Read b) => GHC.Read.Read (Bio.Base.Pair a b)
+ Bio.Base: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Bio.Base.Pair a b)
+ Bio.Prelude: -- | Generic representation type
+ Bio.Prelude: AbsoluteSeek :: SeekMode
+ Bio.Prelude: AppendMode :: IOMode
+ Bio.Prelude: ReadMode :: IOMode
+ Bio.Prelude: ReadWriteMode :: IOMode
+ Bio.Prelude: RelativeSeek :: SeekMode
+ Bio.Prelude: SeekFromEnd :: SeekMode
+ Bio.Prelude: WriteMode :: IOMode
+ Bio.Prelude: class Generic1 (f :: k -> Type) where {
+ Bio.Prelude: class Hashable1 (t :: Type -> Type)
+ Bio.Prelude: class Hashable2 (t :: Type -> Type -> Type)
+ Bio.Prelude: data IOMode
+ Bio.Prelude: data SeekMode
+ Bio.Prelude: from1 :: Generic1 f => f a -> Rep1 f a
+ Bio.Prelude: hFlush :: Handle -> IO ()
+ Bio.Prelude: hSeek :: Handle -> SeekMode -> Integer -> IO ()
+ Bio.Prelude: liftHashWithSalt :: Hashable1 t => (Int -> a -> Int) -> Int -> t a -> Int
+ Bio.Prelude: liftHashWithSalt2 :: Hashable2 t => (Int -> a -> Int) -> (Int -> b -> Int) -> Int -> t a b -> Int
+ Bio.Prelude: openBinaryFile :: FilePath -> IOMode -> IO Handle
+ Bio.Prelude: to1 :: Generic1 f => Rep1 f a -> f a
+ Bio.Prelude: type family Rep1 (f :: k -> Type) :: k -> Type;
+ Bio.Prelude: withBinaryFile :: () => FilePath -> IOMode -> (Handle -> IO r) -> IO r
+ Bio.Prelude: }
+ Bio.Streaming: (:>) :: !a -> b -> Of a b
+ Bio.Streaming: (<|>) :: Alternative f => f a -> f a -> f a
+ Bio.Streaming: Compose :: f (g a) -> Compose
+ Bio.Streaming: Identity :: a -> Identity a
+ Bio.Streaming: InL :: f a -> Sum
+ Bio.Streaming: InR :: g a -> Sum
+ Bio.Streaming: [getCompose] :: Compose -> f (g a)
+ Bio.Streaming: [runIdentity] :: Identity a -> a
+ Bio.Streaming: bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
+ Bio.Streaming: chunksOf :: (Monad m, Functor f) => Int -> Stream f m r -> Stream (Stream f m) m r
+ Bio.Streaming: class Applicative f => Alternative (f :: Type -> Type)
+ Bio.Streaming: class Bifunctor (p :: Type -> Type -> Type)
+ Bio.Streaming: class MFunctor (t :: Type -> Type -> k -> Type)
+ Bio.Streaming: class (MFunctor t, MonadTrans t) => MMonad (t :: Type -> Type -> Type -> Type)
+ Bio.Streaming: class Monad m => MonadIO (m :: Type -> Type)
+ Bio.Streaming: class MonadCatch m => MonadMask (m :: Type -> Type)
+ Bio.Streaming: class MonadTrans (t :: Type -> Type -> Type -> Type)
+ Bio.Streaming: concats :: (Monad m, Functor f) => Stream (Stream f m) m r -> Stream f m r
+ Bio.Streaming: cutoff :: (Monad m, Functor f) => Int -> Stream f m r -> Stream f m (Maybe r)
+ Bio.Streaming: data ByteStream m r
+ Bio.Streaming: data Of a b
+ Bio.Streaming: data Stream (f :: Type -> Type) (m :: Type -> Type) r
+ Bio.Streaming: data Sum (f :: k -> Type) (g :: k -> Type) (a :: k) :: forall k. () => k -> Type -> k -> Type -> k -> Type
+ Bio.Streaming: decompose :: (Monad m, Functor f) => Stream (Compose m f) m r -> Stream f m r
+ Bio.Streaming: delays :: (MonadIO m, Applicative f) => Double -> Stream f m r
+ Bio.Streaming: destroy :: (Functor f, Monad m) => Stream f m r -> (f b -> b) -> (m b -> b) -> (r -> b) -> b
+ Bio.Streaming: distribute :: (Monad m, Functor f, MonadTrans t, MFunctor t, Monad (t (Stream f m))) => Stream f (t m) r -> t (Stream f m) r
+ Bio.Streaming: each :: (Monad m, Foldable f) => f a -> Stream (Of a) m ()
+ Bio.Streaming: effect :: (Monad m, Functor f) => m (Stream f m r) -> Stream f m r
+ Bio.Streaming: embed :: (MMonad t, Monad n) => (forall a. () => m a -> t n a) -> t m b -> t n b
+ Bio.Streaming: expand :: (Monad m, Functor f) => (forall a b. () => (g a -> b) -> f a -> h b) -> Stream f m r -> Stream g (Stream h m) r
+ Bio.Streaming: expandPost :: (Monad m, Functor g) => (forall a b. () => (g a -> b) -> f a -> h b) -> Stream f m r -> Stream g (Stream h m) r
+ Bio.Streaming: first :: Bifunctor p => (a -> b) -> p a c -> p b c
+ Bio.Streaming: groups :: (Monad m, Functor f, Functor g) => Stream (Sum f g) m r -> Stream (Sum (Stream f m) (Stream g m)) m r
+ Bio.Streaming: hoist :: (MFunctor t, Monad m) => (forall a. () => m a -> n a) -> t m b -> t n b
+ Bio.Streaming: hoistUnexposed :: (Monad m, Functor f) => (forall a. () => m a -> n a) -> Stream f m r -> Stream f n r
+ Bio.Streaming: infixl 3 <|>
+ Bio.Streaming: infixr 9 `Compose`
+ Bio.Streaming: inspect :: Monad m => Stream f m r -> m (Either r (f (Stream f m r)))
+ Bio.Streaming: intercalates :: (Monad m, Monad (t m), MonadTrans t) => t m x -> Stream (t m) m r -> t m r
+ Bio.Streaming: interleaves :: (Monad m, Applicative h) => Stream h m r -> Stream h m r -> Stream h m r
+ Bio.Streaming: iterT :: (Functor f, Monad m) => (f (m a) -> m a) -> Stream f m a -> m a
+ Bio.Streaming: iterTM :: (Functor f, Monad m, MonadTrans t, Monad (t m)) => (f (t m a) -> t m a) -> Stream f m a -> t m a
+ Bio.Streaming: join :: Monad m => m (m a) -> m a
+ Bio.Streaming: lazily :: () => Of a b -> (a, b)
+ Bio.Streaming: lift :: (MonadTrans t, Monad m) => m a -> t m a
+ Bio.Streaming: liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
+ Bio.Streaming: liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
+ Bio.Streaming: liftIO :: MonadIO m => IO a -> m a
+ Bio.Streaming: liftM :: Monad m => (a1 -> r) -> m a1 -> m r
+ Bio.Streaming: liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
+ Bio.Streaming: mapped :: (Monad m, Functor f) => (forall x. () => f x -> m (g x)) -> Stream f m r -> Stream g m r
+ Bio.Streaming: mappedPost :: (Monad m, Functor g) => (forall x. () => f x -> m (g x)) -> Stream f m r -> Stream g m r
+ Bio.Streaming: maps :: (Monad m, Functor f) => (forall x. () => f x -> g x) -> Stream f m r -> Stream g m r
+ Bio.Streaming: mapsM :: (Monad m, Functor f) => (forall x. () => f x -> m (g x)) -> Stream f m r -> Stream g m r
+ Bio.Streaming: mapsMPost :: (Monad m, Functor g) => (forall x. () => f x -> m (g x)) -> Stream f m r -> Stream g m r
+ Bio.Streaming: mapsM_ :: (Functor f, Monad m) => (forall x. () => f x -> m x) -> Stream f m r -> m r
+ Bio.Streaming: mapsPost :: (Monad m, Functor g) => (forall x. () => f x -> g x) -> Stream f m r -> Stream g m r
+ Bio.Streaming: mergeStreams :: (Monad m, Ord a) => Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)
+ Bio.Streaming: mergeStreamsBy :: Monad m => (a -> a -> Ordering) -> Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)
+ Bio.Streaming: mergeStreamsOn :: (Monad m, Ord b) => (a -> b) -> Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)
+ Bio.Streaming: never :: (Monad m, Applicative f) => Stream f m r
+ Bio.Streaming: newtype Identity a
+ Bio.Streaming: newtype Compose (f :: k -> Type) (g :: k1 -> k) (a :: k1) :: forall k k1. () => k -> Type -> k1 -> k -> k1 -> Type
+ Bio.Streaming: progressGen :: MonadIO m => (Int -> a -> String) -> Int -> (String -> IO ()) -> Stream (Of a) m r -> Stream (Of a) m r
+ Bio.Streaming: progressNum :: MonadIO m => String -> Int -> (String -> IO ()) -> Stream (Of a) m r -> Stream (Of a) m r
+ Bio.Streaming: progressPos :: MonadIO m => (a -> (Refseq, Int)) -> String -> Refs -> Int -> (String -> IO ()) -> Stream (Of a) m r -> Stream (Of a) m r
+ Bio.Streaming: protectTerm :: (Functor f, MonadIO m) => Stream f m r -> Stream f m r
+ Bio.Streaming: psequence :: MonadIO m => Int -> Stream (Of (IO a)) m b -> Stream (Of a) m b
+ Bio.Streaming: repeats :: (Monad m, Functor f) => f () -> Stream f m r
+ Bio.Streaming: repeatsM :: (Monad m, Functor f) => m (f ()) -> Stream f m r
+ Bio.Streaming: replicates :: (Monad m, Functor f) => Int -> f () -> Stream f m ()
+ Bio.Streaming: run :: Monad m => Stream m m r -> m r
+ Bio.Streaming: second :: Bifunctor p => (b -> c) -> p a b -> p a c
+ Bio.Streaming: separate :: (Monad m, Functor f, Functor g) => Stream (Sum f g) m r -> Stream f (Stream g m) r
+ Bio.Streaming: splitsAt :: (Monad m, Functor f) => Int -> Stream f m r -> Stream f m (Stream f m r)
+ Bio.Streaming: streamBuild :: () => (forall b. () => (r -> b) -> (m b -> b) -> (f b -> b) -> b) -> Stream f m r
+ Bio.Streaming: streamFile :: (MonadIO m, MonadMask m) => FilePath -> (ByteStream m () -> m r) -> m r
+ Bio.Streaming: streamFold :: (Functor f, Monad m) => (r -> b) -> (m b -> b) -> (f b -> b) -> Stream f m r -> b
+ Bio.Streaming: streamHandle :: MonadIO m => Handle -> ByteStream m ()
+ Bio.Streaming: streamInput :: (MonadIO m, MonadMask m) => FilePath -> (ByteStream m () -> m r) -> m r
+ Bio.Streaming: streamInputs :: MonadIO m => [FilePath] -> (Stream (ByteStream m) m () -> r) -> r
+ Bio.Streaming: strictly :: () => (a, b) -> Of a b
+ Bio.Streaming: takes :: (Monad m, Functor f) => Int -> Stream f m r -> Stream f m ()
+ Bio.Streaming: unfold :: (Monad m, Functor f) => (s -> m (Either r (f s))) -> s -> Stream f m r
+ Bio.Streaming: unseparate :: (Monad m, Functor f, Functor g) => Stream f (Stream g m) r -> Stream (Sum f g) m r
+ Bio.Streaming: untilJust :: (Monad m, Applicative f) => m (Maybe r) -> Stream f m r
+ Bio.Streaming: unzips :: (Monad m, Functor f, Functor g) => Stream (Compose f g) m r -> Stream f (Stream g m) r
+ Bio.Streaming: void :: Functor f => f a -> f ()
+ Bio.Streaming: withOutputFile :: (MonadIO m, MonadMask m) => FilePath -> (Handle -> m a) -> m a
+ Bio.Streaming: wrap :: (Monad m, Functor f) => f (Stream f m r) -> Stream f m r
+ Bio.Streaming: yields :: (Monad m, Functor f) => f r -> Stream f m r
+ Bio.Streaming: zips :: (Monad m, Functor f, Functor g) => Stream f m r -> Stream g m r -> Stream (Compose f g) m r
+ Bio.Streaming: zipsWith :: (Monad m, Functor h) => (forall x y. () => f x -> g y -> h (x, y)) -> Stream f m r -> Stream g m r -> Stream h m r
+ Bio.Streaming: zipsWith' :: Monad m => (forall x y p. () => (x -> y -> p) -> f x -> g y -> h p) -> Stream f m r -> Stream g m r -> Stream h m r
+ Bio.Streaming.Bgzf: BB :: {-# UNPACK #-} !ForeignPtr Word8 -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> BB
+ Bio.Streaming.Bgzf: BclArgs :: BclSpecialType -> {-# UNPACK #-} !Vector Word8 -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> BclArgs
+ Bio.Streaming.Bgzf: BclNucsAsc :: BclSpecialType
+ Bio.Streaming.Bgzf: BclNucsAscRev :: BclSpecialType
+ Bio.Streaming.Bgzf: BclNucsBin :: BclSpecialType
+ Bio.Streaming.Bgzf: BclNucsWide :: BclSpecialType
+ Bio.Streaming.Bgzf: BclQualsAsc :: BclSpecialType
+ Bio.Streaming.Bgzf: BclQualsAscRev :: BclSpecialType
+ Bio.Streaming.Bgzf: BclQualsBin :: BclSpecialType
+ Bio.Streaming.Bgzf: TkBclSpecial :: !BclArgs -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkDecimal :: {-# UNPACK #-} !Int -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkDouble :: {-# UNPACK #-} !Double -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkEnd :: BgzfTokens
+ Bio.Streaming.Bgzf: TkEndRecord :: BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkEndRecordPart1 :: BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkEndRecordPart2 :: BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkFloat :: {-# UNPACK #-} !Float -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkLowLevel :: {-# UNPACK #-} !Int -> (BB -> IO BB) -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkSetMark :: BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkString :: {-# UNPACK #-} !ByteString -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkWord16 :: {-# UNPACK #-} !Word16 -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkWord32 :: {-# UNPACK #-} !Word32 -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkWord8 :: {-# UNPACK #-} !Word8 -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: [buffer] :: BB -> {-# UNPACK #-} !ForeignPtr Word8
+ Bio.Streaming.Bgzf: [mark2] :: BB -> {-# UNPACK #-} !Int
+ Bio.Streaming.Bgzf: [mark] :: BB -> {-# UNPACK #-} !Int
+ Bio.Streaming.Bgzf: [off] :: BB -> {-# UNPACK #-} !Int
+ Bio.Streaming.Bgzf: [size] :: BB -> {-# UNPACK #-} !Int
+ Bio.Streaming.Bgzf: [used] :: BB -> {-# UNPACK #-} !Int
+ Bio.Streaming.Bgzf: bgunzip :: MonadIO m => ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bgzf: data BB
+ Bio.Streaming.Bgzf: data BclArgs
+ Bio.Streaming.Bgzf: data BclSpecialType
+ Bio.Streaming.Bgzf: data BgzfTokens
+ Bio.Streaming.Bgzf: encodeBgzf :: MonadIO m => Int -> Stream (Of (Endo BgzfTokens)) m b -> ByteStream m b
+ Bio.Streaming.Bgzf: expandBuffer :: Int -> BB -> IO BB
+ Bio.Streaming.Bgzf: fillBuffer :: BB -> BgzfTokens -> IO (BB, BgzfTokens)
+ Bio.Streaming.Bgzf: getBgzfHdr :: Monad m => ByteStream m r -> m (Maybe Int, ByteString, ByteStream m r)
+ Bio.Streaming.Bgzf: loop_bcl_special :: Ptr Word8 -> BclArgs -> IO Int
+ Bio.Streaming.Bgzf: loop_dec_int :: Ptr Word8 -> Int -> IO Int
+ Bio.Streaming.Bgzf: newBuffer :: Int -> IO BB
+ Bio.Streaming.Bytes: Chunk :: {-# UNPACK #-} !Bytes -> {-# UNPACK #-} !Int64 -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: Empty :: r -> ByteStream m r
+ Bio.Streaming.Bytes: Go :: m (ByteStream m r) -> ByteStream m r
+ Bio.Streaming.Bytes: break :: Monad m => (Word8 -> Bool) -> ByteStream m r -> ByteStream m (ByteStream m r)
+ Bio.Streaming.Bytes: chunk :: Bytes -> ByteStream m ()
+ Bio.Streaming.Bytes: concat :: Monad m => Stream (ByteStream m) m r -> ByteStream m r
+ Bio.Streaming.Bytes: concatBuilders :: Stream (Of Builder) IO () -> Builder
+ Bio.Streaming.Bytes: cons :: Word8 -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: consChunk :: Bytes -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: consChunkOff :: Bytes -> Int64 -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: copy :: Monad m => ByteStream m r -> ByteStream (ByteStream m) r
+ Bio.Streaming.Bytes: data ByteStream m r
+ Bio.Streaming.Bytes: drop :: Monad m => Int64 -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: dropWhile :: Monad m => (Word8 -> Bool) -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: effects :: Monad m => ByteStream m r -> m r
+ Bio.Streaming.Bytes: empty :: ByteStream m ()
+ Bio.Streaming.Bytes: fromChunks :: Monad m => Stream (Of Bytes) m r -> ByteStream m r
+ Bio.Streaming.Bytes: fromLazy :: LazyBytes -> ByteStream m ()
+ Bio.Streaming.Bytes: gunzip :: MonadIO m => ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: gunzipWith :: MonadIO m => (ByteStream m r -> ByteStream m r) -> ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: gzip :: MonadIO m => ByteStream m r -> ByteStream m r
+ Bio.Streaming.Bytes: hGetContents :: MonadIO m => Handle -> ByteStream m ()
+ Bio.Streaming.Bytes: hGetContentsN :: MonadIO m => Int -> Handle -> ByteStream m ()
+ Bio.Streaming.Bytes: hPut :: MonadIO m => Handle -> ByteStream m r -> m r
+ Bio.Streaming.Bytes: instance (GHC.Base.Semigroup r, GHC.Base.Monad m) => GHC.Base.Semigroup (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance (GHC.Base.Semigroup r, GHC.Base.Monoid r, GHC.Base.Monad m) => GHC.Base.Monoid (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance (m Data.Type.Equality.~ Data.Functor.Identity.Identity, GHC.Show.Show r) => GHC.Show.Show (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance (r Data.Type.Equality.~ ()) => Data.String.IsString (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Bio.Streaming.Bytes.ByteStream m)
+ Bio.Streaming.Bytes: instance Control.Monad.Trans.Class.MonadTrans Bio.Streaming.Bytes.ByteStream
+ Bio.Streaming.Bytes: instance GHC.Base.Monad m => GHC.Base.Applicative (Bio.Streaming.Bytes.ByteStream m)
+ Bio.Streaming.Bytes: instance GHC.Base.Monad m => GHC.Base.Functor (Bio.Streaming.Bytes.ByteStream m)
+ Bio.Streaming.Bytes: instance GHC.Base.Monad m => GHC.Base.Monad (Bio.Streaming.Bytes.ByteStream m)
+ Bio.Streaming.Bytes: lines :: Monad m => ByteStream m r -> Stream (ByteStream m) m r
+ Bio.Streaming.Bytes: lines' :: Monad m => ByteStream m r -> Stream (Of Bytes) m r
+ Bio.Streaming.Bytes: mapChunksM_ :: Monad m => (Bytes -> m ()) -> ByteStream m r -> m r
+ Bio.Streaming.Bytes: mwrap :: m (ByteStream m r) -> ByteStream m r
+ Bio.Streaming.Bytes: nextByte :: Monad m => ByteStream m r -> m (Either r (Word8, ByteStream m r))
+ Bio.Streaming.Bytes: nextByteOff :: Monad m => ByteStream m r -> m (Either r (Word8, Int64, ByteStream m r))
+ Bio.Streaming.Bytes: nextChunk :: Monad m => ByteStream m r -> m (Either r (Bytes, ByteStream m r))
+ Bio.Streaming.Bytes: nextChunkOff :: Monad m => ByteStream m r -> m (Either r (Bytes, Int64, ByteStream m r))
+ Bio.Streaming.Bytes: singleton :: Word8 -> ByteStream m ()
+ Bio.Streaming.Bytes: splitAt :: Monad m => Int64 -> ByteStream m r -> ByteStream m (ByteStream m r)
+ Bio.Streaming.Bytes: splitAt' :: Monad m => Int -> ByteStream m r -> m (Of Bytes (ByteStream m r))
+ Bio.Streaming.Bytes: toByteStream :: MonadIO m => Builder -> ByteStream m ()
+ Bio.Streaming.Bytes: toByteStreamWith :: MonadIO m => AllocationStrategy -> Builder -> ByteStream m ()
+ Bio.Streaming.Bytes: toLazy :: Monad m => ByteStream m r -> m (Of LazyBytes r)
+ Bio.Streaming.Bytes: toStrict :: Monad m => ByteStream m r -> m (Of Bytes r)
+ Bio.Streaming.Bytes: trim :: Monad m => Int64 -> ByteStream m () -> ByteStream m ()
+ Bio.Streaming.Bytes: withOutputFile :: (MonadIO m, MonadMask m) => FilePath -> (Handle -> m a) -> m a
+ Bio.Streaming.Bytes: writeFile :: (MonadIO m, MonadMask m) => FilePath -> ByteStream m r -> m r
+ Bio.Streaming.Furrow: Furrow :: Stream ((->) (Maybe a)) m r -> Furrow a m r
+ Bio.Streaming.Furrow: afford :: Monad m => Furrow a m b -> a -> m (Furrow a m b)
+ Bio.Streaming.Furrow: drain :: Monad m => Furrow a m b -> m b
+ Bio.Streaming.Furrow: evertStream :: Monad m => (Stream (Of a) (Furrow a m) () -> Furrow a m b) -> Furrow a m b
+ Bio.Streaming.Furrow: instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Bio.Streaming.Furrow.Furrow a m)
+ Bio.Streaming.Furrow: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Bio.Streaming.Furrow.Furrow a m)
+ Bio.Streaming.Furrow: instance Control.Monad.Morph.MFunctor (Bio.Streaming.Furrow.Furrow a)
+ Bio.Streaming.Furrow: instance Control.Monad.Morph.MMonad (Bio.Streaming.Furrow.Furrow a)
+ Bio.Streaming.Furrow: instance Control.Monad.Trans.Class.MonadTrans (Bio.Streaming.Furrow.Furrow a)
+ Bio.Streaming.Furrow: instance GHC.Base.Monad m => GHC.Base.Applicative (Bio.Streaming.Furrow.Furrow a m)
+ Bio.Streaming.Furrow: instance GHC.Base.Monad m => GHC.Base.Functor (Bio.Streaming.Furrow.Furrow a m)
+ Bio.Streaming.Furrow: instance GHC.Base.Monad m => GHC.Base.Monad (Bio.Streaming.Furrow.Furrow a m)
+ Bio.Streaming.Furrow: newtype Furrow a m r
+ Bio.Streaming.Parse: EofException :: EofException
+ Bio.Streaming.Parse: ParseError :: [String] -> String -> ParseError
+ Bio.Streaming.Parse: [errorContexts] :: ParseError -> [String]
+ Bio.Streaming.Parse: [errorMessage] :: ParseError -> String
+ Bio.Streaming.Parse: abortParse :: Monad m => Parser r m a
+ Bio.Streaming.Parse: atto :: Monad m => Parser a -> Parser r m a
+ Bio.Streaming.Parse: data EofException
+ Bio.Streaming.Parse: data ParseError
+ Bio.Streaming.Parse: data Parser r m a
+ Bio.Streaming.Parse: drop :: Monad m => Int -> Parser r m ()
+ Bio.Streaming.Parse: dropLine :: Monad m => Parser r m ()
+ Bio.Streaming.Parse: getByte :: Monad m => Parser r m Word8
+ Bio.Streaming.Parse: getString :: Monad m => Int -> Parser r m ByteString
+ Bio.Streaming.Parse: getWord32 :: Monad m => Parser r m Word32
+ Bio.Streaming.Parse: getWord64 :: Monad m => Parser r m Word64
+ Bio.Streaming.Parse: instance Control.Monad.Catch.MonadThrow (Bio.Streaming.Parse.Parser r m)
+ Bio.Streaming.Parse: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Bio.Streaming.Parse.Parser r m)
+ Bio.Streaming.Parse: instance Control.Monad.Trans.Class.MonadTrans (Bio.Streaming.Parse.Parser r)
+ Bio.Streaming.Parse: instance GHC.Base.Applicative (Bio.Streaming.Parse.Parser r m)
+ Bio.Streaming.Parse: instance GHC.Base.Functor (Bio.Streaming.Parse.Parser r m)
+ Bio.Streaming.Parse: instance GHC.Base.Monad (Bio.Streaming.Parse.Parser r m)
+ Bio.Streaming.Parse: instance GHC.Exception.Type.Exception Bio.Streaming.Parse.EofException
+ Bio.Streaming.Parse: instance GHC.Exception.Type.Exception Bio.Streaming.Parse.ParseError
+ Bio.Streaming.Parse: instance GHC.Show.Show Bio.Streaming.Parse.EofException
+ Bio.Streaming.Parse: instance GHC.Show.Show Bio.Streaming.Parse.ParseError
+ Bio.Streaming.Parse: isFinished :: Monad m => Parser r m Bool
+ Bio.Streaming.Parse: isolate :: Monad m => Int -> Parser (ByteStream m r) m a -> Parser r m a
+ Bio.Streaming.Parse: parse :: Monad m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either SomeException (Either r (a, ByteStream m r)))
+ Bio.Streaming.Parse: parseIO :: MonadIO m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))
+ Bio.Streaming.Parse: parseM :: MonadThrow m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))
+ Bio.Streaming.Vector: stream2vector :: (MonadIO m, Vector v a) => Stream (Of a) m r -> m (Of (v a) r)
+ Bio.Streaming.Vector: stream2vectorN :: (MonadIO m, Vector v a) => Int -> Stream (Of a) m () -> m (v a)
+ Bio.Util.Nub: nubHash :: (Hashable a, Eq a) => [a] -> [a]
+ Bio.Util.Nub: nubHashBy :: (Hashable b, Eq b) => (a -> b) -> [a] -> [a]
+ Bio.Util.Text: c2w :: Char -> Word8
+ Bio.Util.Text: class Unpack s
+ Bio.Util.Text: decodeBytes :: ByteString -> Text
+ Bio.Util.Text: decompressGzip :: ByteString -> ByteString
+ Bio.Util.Text: encodeBytes :: Text -> ByteString
+ Bio.Util.Text: instance Bio.Util.Text.Unpack Data.ByteString.Internal.ByteString
+ Bio.Util.Text: instance Bio.Util.Text.Unpack Data.Text.Internal.Text
+ Bio.Util.Text: instance Bio.Util.Text.Unpack GHC.Base.String
+ Bio.Util.Text: unpack :: Unpack s => s -> String
+ Bio.Util.Text: w2c :: Word8 -> Char
- Bio.Adna: DmgStats :: CompositionStats -> CompositionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> a -> DmgStats a
+ Bio.Adna: DmgStats :: CompositionStats -> CompositionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> SubstitutionStats -> DmgStats
- Bio.Adna: [basecompo3] :: DmgStats a -> CompositionStats
+ Bio.Adna: [basecompo3] :: DmgStats -> CompositionStats
- Bio.Adna: [basecompo5] :: DmgStats a -> CompositionStats
+ Bio.Adna: [basecompo5] :: DmgStats -> CompositionStats
- Bio.Adna: [substs3] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs3] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs3cpg] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs3cpg] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs3d3] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs3d3] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs3d5] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs3d5] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs3dd] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs3dd] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs5] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs5] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs5cpg] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs5cpg] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs5d3] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs5d3] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs5d5] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs5d5] :: DmgStats -> SubstitutionStats
- Bio.Adna: [substs5dd] :: DmgStats a -> SubstitutionStats
+ Bio.Adna: [substs5dd] :: DmgStats -> SubstitutionStats
- Bio.Adna: addFragType :: BamMeta -> Enumeratee [BamRaw] [(BamRaw, FragType)] m b
+ Bio.Adna: addFragType :: Monad m => BamMeta -> Stream (Of BamRaw) m b -> Stream (Of (BamRaw, FragType)) m b
- Bio.Adna: damagePatternsIter :: MonadIO m => Int -> Int -> Iteratee [Alignment] m b -> Iteratee [(BamRec, FragType, Vector Word8, Vector NPair)] m (DmgStats b)
+ Bio.Adna: damagePatternsIter :: MonadIO m => Int -> Int -> Stream (Of (BamRec, FragType, Vector Word8, Vector NPair)) m x -> Stream (Of Alignment) m DmgStats
- Bio.Adna: damagePatternsIter2Bit :: MonadIO m => Refs -> TwoBitFile -> Int -> Int -> Iteratee [Alignment] m b -> Iteratee [(BamRaw, FragType)] m (DmgStats b)
+ Bio.Adna: damagePatternsIter2Bit :: MonadIO m => Refs -> TwoBitFile -> Int -> Int -> Stream (Of (BamRaw, FragType)) m x -> Stream (Of Alignment) m DmgStats
- Bio.Adna: damagePatternsIterMD :: MonadIO m => Int -> Iteratee [Alignment] m b -> Iteratee [(BamRaw, FragType)] m (DmgStats b)
+ Bio.Adna: damagePatternsIterMD :: MonadIO m => Int -> Stream (Of (BamRaw, FragType)) m x -> Stream (Of Alignment) m DmgStats
- Bio.Adna: data DmgStats a
+ Bio.Adna: data DmgStats
- Bio.Bam.Fastq: parseFastq :: Monad m => Enumeratee Bytes [BamRec] m a
+ Bio.Bam.Fastq: parseFastq :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
- Bio.Bam.Fastq: parseFastqCassava :: Monad m => Enumeratee Bytes [BamRec] m a
+ Bio.Bam.Fastq: parseFastqCassava :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
- Bio.Bam.Filter: filterPairs :: Monad m => (BamRec -> [BamRec]) -> (Maybe BamRec -> Maybe BamRec -> [BamRec]) -> Enumeratee [BamRec] [BamRec] m a
+ Bio.Bam.Filter: filterPairs :: Monad m => (BamRec -> [BamRec]) -> (Maybe BamRec -> Maybe BamRec -> [BamRec]) -> Stream (Of BamRec) m r -> Stream (Of BamRec) m r
- Bio.Bam.Header: BamHeader :: (Int, Int) -> !BamSorting -> BamOtherShit -> BamHeader
+ Bio.Bam.Header: BamHeader :: (Int, Int) -> BamSorting -> BamOtherShit -> BamHeader
- Bio.Bam.Header: BamMeta :: !BamHeader -> !Refs -> [(BamKey, BamOtherShit)] -> [Bytes] -> BamMeta
+ Bio.Bam.Header: BamMeta :: !BamHeader -> !Refs -> [Fix BamPG] -> [(BamKey, BamOtherShit)] -> [Bytes] -> BamMeta
- Bio.Bam.Header: BamSQ :: Seqid -> Int -> BamOtherShit -> BamSQ
+ Bio.Bam.Header: BamSQ :: Bytes -> Int -> BamOtherShit -> BamSQ
- Bio.Bam.Header: [hdr_sorting] :: BamHeader -> !BamSorting
+ Bio.Bam.Header: [hdr_sorting] :: BamHeader -> BamSorting
- Bio.Bam.Header: [sq_name] :: BamSQ -> Seqid
+ Bio.Bam.Header: [sq_name] :: BamSQ -> Bytes
- Bio.Bam.Header: compareNames :: Seqid -> Seqid -> Ordering
+ Bio.Bam.Header: compareNames :: Bytes -> Bytes -> Ordering
- Bio.Bam.Index: readBaiIndex :: MonadIO m => Iteratee Bytes m (BamIndex ())
+ Bio.Bam.Index: readBaiIndex :: MonadIO m => ByteStream m r -> m (BamIndex ())
- Bio.Bam.Index: readTabix :: MonadIO m => Iteratee Bytes m TabIndex
+ Bio.Bam.Index: readTabix :: MonadIO m => ByteStream m r -> m TabIndex
- Bio.Bam.Pileup: pileup :: Enumeratee [PosPrimChunks] [Pile] IO b
+ Bio.Bam.Pileup: pileup :: Stream (Of PosPrimChunks) IO b -> Stream (Of Pile) IO b
- Bio.Bam.Reader: concatInputs :: MonadBracketIO m => [FilePath] -> Enumerator' BamMeta [BamRaw] m a
+ Bio.Bam.Reader: concatInputs :: (MonadIO m, MonadMask m) => [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
- Bio.Bam.Reader: decodeBam :: Monad m => (BamMeta -> Iteratee [BamRaw] m a) -> Iteratee Block m (Iteratee [BamRaw] m a)
+ Bio.Bam.Reader: decodeBam :: MonadIO m => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
- Bio.Bam.Reader: getBamRaw :: Monad m => Iteratee Block m [BamRaw]
+ Bio.Bam.Reader: getBamRaw :: Monad m => Int64 -> Parser r m BamRaw
- Bio.Bam.Rec: BamRec :: Seqid -> Int -> Refseq -> Int -> Qual -> Vector Cigar -> Refseq -> Int -> Int -> Vector_Nucs_half Nucleotides -> Vector Qual -> Extensions -> FileOffset -> BamRec
+ Bio.Bam.Rec: BamRec :: Bytes -> Int -> Refseq -> Int -> Qual -> Vector Cigar -> Refseq -> Int -> Int -> Vector_Nucs_half Nucleotides -> Vector Qual -> Extensions -> Int64 -> BamRec
- Bio.Bam.Rec: [b_qname] :: BamRec -> Seqid
+ Bio.Bam.Rec: [b_qname] :: BamRec -> Bytes
- Bio.Bam.Rec: [b_virtual_offset] :: BamRec -> FileOffset
+ Bio.Bam.Rec: [b_virtual_offset] :: BamRec -> Int64
- Bio.Bam.Rec: bamRaw :: FileOffset -> Bytes -> BamRaw
+ Bio.Bam.Rec: bamRaw :: Int64 -> Bytes -> BamRaw
- Bio.Bam.Rec: virt_offset :: BamRaw -> FileOffset
+ Bio.Bam.Rec: virt_offset :: BamRaw -> Int64
- Bio.Bam.Rmdup: check_sort :: Monad m => (a -> BamRec) -> String -> Enumeratee [a] [a] m b
+ Bio.Bam.Rmdup: check_sort :: (Monad m, Ord b) => (a -> b) -> String -> Stream (Of a) m r -> Stream (Of a) m r
- Bio.Bam.Rmdup: normalizeTo :: Seqid -> Int -> BamRec -> Either BamRec BamRec
+ Bio.Bam.Rmdup: normalizeTo :: Bytes -> Int -> BamRec -> Either BamRec BamRec
- Bio.Bam.Rmdup: rmdup :: (Monad m, Ord l) => (BamRec -> l) -> Bool -> Collapse -> Enumeratee [BamRec] [(Int, BamRec)] m r
+ Bio.Bam.Rmdup: rmdup :: (Monad m, Ord l) => (BamRec -> l) -> Bool -> Collapse -> Stream (Of BamRec) m r -> Stream (Of (Int, BamRec)) m r
- Bio.Bam.Trim: mergeTrimBam :: Monad m => Int -> Int -> [Vector Nucleotides] -> [Vector Nucleotides] -> Enumeratee [BamRec] [BamRec] m a
+ Bio.Bam.Trim: mergeTrimBam :: Monad m => Int -> Int -> [Vector Nucleotides] -> [Vector Nucleotides] -> Stream (Of BamRec) m r -> Stream (Of BamRec) m r
- Bio.Bam.Writer: encodeBamWith :: (MonadIO m, IsBamRec r) => Int -> BamMeta -> Enumeratee [r] ByteString m ()
+ Bio.Bam.Writer: encodeBamWith :: (IsBamRec a, MonadIO m) => Int -> BamMeta -> Stream (Of a) m r -> ByteStream m r
- Bio.Bam.Writer: pipeBamOutput :: IsBamRec r => BamMeta -> Iteratee [r] IO ()
+ Bio.Bam.Writer: pipeBamOutput :: (IsBamRec a, MonadIO m) => BamMeta -> Stream (Of a) m r -> m r
- Bio.Bam.Writer: pipeSamOutput :: MonadIO m => BamMeta -> Iteratee [BamRec] m ()
+ Bio.Bam.Writer: pipeSamOutput :: (IsBamRec a, MonadIO m) => BamMeta -> Stream (Of a) m r -> m r
- Bio.Bam.Writer: writeBamFile :: IsBamRec r => FilePath -> BamMeta -> Iteratee [r] IO ()
+ Bio.Bam.Writer: writeBamFile :: (IsBamRec a, MonadIO m, MonadMask m) => FilePath -> BamMeta -> Stream (Of a) m r -> m r
- Bio.Bam.Writer: writeBamHandle :: (MonadIO m, IsBamRec r) => Handle -> BamMeta -> Iteratee [r] m ()
+ Bio.Bam.Writer: writeBamHandle :: (IsBamRec a, MonadIO m) => Handle -> BamMeta -> Stream (Of a) m r -> m r
- Bio.Base: Pos :: {-# UNPACK #-} !Seqid -> {-# UNPACK #-} !Int -> Position
+ Bio.Base: Pos :: {-# UNPACK #-} !ByteString -> {-# UNPACK #-} !Int -> Position
- Bio.Base: [p_seq] :: Position -> {-# UNPACK #-} !Seqid
+ Bio.Base: [p_seq] :: Position -> {-# UNPACK #-} !ByteString
- Bio.TwoBit: TBF :: ByteString -> !HashMap Seqid TwoBitSequence -> TwoBitFile
+ Bio.TwoBit: TBF :: ByteString -> !HashMap Bytes TwoBitSequence -> TwoBitFile
- Bio.TwoBit: [tbf_seqs] :: TwoBitFile -> !HashMap Seqid TwoBitSequence
+ Bio.TwoBit: [tbf_seqs] :: TwoBitFile -> !HashMap Bytes TwoBitSequence
- Bio.TwoBit: getFragment :: TwoBitFile -> Seqid -> Int -> Int -> Vector Word8
+ Bio.TwoBit: getFragment :: TwoBitFile -> Bytes -> Int -> Int -> Vector Word8
- Bio.TwoBit: getSeqLength :: TwoBitFile -> Seqid -> Int
+ Bio.TwoBit: getSeqLength :: TwoBitFile -> Bytes -> Int
- Bio.TwoBit: getSeqnames :: TwoBitFile -> [Seqid]
+ Bio.TwoBit: getSeqnames :: TwoBitFile -> [Bytes]
- Bio.TwoBit: lookupSequence :: TwoBitFile -> Seqid -> Maybe TwoBitSequence
+ Bio.TwoBit: lookupSequence :: TwoBitFile -> Bytes -> Maybe TwoBitSequence
- Bio.Util.MMap: unsafeMMapFile :: FilePath -> IO Bytes
+ Bio.Util.MMap: unsafeMMapFile :: FilePath -> IO ByteString

Files

+ Bio/Adna.hs view
@@ -0,0 +1,655 @@+-- | Things specific to ancient DNA, e.g. damage models.+--+-- For aDNA, we need a substitution probability.  We have three options:+-- use an empirically determined PSSM, use an arithmetically defined+-- PSSM based on the /Johnson/ model, use a context sensitive PSSM based+-- on the /Johnson/ model and an alignment.  Using /Dindel/, actual+-- substitutions relative to a called haplotype would be taken into+-- account.  Since we're not going to do that, taking alignments into+-- account is difficult, somewhat approximate, and therefore not worth+-- the hassle.++module Bio.Adna (+    DmgStats(..),+    CompositionStats,+    SubstitutionStats,+    addFragType,+    damagePatternsIter,+    damagePatternsIterMD,+    damagePatternsIter2Bit,+    alnFromMd,++    DamageParameters(..),+    NewDamageParameters(..),+    GenDamageParameters(..),+    DamageModel,+    bang, nudge,+    Alignment(..),+    FragType(..),+    Subst(..),++    NPair,+    npair,+    fst_np,+    snd_np,++    noDamage,+    univDamage,+    empDamage,+    Mat44D(..),+    MMat44D(..),+    scalarMat,+    complMat,+    freezeMats,++    bwa_cal_maxdiff+  ) where++import Bio.Bam+import Bio.Prelude+import Bio.TwoBit++import qualified Data.Vector                    as V+import qualified Data.Vector.Generic            as G+import qualified Data.Vector.Storable           as VS+import qualified Data.Vector.Unboxed            as U+import qualified Data.Vector.Unboxed.Mutable    as UM+import qualified Streaming.Prelude              as Q++-- | We represent substitution matrices by the type 'Mat44D'.  Internally,+-- this is a vector of packed vectors.  Conveniently, each of the packed+-- vectors represents all transitions /into/ the given nucleotide.++newtype Mat44D = Mat44D (U.Vector Double) deriving (Show, Generic)+newtype MMat44D = MMat44D (UM.IOVector Double)++-- | A 'DamageModel' is a function that gives substitution matrices for+-- each position in a read.  The 'DamageModel' can depend on whether the+-- alignment is reversed, the length of the read and the position.  (In+-- practice, we should probably memoize precomputed damage models+-- somehow.)++type DamageModel = Bool -> Int -> Int -> Mat44D+data Subst = Nucleotide :-> Nucleotide deriving (Eq, Ord, Ix, Show)++infix 9 :->+infix 8 `bang`++-- | Convenience function to access a substitution matrix that has a+-- mnemonic reading.+{-# INLINE bang #-}+bang :: Mat44D -> Subst -> Double+bang (Mat44D v) (N x :-> N y)+    | U.length v == 16 = v U.! (fromIntegral x + 4 * fromIntegral y)+    | otherwise = error $ "Huh? " ++ show (U.length v)++{-# INLINE nudge #-}+nudge :: MMat44D -> Subst -> Double -> IO ()+nudge (MMat44D v) (N x :-> N y) a = UM.read v i >>= UM.write v i . (+) a+  where i = fromIntegral x + 4 * fromIntegral y++scalarMat :: Double -> Mat44D+scalarMat s = Mat44D $ U.fromListN 16 [ s, 0, 0, 0+                                      , 0, s, 0, 0+                                      , 0, 0, s, 0+                                      , 0, 0, 0, s ]+complMat :: Mat44D -> Mat44D+complMat v = Mat44D $ U.fromListN 16 [ v `bang` compl x :-> compl y+                                     | y <- range (nucA, nucT)+                                     , x <- range (nucA, nucT) ]++-- | Adds the two matrices of a mutable substitution model (one for each+-- strand) appropriately, normalizes the result (to make probabilities+-- from pseudo-counts), and freezes that into one immutable matrix.  We+-- add a single count everywhere to avoid getting NaN from bizarre data.+freezeMats :: MMat44D -> MMat44D -> IO Mat44D+freezeMats (MMat44D vv) (MMat44D ww) = do+    v <-            Mat44D <$> U.freeze vv+    w <- complMat . Mat44D <$> U.freeze ww++    let sums = U.generate 4 $ \x0 ->+                    let x = N $ fromIntegral x0+                    in sum [ v `bang` x :-> z + w `bang` x :-> z+                           | z <- range (nucA, nucT) ] + 4++    return . Mat44D $ U.fromListN 16+            [ (v `bang` x :-> y + w `bang` x :-> y + 1) / s+            | y <- range (nucA, nucT)+            , x <- range (nucA, nucT)+            , let s = sums U.! fromIntegral (unN x) ]+++-- | 'DamageModel' for undamaged DNA.  The likelihoods follow directly+-- from the quality score.  This needs elaboration to see what to do+-- with amibiguity codes (even though those haven't actually been+-- observed in the wild).+noDamage :: DamageModel+noDamage _ _ _ = one where !one = scalarMat 1+++-- | Parameters for the universal damage model.+--+-- We assume the correct model is either no damage, or single strand+-- damage, or double strand damage.  Each of them comes with a+-- probability.  It turns out that blending them into one is simply+-- accomplished by multiplying these probabilities onto the deamination+-- probabilities.+--+-- For single stranded library prep, only one kind of damage occurs (C+-- frequency ('ssd_sigma') in single stranded parts, and the overhang+-- length is distributed exponentially with parameter 'ssd_lambda' at+-- the 5' end and 'ssd_kappa' at the 3' end.  (Without UDG treatment,+-- those will be equal.  With UDG, those are much smaller and in fact+-- don't literally represent overhangs.)+--+-- For double stranded library prep, we get C->T damage at the 5' end+-- and G->A at the 3' end with rate 'dsd_sigma' and both in the interior+-- with rate 'dsd_delta'.  Everything is symmetric, and therefore the+-- orientation of the aligned read doesn't matter either.  Both+-- overhangs follow a distribution with parameter 'dsd_lambda'.++data DamageParameters float = DP { ssd_sigma  :: !float         -- deamination rate in ss DNA, SS model+                                 , ssd_delta  :: !float         -- deamination rate in ds DNA, SS model+                                 , ssd_lambda :: !float         -- param for geom. distribution, 5' end, SS model+                                 , ssd_kappa  :: !float         -- param for geom. distribution, 3' end, SS model+                                 , dsd_sigma  :: !float         -- deamination rate in ss DNA, DS model+                                 , dsd_delta  :: !float         -- deamination rate in ds DNA, DS model+                                 , dsd_lambda :: !float }       -- param for geom. distribution, DS model+  deriving (Read, Show, Generic)++data NewDamageParameters vec float = NDP { dp_gc_frac :: !float+                                         , dp_mu      :: !float+                                         , dp_nu      :: !float+                                         , dp_alpha5  :: !(vec float)+                                         , dp_beta5   :: !(vec float)+                                         , dp_alpha   :: !float+                                         , dp_beta    :: !float+                                         , dp_alpha3  :: !(vec float)+                                         , dp_beta3   :: !(vec float) }+  deriving (Read, Show, Generic)++data GenDamageParameters vec float+    = UnknownDamage+    | OldDamage (DamageParameters float)+    | NewDamage (NewDamageParameters vec float)+  deriving (Show, Generic, Read)++++-- | Generic substitution matrix, has C->T and G->A deamination as+-- parameters.  Setting 'p' or 'q' to 0 as appropriate makes this apply+-- to the single stranded or undamaged case.++{-# INLINE genSubstMat #-}+genSubstMat :: Double -> Double -> Mat44D+genSubstMat p q = Mat44D $ U.fromListN 16 [ 1,  0,   q,  0+                                          , 0, 1-p,  0,  0+                                          , 0,  0,  1-q, 0+                                          , 0,  p,   0,  1 ]++univDamage :: DamageParameters Double -> DamageModel+univDamage DP{..} r l i = genSubstMat (p1+p2) (q1+q2)+    where+        (p1, q1) = if r then let lam5 = ssd_lambda ^ (l-i)+                                 lam3 = ssd_kappa ^ (1+i)+                                 lam  = lam3 + lam5 - lam3 * lam5+                                 p    = ssd_sigma * lam + ssd_delta * (1-lam)+                             in (0,p)+                        else let lam5 = ssd_lambda ^ (1+i)+                                 lam3 = ssd_kappa ^ (l-i)+                                 lam  = lam3 + lam5 - lam3 * lam5+                                 p    = ssd_sigma * lam + ssd_delta * (1-lam)+                             in (p,0)++        p2      = dsd_sigma * lam5_ds + dsd_delta * (1-lam5_ds)+        q2      = dsd_sigma * lam3_ds + dsd_delta * (1-lam3_ds)+        lam5_ds = dsd_lambda ^ (1+i)+        lam3_ds = dsd_lambda ^ (l-i)++empDamage :: NewDamageParameters U.Vector Double -> DamageModel+empDamage NDP{..} =+    \r l i -> if i+i < l then+                if r then fromMaybe middleRev (rev5 V.!? i)+                     else fromMaybe middle    (fwd5 V.!? i)+              else+                if r then fromMaybe middleRev (rev3 V.!? (l-i-1))+                     else fromMaybe middle    (fwd3 V.!? (l-i-1))+  where+    !middle    = genSubstMat' dp_alpha dp_beta+    !middleRev = genSubstMat' dp_beta dp_alpha++    !fwd5 = V.zipWith genSubstMat' (G.convert dp_alpha5) (G.convert dp_beta5)+    !fwd3 = V.zipWith genSubstMat' (G.convert dp_alpha3) (G.convert dp_beta3)++    !rev5 = V.zipWith genSubstMat' (G.convert dp_beta5) (G.convert dp_alpha5)+    !rev3 = V.zipWith genSubstMat' (G.convert dp_beta3) (G.convert dp_alpha3)++    genSubstMat' a b = genSubstMat (recip $ 1 + exp (-a)) (recip $ 1 + exp (-b))+++-- | Collected \"traditional\" statistics:+--+-- * Base composition near 5' end and near 3' end.  Each consists of+--   five vectors of counts of A,C,G,T, and everything else.+--   'basecompo5' begins with 'context' bases to the left of the 5' end,+--   'basecompo3' ends with 'context' bases to the right of the 3' end.+--+-- * Substitutions.  Counted from the reconstructed alignment, once+--   around the 5' end and once around the 3' end.  For a total of 2*4*4+--   different substitutions.  Positions where the query has a gap are+--   skipped.+--+-- * Substitutions at CpG motifs.  Also counted from the reconstructed+--   alignment, and a CpG site is simply the sequence CG in the+--   reference.  Gaps may confuse that definition, so that CpHpG still+--   counts as CpG, because the H is gapped.  That might actually+--   be desirable.+--+-- * Conditional substitutions.  The 5' and 3' ends count as damaged if+--   the very last position has a C-to-T substitution.  With that in+--   mind, 'substs5d5', 'substs5d3', 'substs5dd' are like 'substs5', but+--   counting only reads where the 5' end is damaged, where the 3' end+--   is damaged, and where both ends are damaged, respectively.++data DmgStats = DmgStats {+    basecompo5 :: CompositionStats,+    basecompo3 :: CompositionStats,+    substs5    :: SubstitutionStats,+    substs3    :: SubstitutionStats,+    substs5d5  :: SubstitutionStats,+    substs3d5  :: SubstitutionStats,+    substs5d3  :: SubstitutionStats,+    substs3d3  :: SubstitutionStats,+    substs5dd  :: SubstitutionStats,+    substs3dd  :: SubstitutionStats,+    substs5cpg :: SubstitutionStats,+    substs3cpg :: SubstitutionStats }+  deriving Show++type CompositionStats  = [( Maybe Nucleotide, U.Vector Int )]+type SubstitutionStats = [( Subst, U.Vector Int )]+++data FragType = Complete | Leading | Trailing deriving (Show, Eq)++-- | Compact storage of a pair of ambiguous 'Nucleotides'.  Used to+-- represent alignments in a way that is accessible even to assembly+-- code.  The first and sencond field are stored in the low and high+-- nybble, respectively.  See 'fst_np', 'snd_np', 'npair'.+newtype NPair = NPair Word8 deriving (Eq, Ord)++npair :: Nucleotides -> Nucleotides -> NPair+npair (Ns r) (Ns q) = NPair $ shiftL q 4 .|. r .&. 0xF++fst_np, snd_np :: NPair -> Nucleotides+fst_np (NPair w) = Ns (w .&. 0xF)+snd_np (NPair w) = Ns (shiftR w 4)++instance Storable NPair where+    sizeOf    _ = 1+    alignment _ = 1+    peek p = NPair <$> peek (castPtr p :: Ptr Word8)+    poke p (NPair v) = poke (castPtr p :: Ptr Word8) v++instance Show NPair where+    showsPrec _ p = shows (fst_np p) . (:) '/' . shows (snd_np p)++-- | Alignment record.  The reference sequence is filled with Ns if+-- missing.+data Alignment = ALN+    { a_sequence :: !(VS.Vector NPair)      -- the alignment proper+    , a_fragment_type :: !FragType }        -- was the adapter trimmed?++addFragType :: Monad m => BamMeta -> Stream (Of BamRaw) m b -> Stream (Of (BamRaw,FragType)) m b+addFragType meta = Q.map $ \br -> (br, case unpackBam br of+    b | isFirstMate  b && isPaired     b -> Leading+      | isSecondMate b && isPaired     b -> Trailing+      | not sane                         -> Complete     -- leeHom fscked it up+      | isFirstMate  b || isSecondMate b -> Complete     -- old style flagging+      | isTrimmed    b || isMerged     b -> Complete     -- new style flagging+      | otherwise                        -> Leading)+  where+    sane = null [ () | ("PG",line) <- meta_other_shit meta+                     , ("PN","mergeTrimReadsBAM") <- line ]++-- | Stream transformer that computes some statistics from plain BAM+-- (no MD field needed) and a 2bit file.  The 'Alignment' is also+-- reconstructed and passed downstream.  The final value of the source+-- stream ends up in the 'stats_more' field of the result.+--+-- * Get the reference sequence including both contexts once.  If this+--   includes invalid sequence (negative coordinate), pad suitably.+-- * Accumulate counts for the valid parts around 5' and 3' ends as+--   appropriate from flags and config.+-- * Combine the part that was aligned to (so no context) with the read+--   to reconstruct the alignment.+--+-- Arguments are the table of reference names, the 2bit file with the+-- reference, the amount of context outside the alignment desired, and+-- the amount of context inside desired.+--+-- For 'Complete' fragments, we cut the read in the middle, so the 5'+-- and 3' plots stay clean from each other's influence.  'Leading' and+-- 'Trailing' fragments count completely towards the appropriate end.++damagePatternsIter2Bit :: MonadIO m+                       => Refs -> TwoBitFile -> Int -> Int+                       -> Stream (Of (BamRaw,FragType)) m x+                       -> Stream (Of Alignment) m DmgStats+damagePatternsIter2Bit refs tbf ctx rng =+    damagePatternsIter ctx rng .+    Q.mapMaybe (\(br,ft) -> do+        let b@BamRec{..} = unpackBam br+        guard (not $ isUnmapped b)+        let ref_nm = sq_name $ getRef refs b_rname+            ref    = getFragment tbf ref_nm (b_pos - ctx) (alignedLength b_cigar + 2*ctx)+            pps    = aln_from_ref (U.drop ctx ref) b_seq b_cigar+        return (b, ft, ref, pps))++-- | Stream transformer that computes some statistics from plain BAM+-- with a valid MD field.  The 'Alignment' is also reconstructed and+-- passed downstream.  The final value of the source stream becomes+-- the 'stats_more' field of the result.+--+-- * Reconstruct the alignment from CIGAR, SEQ, and MD.+-- * Filter the alignment to get the reference sequence, accumulate it.+-- * Accumulate everything over the alignment.+--+-- The argument is the amount of context inside desired.+--+-- For 'Complete' fragments, we cut the read in the middle, so the 5'+-- and 3' plots stay clean from each other's influence.  'Leading' and+-- 'Trailing' fragments count completely towards the appropriate end.++damagePatternsIterMD :: MonadIO m+                     => Int+                     -> Stream (Of (BamRaw,FragType)) m x+                     -> Stream (Of Alignment) m DmgStats+damagePatternsIterMD rng =+    damagePatternsIter 0 rng .+    Q.mapMaybe (\(br,ft) -> do+        let b@BamRec{..} = unpackBam br+        guard (not $ isUnmapped b)+        md <- getMd b+        let pps = alnFromMd b_seq b_cigar md+            ref = U.convert $ VS.map fromN $ VS.filter ((/=) gap) $ VS.map fst_np pps+        return (b, ft, ref, pps))+  where+    fromN ns | ns == nucsA = 2+             | ns == nucsC = 1+             | ns == nucsG = 3+             | ns == nucsT = 0+             | otherwise   = 4++-- | Common logic for statistics. The function 'get_ref_and_aln'+-- reconstructs reference sequence and alignment from a Bam record.  It+-- is expected to construct the alignment with respect to the forwards+-- strand of the reference; we reverse-complement it if necessary.+damagePatternsIter :: MonadIO m+                   => Int -> Int+                   -> Stream (Of (BamRec, FragType, U.Vector Word8, VS.Vector NPair)) m x+                   -> Stream (Of Alignment) m DmgStats+damagePatternsIter ctx rng stream = do+    let maxwidth = ctx + rng+    acc_bc <- liftIO $ UM.replicate (2 * 5 *    maxwidth) (0::Int)+    acc_st <- liftIO $ UM.replicate (2 * 4 * 4 * 4 * rng) (0::Int)+    acc_cg <- liftIO $ UM.replicate (2 * 2 * 4 *     rng) (0::Int)++    void $ flip Q.mapM (Q.map revcom_both stream) $+             \(BamRec{..}, a_fragment_type, ref, a_sequence) -> liftIO $ do+                  -- basecompositon near 5' end, near 3' end+                  let (width5, width3) = case a_fragment_type of+                                                Leading -> (full_width, 0)+                                                Trailing -> (0, full_width)+                                                Complete -> (half_width, half_width)+                            where full_width = min (U.length ref) $ ctx + min rng (alignedLength b_cigar)+                                  half_width = min (U.length ref) $ ctx + min rng (alignedLength b_cigar `div` 2)+                  mapM_ (\i -> bump (fromIntegral (ref U.!  i                   ) * maxwidth + i) acc_bc) [0 .. width5-1]+                  mapM_ (\i -> bump (fromIntegral (ref U.! (i + U.length ref) +6) * maxwidth + i) acc_bc) [-width3 .. -1]++                  -- For substitutions, decide what damage class we're in:+                  -- 0 - no damage, 1 - damaged 5' end, 2 - damaged 3' end, 3 - both+                  let dmgbase = 2*4*4*rng *+                                ((if VS.null a_sequence || VS.head a_sequence /= npair nucsC nucsT then 1 else 0)+                                +(if VS.null a_sequence || VS.last a_sequence /= npair nucsC nucsT then 2 else 0))++                  -- substitutions near 5' end+                  let len_at_5 = case a_fragment_type of Leading  -> min rng (G.length b_seq)+                                                         Complete -> min rng (G.length b_seq `div` 2)+                                                         Trailing -> 0+                  flip G.imapM_ (VS.take len_at_5 a_sequence) $+                        \i uv -> withPair uv $ \j -> bump (j * rng + i + dmgbase) acc_st++                  -- substitutions at CpG sites near 5' end+                  G.izipWithM_+                      (\i uv wz ->+                          when (fst_np uv == nucsC && fst_np wz == nucsG) $ do+                              withNs (snd_np uv) $ \y -> bump (  y   * rng +  i ) acc_cg+                              withNs (snd_np wz) $ \y -> bump ((y+4) * rng + i+1) acc_cg)+                      (VS.take len_at_5 a_sequence) (VS.drop 1 a_sequence)++                  -- substitutions near 3' end+                  let len_at_3 = case a_fragment_type of Leading  -> 0+                                                         Complete -> min rng (G.length b_seq `div` 2)+                                                         Trailing -> min rng (G.length b_seq)+                  flip G.imapM_ (VS.take len_at_3 (VS.reverse a_sequence)) $+                        \i uv -> withPair uv $ \j -> bump ((17+j) * rng -i -1 + dmgbase) acc_st++                  -- substitutions at CpG sites near 3' end+                  G.izipWithM_+                      (\i wz uv ->+                          when (fst_np uv == nucsC && fst_np wz == nucsG) $ do+                              withNs (snd_np uv) $ \y -> bump ((y+ 9) * rng - i-2) acc_cg+                              withNs (snd_np wz) $ \y -> bump ((y+13) * rng - i-1) acc_cg)+                      (VS.take len_at_3 (VS.reverse a_sequence))+                      (VS.drop 1 (VS.reverse a_sequence))++                  return ALN{..}+++    let nsubsts = 4*4*rng+        mk_substs off = sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((4*i+j)*rng + off*nsubsts) rng acc_st)+                                 | (i,n1) <- zip [0..] [nucA..nucT]+                                 , (j,n2) <- zip [0..] [nucA..nucT] ]++    accs <- liftIO $ DmgStats <$> sequence [ (,) nuc <$> U.unsafeFreeze (UM.slice (i*maxwidth) maxwidth acc_bc)+                                           | (i,nuc) <- zip [2,1,3,0,4] [Just nucA,Just nucC,Just nucG,Just nucT,Nothing] ]+                              <*> sequence [ (,) nuc <$> U.unsafeFreeze (UM.slice (i*maxwidth) maxwidth acc_bc)+                                           | (i,nuc) <- zip [7,6,8,5,9] [Just nucA,Just nucC,Just nucG,Just nucT,Nothing] ]++                              <*> mk_substs 0+                              <*> mk_substs 1+                              <*> mk_substs 2+                              <*> mk_substs 3+                              <*> mk_substs 4+                              <*> mk_substs 5+                              <*> mk_substs 6+                              <*> mk_substs 7++                              <*> sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((i+j)*rng) rng acc_cg)+                                           | (i,n1) <- [(0,nucC), (4,nucG)]+                                           , (j,n2) <- zip [0..] [nucA..nucT] ]++                              <*> sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((i+j)*rng) rng acc_cg)+                                           | (i,n2) <- [(8,nucC), (12,nucG)]+                                           , (j,n1) <- zip [0..] [nucA..nucT] ]++    return $ accs { substs5   = mconcat [ substs5 accs, substs5d5 accs, substs5d3 accs, substs5dd accs ]+                  , substs3   = mconcat [ substs3 accs, substs3d5 accs, substs3d3 accs, substs3dd accs ]+                  , substs5d5 = mconcat [ substs5d5 accs, substs5dd accs]+                  , substs3d5 = mconcat [ substs3d5 accs, substs3dd accs]+                  , substs5d3 = mconcat [ substs5d3 accs, substs5dd accs]+                  , substs3d3 = mconcat [ substs3d3 accs, substs3dd accs] }+  where+    {-# INLINE withPair #-}+    withPair (NPair i) k =+        case pairTab `U.unsafeIndex` fromIntegral i of+            j -> when (j >= 0) (k j)++    !pairTab = U.replicate 256 (-1) U.//+            [ (fromIntegral i, x*4+y) | (u,x) <- zip [nucsA, nucsC, nucsG, nucsT] [0,1,2,3]+                                      , (v,y) <- zip [nucsA, nucsC, nucsG, nucsT] [0,1,2,3]+                                      , let NPair i = npair u v ]+    {-# INLINE bump #-}+    bump i v = UM.unsafeRead v i >>= UM.unsafeWrite v i . succ++    {-# INLINE withNs #-}+    withNs ns k | ns == nucsA = k 0+                | ns == nucsC = k 1+                | ns == nucsG = k 2+                | ns == nucsT = k 3+                | otherwise   = return ()+++instance Monoid DmgStats where+    mappend = (<>)+    mempty = DmgStats { basecompo5 = empty_compo+                      , basecompo3 = empty_compo+                      , substs5    = empty_subst+                      , substs3    = empty_subst+                      , substs5d5  = empty_subst+                      , substs3d5  = empty_subst+                      , substs5d3  = empty_subst+                      , substs3d3  = empty_subst+                      , substs5dd  = empty_subst+                      , substs3dd  = empty_subst+                      , substs5cpg = empty_subst+                      , substs3cpg = empty_subst }+      where+        empty_compo = [ (nuc, U.empty) | nuc <- [Just nucA, Just nucC, Just nucG, Just nucT, Nothing] ]+        empty_subst = [ (n1 :-> n2, U.empty) | n1 <- [nucA..nucT], n2 <- [nucA..nucT] ]++instance Semigroup DmgStats where+    (<>) = merge_dmg_stats++merge_dmg_stats :: DmgStats -> DmgStats -> DmgStats+merge_dmg_stats a b =+    DmgStats { basecompo5 = zipWith s1 (basecompo5 a) (basecompo5 b)+             , basecompo3 = zipWith s1 (basecompo3 a) (basecompo3 b)+             , substs5    = zipWith s2 (substs5    a) (substs5    b)+             , substs3    = zipWith s2 (substs3    a) (substs3    b)+             , substs5d5  = zipWith s2 (substs5d5  a) (substs5d5  b)+             , substs3d5  = zipWith s2 (substs3d5  a) (substs3d5  b)+             , substs5d3  = zipWith s2 (substs5d3  a) (substs5d3  b)+             , substs3d3  = zipWith s2 (substs3d3  a) (substs3d3  b)+             , substs5dd  = zipWith s2 (substs5dd  a) (substs5dd  b)+             , substs3dd  = zipWith s2 (substs3dd  a) (substs3dd  b)+             , substs5cpg = zipWith s2 (substs5cpg a) (substs5cpg b)+             , substs3cpg = zipWith s2 (substs3cpg a) (substs3cpg b) }+      where+        s1 (x, u) (z, v) | x /= z    = error "Mismatch in zip.  This is a bug."+                         | U.null u  = (x, v)+                         | U.null v  = (x, u)+                         | otherwise = (x, U.zipWith (+) u v)++        s2 (x :-> y, u) (z :-> w, v) | x /= z || y /= w = error "Mismatch in zip.  This is a bug."+                                     | U.null u         = (x :-> y, v)+                                     | U.null v         = (x :-> y, u)+                                     | otherwise        = (x :-> y, U.zipWith (+) u v)+++revcom_both :: ( BamRec, FragType, U.Vector Word8, VS.Vector NPair )+            -> ( BamRec, FragType, U.Vector Word8, VS.Vector NPair )+revcom_both (b, ft, ref, pps)+    | isReversed b = ( b, ft, revcom_ref ref, revcom_pairs pps )+    | otherwise    = ( b, ft,            ref,              pps )+  where+    revcom_ref   =  U.reverse .  U.map (\c -> if c > 3 then c else xor c 2)+    revcom_pairs = VS.reverse . VS.map (\p -> npair (compls $ fst_np p) (compls $ snd_np p))+++-- | Reconstructs the alignment from reference, query, and cigar.  Only+-- positions where the query is not gapped are produced.+aln_from_ref :: U.Vector Word8 -> Vector_Nucs_half Nucleotides -> VS.Vector Cigar -> VS.Vector NPair+aln_from_ref ref0 qry0 cig0 = VS.fromList $ step ref0 qry0 cig0+  where+    step ref qry cig1+        | U.null ref || G.null qry || G.null cig1 = []+        | otherwise = case G.unsafeHead cig1 of { op :* n ->+                      case G.unsafeTail cig1 of { cig ->+                      case op of {++        Mat -> zipWith (npair . nn) (G.toList (G.take n ref))+                                    (G.toList (G.take n qry)) ++ step (G.drop n ref) (G.drop n qry) cig ;+        Del ->                                                   step (G.drop n ref)           qry  cig ;+        Ins -> map (npair gap) (G.toList (G.take n qry))      ++ step           ref  (G.drop n qry) cig ;+        SMa -> map (npair gap) (G.toList (G.take n qry))      ++ step           ref  (G.drop n qry) cig ;+        HMa -> replicate n (npair gap nucsN)                  ++ step           ref            qry  cig ;+        Nop ->                                                   step           ref            qry  cig ;+        Pad ->                                                   step           ref            qry  cig }}}++    nn 0 = nucsT+    nn 1 = nucsC+    nn 2 = nucsA+    nn 3 = nucsG+    nn _ = nucsN+++-- | Reconstructs the alignment from query, cigar, and md.  Only+-- positions where the query is not gapped are produced.+alnFromMd :: Vector_Nucs_half Nucleotides -> VS.Vector Cigar -> [MdOp] -> VS.Vector NPair+alnFromMd qry0 cig0 md0 = VS.fromList $ step qry0 cig0 md0+  where+    step qry cig1 md+        | G.null qry || G.null cig1 || null md = []+        | otherwise = case G.unsafeHead cig1 of op :* n -> step' qry op n (G.unsafeTail cig1) md++    step' qry  _ 0 cig             md  = step  qry      cig md+    step' qry op n cig (MdNum  0 : md) = step' qry op n cig md+    step' qry op n cig (MdDel [] : md) = step' qry op n cig md++    step' qry Mat n cig (MdNum m : md)+            | n <  m =    map twin (G.toList (G.take n qry)) ++ step  (G.drop n qry)           cig (MdNum (m-n) : md)+            | n >  m =    map twin (G.toList (G.take m qry)) ++ step' (G.drop m qry) Mat (n-m) cig                md+            | n == m =    map twin (G.toList (G.take n qry)) ++ step  (G.drop n qry)           cig                md+    step' qry Mat n cig (MdRep c : md) = npair c (G.head qry) : step' (G.tail   qry) Mat (n-1) cig                md+    step'   _ Mat _   _          _     = []++    step' qry Del n cig (MdDel (_:ss) : md) = step' qry Del (n-1) cig (MdDel ss : md)+    step'   _ Del _   _               _     = []++    step' qry Ins n cig                 md  = map (npair gap) (G.toList (G.take n qry)) ++ step (G.drop n qry) cig md+    step' qry SMa n cig                 md  = map (npair gap) (G.toList (G.take n qry)) ++ step (G.drop n qry) cig md+    step' qry HMa n cig                 md  =             replicate n (npair gap nucsN) ++ step           qry  cig md+    step' qry Nop _ cig                 md  =                                              step           qry  cig md+    step' qry Pad _ cig                 md  =                                              step           qry  cig md++    twin q = npair q q++-- | Number of mismatches allowed by BWA.+-- @bwa_cal_maxdiff thresh len@ returns the number of mismatches+-- @bwa aln -n $tresh@ would allow in a read of length @len@.  For+-- reference, here is the code from BWA that computes it (we assume @err+-- = 0.02@, just like BWA):+--+-- @+-- int bwa_cal_maxdiff(int l, double err, double thres)+--   {+--      double elambda = exp(-l * err);+--      double sum, y = 1.0;+--      int k, x = 1;+--      for (k = 1, sum = elambda; k < 1000; ++k) {+--          y *= l * err;+--          x *= k;+--          sum += elambda * y / x;+--          if (1.0 - sum < thres) return k;+--      }+--      return 2;+--   }+-- @++bwa_cal_maxdiff :: Double -> Int -> Int+bwa_cal_maxdiff thresh len = k_fin-1+  where+    (k_fin, _, _, _) = head $ dropWhile bad $ iterate step (1,elambda,1,1)++    err = 0.02+    elambda = exp . negate $ fromIntegral len * err++    step (k, s, x, y) = (k+1, s', x', y')+      where y' = y * fromIntegral len * err+            x' = x * fromIntegral k+            s' = s + elambda * y' / x'++    bad (_, s, _, _) = 1-s >= thresh+
+ Bio/Align.hs view
@@ -0,0 +1,85 @@+module Bio.Align (+    Mode(..),+    myersAlign,+    showAligned+                 ) where++import Bio.Prelude       hiding ( lefts, rights )+import Foreign.C.String         ( CString )+import Foreign.C.Types          ( CInt(..) )+import Foreign.Marshal.Alloc    ( allocaBytes )++import qualified Data.ByteString.Char8      as S+import qualified Data.ByteString.Unsafe     as S+import qualified Data.ByteString.Lazy.Char8 as L++foreign import ccall unsafe "myers_align.h myers_diff" myers_diff ::+        CString -> CInt ->              -- sequence A and length A+        CInt ->                         -- mode (an enum)+        CString -> CInt ->              -- sequence B and length B+        CInt ->                         -- max distance+        CString ->                      -- backtracing space A+        CString ->                      -- backtracing space B+        IO CInt                         -- returns distance++-- | Mode argument for 'myersAlign', determines where free gaps are+-- allowed.+data Mode = Globally  -- ^ align globally, without gaps at either end+          | HasPrefix -- ^ align so that the second sequence is a prefix of the first+          | IsPrefix  -- ^ align so that the first sequence is a prefix of the second+    deriving Enum++-- | Align two strings.  @myersAlign maxd seqA mode seqB@ tries to align+-- @seqA@ to @seqB@, which will work as long as no more than @maxd@ gaps+-- or mismatches are incurred.  The @mode@ argument determines if either+-- of the sequences is allowed to have an overhanging tail.+--+-- The result is the triple of the actual distance (gaps + mismatches)+-- and the two padded sequences.  These sequences are the original+-- sequences with dashes inserted for gaps.+--+-- The algorithm is the O(nd) algorithm by Myers, implemented in C.  A+-- gap and a mismatch score the same.  The strings are supposed to code+-- for DNA, the code understands IUPAC-IUB ambiguity codes.  Two+-- characters match iff there is at least one nucleotide both can code+-- for.  Note that N is a wildcard, while X matches nothing.++myersAlign :: Int -> Bytes -> Mode -> Bytes -> (Int, Bytes, Bytes)+myersAlign maxd seqA mode seqB =+    unsafePerformIO                                 $+    S.unsafeUseAsCStringLen seqA                    $ \(seq_a, len_a) ->+    S.unsafeUseAsCStringLen seqB                    $ \(seq_b, len_b) ->++    -- size of output buffers derives from this:+    -- char *out_a = bt_a + len_a + maxd +2 ;+    -- char *out_b = bt_b + len_b + maxd +2 ;+    allocaBytes (len_a + maxd + 2)                  $ \bt_a ->+    allocaBytes (len_b + maxd + 2)                  $ \bt_b ->++    myers_diff seq_a (fromIntegral len_a)+               (fromIntegral $ fromEnum mode)+               seq_b (fromIntegral len_b)+               (fromIntegral maxd) bt_a bt_b      >>= \dist ->+    if dist < 0+      then return (maxBound, S.empty, S.empty)+      else (,,) (fromIntegral dist) <$>+           S.packCString bt_a <*>+           S.packCString bt_b+++-- | Nicely print an alignment.  An alignment is simply a list of+-- strings with inserted gaps to make them align.  We split them into+-- manageable chunks, stack them vertically and add a line showing+-- asterisks in every column where all aligned strings agree.  The+-- result is /almost/ the Clustal format.+showAligned :: Int -> [Bytes] -> [L.ByteString]+showAligned w ss | all S.null ss = []+                 | otherwise = map (L.fromChunks . (:[])) lefts +++                               L.pack agreement :+                               L.empty :+                               showAligned w rights+  where+    (lefts, rights) = unzip $ map (S.splitAt w) ss+    agreement = map star $ S.transpose lefts+    star str = if S.null str || S.all (== S.head str) str then '*' else ' '+
+ Bio/Bam.hs view
@@ -0,0 +1,24 @@+-- | Umbrella module for most of what's under 'Bio.Bam'.++module Bio.Bam (+    module Bio.Bam.Fastq,+    module Bio.Bam.Filter,+    module Bio.Bam.Header,+    module Bio.Bam.Index,+    module Bio.Bam.Reader,+    module Bio.Bam.Rec,+    module Bio.Bam.Trim,+    module Bio.Bam.Writer,+    module Bio.Streaming+               ) where++import Bio.Bam.Fastq+import Bio.Bam.Filter+import Bio.Bam.Header+import Bio.Bam.Index+import Bio.Bam.Reader+import Bio.Bam.Rec+import Bio.Bam.Trim+import Bio.Bam.Writer+import Bio.Streaming+
+ Bio/Bam/Evan.hs view
@@ -0,0 +1,100 @@+-- | This module contains stuff relating to conventions local to MPI+-- EVAN.  The code is needed regularly, but it can be harmful when+-- applied to BAM files that follow different conventions.  Most+-- importantly, no program should call these functions by default.++module Bio.Bam.Evan+    ( fixupFlagAbuse+    , fixupBwaFlags+    , removeWarts+    ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude++import qualified Data.ByteString.Char8 as S++-- | Fixes abuse of flags valued 0x800 and 0x1000.  We used them for+-- low quality and low complexity, but they have since been redefined.+-- If set, we clear them and store them into the ZQ field.  Also fixes+-- abuse of the combination of the paired, 1st mate and 2nd mate flags+-- used to indicate merging or trimming.  These are canonicalized and+-- stored into the FF field.  This function is unsafe on BAM files of+-- unclear origin!+fixupFlagAbuse :: BamRec -> BamRec+fixupFlagAbuse b =+    (if b_flag b .&. flag_low_quality /= 0 then setQualFlag 'Q' else id) $          -- low qual, new convention+    (if b_flag b .&. flag_low_complexity /= 0 then setQualFlag 'C' else id) $       -- low complexity, new convention+    b { b_flag = cleaned_flags, b_exts = cleaned_exts }+  where+        -- removes old flag abuse+        flags' = b_flag b .&. complement (flag_low_quality .|. flag_low_complexity)+        cleaned_flags | flags' .&. flagPaired == 0 = flags' .&. complement (flagFirstMate .|. flagSecondMate)+                      | otherwise                  = flags'++        flag_low_quality    =  0x800+        flag_low_complexity = 0x1000++        -- merged & trimmed from old flag abuse+        is_merged  = flags' .&. (flagPaired .|. flagFirstMate .|. flagSecondMate) == flagFirstMate .|. flagSecondMate+        is_trimmed = flags' .&. (flagPaired .|. flagFirstMate .|. flagSecondMate) == flagSecondMate+        newflags = (if is_merged then eflagMerged else 0) .|. (if is_trimmed then eflagTrimmed else 0)++        -- Extended flags, renamed to avoid collision with BWA.  Goes like this:  if FF is there, use+        -- it.  Else check if XF is there __and is numeric__.  If so, use it, remove it, and set FF+        -- instead.  Else use 0 and leave it alone.  Note that this resolves the collision with BWA,+        -- since BWA puts a character there, not an int.+        cleaned_exts = case (lookup "FF" (b_exts b), lookup "XF" (b_exts b)) of+                ( Just (Int i), _ ) -> updateE "FF" (Int (i .|. newflags))                (b_exts b)+                ( _, Just (Int i) ) -> updateE "FF" (Int (i .|. newflags)) $ deleteE "XF" (b_exts b)+                _ | newflags /= 0   -> updateE "FF" (Int        newflags )                (b_exts b)+                  | otherwise       ->                                                     b_exts b+++-- | Fixes typical inconsistencies produced by Bwa: sometimes, 'mate unmapped' should be set, and we+-- can see it, because we match the mate's coordinates.  Sometimes 'properly paired' should not be+-- set, because one mate is unmapped.  This function is generally safe, but needs to be called only+-- on the output of affected (older?) versions of Bwa.+fixupBwaFlags :: BamRec -> BamRec+fixupBwaFlags b = b { b_flag = fixPP $ b_flag b .|. if mu then flagMateUnmapped else 0 }+  where+        -- Set "mate unmapped" if self coordinates and mate coordinates are equal, but self is+        -- paired and mapped.  (BWA forgets this flag for invalid mate alignments)+        mu = and [ isPaired b, not (isUnmapped b)+                 , isReversed b == isMateReversed b+                 , b_rname b == b_mrnm b, b_pos b == b_mpos b ]++        -- If either mate is unmapped, remove "properly paired".+        fixPP f | f .&. (flagUnmapped .|. flagMateUnmapped) == 0 = f+                | otherwise = f .&. complement flagProperlyPaired+++-- | Removes syntactic warts from old read names or the read names used+-- in FastQ files.+removeWarts :: BamRec -> BamRec+removeWarts br = br { b_qname = name, b_flag = flags, b_exts = tags }+  where+    (name, flags, tags) = checkFR $ checkC $ checkSharp (b_qname br, b_flag br, b_exts br)++    checkFR (n,f,t) | "F_" `S.isPrefixOf` n = checkC (S.drop 2 n, f .|. flagFirstMate  .|. flagPaired, t)+                    | "R_" `S.isPrefixOf` n = checkC (S.drop 2 n, f .|. flagSecondMate .|. flagPaired, t)+                    | "M_" `S.isPrefixOf` n = checkC (S.drop 2 n, f,   insertE "FF" (Int  eflagMerged) t)+                    | "T_" `S.isPrefixOf` n = checkC (S.drop 2 n, f,   insertE "FF" (Int eflagTrimmed) t)+                    | "/1" `S.isSuffixOf` n =        ( rdrop 2 n, f .|. flagFirstMate  .|. flagPaired, t)+                    | "/2" `S.isSuffixOf` n =        ( rdrop 2 n, f .|. flagSecondMate .|. flagPaired, t)+                    | otherwise             =        (         n, f,                                   t)++    checkC (n,f,t) | "C_" `S.isPrefixOf` n  = (S.drop 2 n, f, insertE "XP" (Int (-1)) t)+                   | otherwise              = (         n, f,                         t)++    rdrop n s = S.take (S.length s - n) s++    checkSharp (n,f,t) = case S.split '#' n of [n',ts] -> (n', f, insertTags ts t)+                                               _       -> ( n, f,               t)++    insertTags ts t | S.null y  = insertE "XI" (Text ts) t+                    | otherwise = insertE "XI" (Text  x) $ insertE "XJ" (Text $ S.tail y) t+        where (x,y) = S.break (== ',') ts++
+ Bio/Bam/Fastq.hs view
@@ -0,0 +1,121 @@+-- | Parser for @FastA/FastQ@, 'ByteStream' style, based on+-- "Data.Attoparsec", and written such that it is compatible with module+-- "Bio.Bam".  This gives import of @FastA/FastQ@ while respecting some+-- local (to MPI EVAN) conventions.++module Bio.Bam.Fastq ( parseFastq, parseFastqWith, parseFastqCassava ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude hiding ( isSpace )+import Bio.Streaming+import Bio.Streaming.Parse ( parse, atto )+import Data.Attoparsec.ByteString.Char8+        ( char, skipSpace, satisfy, inClass, skipWhile, takeTill+        , scan, isSpace, isSpace_w8, (<?>) )++import qualified Data.Attoparsec.ByteString.Char8   as P+import qualified Data.ByteString                    as B+import qualified Data.ByteString.Char8              as C+import qualified Data.Vector.Generic                as V+import qualified Streaming.Prelude                  as Q+import qualified Bio.Streaming.Bytes                as S++-- | Reader for DNA (not protein) sequences in FastA and FastQ.  We read+-- everything vaguely looking like FastA or FastQ, then shoehorn it into+-- a BAM record.  We strive to extract information following more or+-- less established conventions from the header, but don't aim for+-- completeness.  The recognized syntactical warts are converted into+-- appropriate flags and removed.  Only the canonical variant of FastQ+-- is supported (qualities stored as raw bytes with offset 33).+--+-- Supported additional conventions:+--+-- * A name suffix of @/1@ or @/2@ is turned into the first mate or second+--   mate flag and the read is flagged as paired.+--+-- * Same for name prefixes of @F_@ or @R_@, respectively.+--+-- * A name prefix of @M_@ flags the sequence as unpaired and merged+--+-- * A name prefix of @T_@ flags the sequence as unpaired and trimmed+--+-- * A name prefix of @C_@, optionally before or after any of the other+--   prefixes, is turned into the extra flag @XP:i:-1@ (result of+--   duplicate removal with unknown duplicate count).+--+-- * A collection of tags separated from the name by an octothorpe is+--   removed and put into the fields @XI@ and @XJ@ as text.+--+-- Everything before the first sequence header is ignored.  Headers can+-- start with @\>@ or @\@@, we treat both equally.  The first word of+-- the header becomes the read name, the remainder of the header is+-- ignored.  The sequence can be split across multiple lines;+-- whitespace, dashes and dots are ignored, IUPAC-IUB ambiguity codes+-- are accepted as bases, anything else causes an error.  The sequence+-- ends at a line that is either a header or starts with @\+@, in the+-- latter case, that line is ignored and must be followed by quality+-- scores.  There must be exactly as many Q-scores as there are bases,+-- followed immediately by a header or end-of-file.  Whitespace is+-- ignored.++parseFastq :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)+parseFastq = parseFastqWith (const id)++-- | Like 'parseFastq', but also+--+-- * If the first word of the description has at least four colon+--   separated subfields, the first is used to flag first/second mate,+--   the second is the \"QC failed\" flag, and the fourth is the index+--   sequence.++parseFastqCassava :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)+parseFastqCassava = parseFastqWith (pdesc . C.split ':' . C.takeWhile (' ' /=))+  where+    pdesc (num:flg:_:idx:_) br = br { b_flag = sum [ if num == "1" then flagFirstMate .|. flagPaired else 0+                                                   , if num == "2" then flagSecondMate .|. flagPaired else 0+                                                   , if flg == "Y" then flagFailsQC else 0+                                                   , b_flag br .&. complement (flagFailsQC .|. flagSecondMate .|. flagPaired) ]+                                    , b_exts = if C.all (`C.elem` "ACGTN") idx then insertE "XI" (Text idx) (b_exts br) else b_exts br }+    pdesc _ br = br++-- | Same as 'parseFastq', but a custom function can be applied to the+-- description string (the part of the header after the sequence name),+-- which can modify the parsed record.  Note that the quality field can+-- end up empty.++parseFastqWith :: Monad m => (Bytes -> BamRec -> BamRec) -> ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)+parseFastqWith descr = Q.unfoldr (liftM twiddle . parse (const $ atto pRec)) . skipJunk+  where+    twiddle        (Left  e)  = Left (Left  e)+    twiddle (Right (Left  r)) = Left (Right r)+    twiddle (Right (Right a)) = Right a++    isCBase   = inClass "ACGTUBDHVSWMKRYNacgtubdhvswmkryn"+    canSkip c = isSpace c || c == '.' || c == '-'+    isHdr   c = c == '@' || c == '>'++    pRec   = (satisfy isHdr <?> "start marker") *> (makeRecord <$> pName <*> (descr <$> P.takeWhile ('\n' /=)) <*> (pSeq >>= pQual))+    pName  = takeTill isSpace <* skipWhile (\c -> c /= '\n' && isSpace c)  <?> "read name"+    pSeq   =     (:) <$> satisfy isCBase <*> pSeq+             <|> satisfy canSkip *> pSeq+             <|> pure []                                                   <?> "sequence"++    pQual sq = (,) sq <$> (char '+' *> skipWhile ('\n' /=) *> pQual' (length sq) <* skipSpace <|> return C.empty)  <?> "qualities"+    pQual' n = B.filter (not . isSpace_w8) <$> scan n step+    step 0 _ = Nothing+    step i c | isSpace c = Just i+             | otherwise = Just (i-1)++skipJunk :: Monad m => ByteStream m r -> ByteStream m r+skipJunk = lift . S.nextByte >=> check+  where+    check (Right (c,s)) | bad c     = skipJunk . S.drop 1 . S.dropWhile (c2w '\n' /=) $ s+                        | otherwise = S.cons c s+    check (Left r)                  = pure r+    bad c = c /= c2w '>' && c /= c2w '@'++makeRecord :: Bytes -> (BamRec->BamRec) -> (String, Bytes) -> BamRec+makeRecord name extra (sq,qual) = extra $ nullBamRec+        { b_qname = name, b_seq = V.fromList $ read sq, b_qual = V.fromList $ map (Q . subtract 33) $ B.unpack qual }+
+ Bio/Bam/Filter.hs view
@@ -0,0 +1,121 @@+-- | Quality filters adapted from prehistoric pipeline.++module Bio.Bam.Filter (+    filterPairs, QualFilter,+    complexSimple, complexEntropy,+    qualityAverage, qualityMinimum,+    qualityFromOldIllumina, qualityFromNewIllumina+                      ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude+import Bio.Streaming++import qualified Data.Vector.Generic as V++-- | A filter/transformation applied to pairs of reads.  We supply a+-- predicate to be applied to single reads and one to be applied to+-- pairs, the latter can get incomplete pairs, too, if mates have been+-- separated or filtered asymmetrically.  This fails spectacularly if+-- the input isn't grouped by name.++filterPairs :: Monad m => (BamRec -> [BamRec])+                       -> (Maybe BamRec -> Maybe BamRec -> [BamRec])+                       -> Stream (Of BamRec) m r -> Stream (Of BamRec) m r+filterPairs ps pp = step+  where+    step = lift . inspect >=> either pure step'+    step' (b :> s)+        | isPaired b = lift (inspect s) >>= step'' b+        | otherwise  = each (ps b) >> step s++    step'' b (Left r) = each (pp (Just b) Nothing) >> pure r++    step'' b (Right (c :> s))+        | b_rname b /= b_rname c || not (isPaired c) =+                let b' = if isSecondMate b then pp Nothing (Just b) else pp (Just b) Nothing+                in each b' >> step' (c :> s)++        | isFirstMate c && isSecondMate b = step''' c b s+        | otherwise                       = step''' b c s++    step''' b c s = each (pp (Just b) (Just c)) >> step s+++-- | A quality filter is simply a transformation on @BamRec@s.  By+-- convention, quality filters should set @flagFailsQC@, a further step+-- can then remove the failed reads.  Filtering of individual reads+-- tends to result in mate pairs with inconsistent flags, which in turn+-- will result in lone mates and all sort of troubles with programs that+-- expect non-broken BAM files.  It is therefore recommended to use+-- @pairFilter@ with suitable predicates to do the post processing.++type QualFilter = BamRec -> BamRec++{-# INLINE count #-}+count :: (V.Vector v a, Eq a) => a -> v a -> Int+count x = V.foldl' (\acc y -> if x == y then acc+1 else acc) 0++-- | Simple complexity filter aka "Nancy Filter".  A read is considered+-- not-sufficiently-complex if the most common base accounts for greater+-- than the @cutoff@ fraction of all non-N bases.+{-# INLINE complexSimple #-}+complexSimple :: Double -> QualFilter+complexSimple r b = if p then b else b'+  where+    b' = setQualFlag 'C' $ b { b_flag = b_flag b .|. flagFailsQC }+    p  = let counts = [ count x $ b_seq b | x <- properBases ]+             lim = floor $ r * fromIntegral (sum counts)+         in all (<= lim) counts++-- | Filter on order zero empirical entropy.  Entropy per base must be+-- greater than cutoff.+{-# INLINE complexEntropy #-}+complexEntropy :: Double -> QualFilter+complexEntropy r b = if p then b else b'+  where+    b' = setQualFlag 'C' $ b { b_flag = b_flag b .|. flagFailsQC }+    p = ent >= r * total++    counts = [ count x $ b_seq b | x <- properBases ]+    total = fromIntegral $ V.length $ b_seq b+    ent   = sum [ fromIntegral c * log (total / fromIntegral c) | c <- counts, c /= 0 ] / log 2++-- | Filter on average quality.  Reads without quality string pass.+{-# INLINE qualityAverage #-}+qualityAverage :: Int -> QualFilter+qualityAverage q b = if p then b else b'+  where+    b' = setQualFlag 'Q' $ b { b_flag = b_flag b .|. flagFailsQC }+    p  = let total = V.foldl' (\a x -> a + fromIntegral (unQ x)) 0 $ b_qual b+         in total >= q * V.length (b_qual b)++-- | Filter on minimum quality.  In @qualityMinimum n q@, a read passes+-- if it has no more than @n@ bases with quality less than @q@.  Reads+-- without quality string pass.+{-# INLINE qualityMinimum #-}+qualityMinimum :: Int -> Qual -> QualFilter+qualityMinimum n (Q q) b = if p then b else b'+  where+    b' = setQualFlag 'Q' $ b { b_flag = b_flag b .|. flagFailsQC }+    p  = V.length (V.filter (< Q q) (b_qual b)) <= n+++-- | Convert quality scores from old Illumina scale (different formula+-- and offset 64 in FastQ).+qualityFromOldIllumina :: BamRec -> BamRec+qualityFromOldIllumina b = b { b_qual = V.map conv $ b_qual b }+  where+    conv (Q s) = let s' :: Double+                     s' = exp $ log 10 * (fromIntegral s - 31) / (-10)+                     p  = s' / (1+s')+                     q  = - 10 * log p / log 10+                 in Q (round q)++-- | Convert quality scores from new Illumina scale (standard formula+-- but offset 64 in FastQ).+qualityFromNewIllumina :: BamRec -> BamRec+qualityFromNewIllumina b = b { b_qual = V.map (Q . subtract 31 . unQ) $ b_qual b }++
+ Bio/Bam/Header.hs view
@@ -0,0 +1,570 @@+{-# LANGUAGE UndecidableInstances #-}+module Bio.Bam.Header (+        BamMeta(..),+        parseBamMeta,+        showBamMeta,+        addPG,++        BamKey(..),+        BamHeader(..),+        BamSQ(..),+        BamSorting(..),+        BamOtherShit,++        Refseq(..),+        invalidRefseq,+        isValidRefseq,+        invalidPos,+        isValidPos,+        unknownMapq,+        isKnownMapq,++        Refs(..),+        getRef,++        compareNames,++        flagPaired,+        flagProperlyPaired,+        flagUnmapped,+        flagMateUnmapped,+        flagReversed,+        flagMateReversed,+        flagFirstMate,+        flagSecondMate,+        flagAuxillary,+        flagSecondary,+        flagFailsQC,+        flagDuplicate,+        flagSupplementary,+        eflagTrimmed,+        eflagMerged,+        eflagAlternative,+        eflagExactIndex,++        distinctBin,++        MdOp(..),+        readMd,+        showMd+    ) where++import Bio.Prelude           hiding ( uncons )+import Bio.Util.Nub+import Control.Monad.Trans.RWS+import Data.ByteString              ( uncons )+import Data.ByteString.Builder      ( Builder, byteString, char7, intDec, word16LE )++import qualified Data.Attoparsec.ByteString.Char8   as P+import qualified Data.ByteString.Char8              as S+import qualified Data.HashMap.Strict                as H+import qualified Data.Vector                        as V++data BamMeta = BamMeta {+        meta_hdr :: !BamHeader,+        meta_refs :: !Refs,+        meta_pgs :: [Fix BamPG],+        meta_other_shit :: [(BamKey, BamOtherShit)],+        meta_comment :: [Bytes]+    } deriving ( Show, Generic )++-- | Exactly two characters, for the \"named\" fields in bam.+newtype BamKey = BamKey Word16+    deriving ( Eq, Ord, Hashable, Generic )++instance IsString BamKey where+    {-# INLINE fromString #-}+    fromString [a,b]+        | ord a < 256 && ord b < 256+            = BamKey . fromIntegral $ ord a .|. shiftL (ord b) 8++    fromString s+            = error $ "Not a legal BAM key: " ++ show s++instance Show BamKey where+    show (BamKey a) = [ chr (fromIntegral a .&. 0xff), chr (shiftR (fromIntegral a) 8 .&. 0xff) ]++-- | Adds a new program line to a header.  The new entry is+-- (arbitrarily) prepended to the first existing chain, or forms a new+-- singleton chain if none exists.++addPG :: Maybe Version -> IO (BamMeta -> BamMeta)+addPG vn = do+    args <- getArgs+    pn   <- getProgName++    let more = ("PN", S.pack pn) :+               ("CL", S.pack $ unwords args) :+               maybe [] (\v -> [("VN",S.pack (showVersion v))]) vn++    return $ \bm -> case meta_pgs bm of+        [    ] -> bm { meta_pgs = Fix (BamPG (S.pack pn)  Nothing  more) : [ ] }+        pg:pgs -> bm { meta_pgs = Fix (BamPG (S.pack pn) (Just pg) more) : pgs }+++instance Semigroup BamMeta where (<>)    = combineBamMeta+instance Monoid    BamMeta where mempty  = BamMeta mempty mempty mempty [] []+                                 mappend = (<>)++{- | Combines two bam headers into one.++The overarching goal is to combine headers in such a way that no+information is lost, but redundant information is removed.  In+particular, we sometimes \"merge\" headers with the same references, at+other times we \"meld\" headers with entirely different references.  In+the former case, we must concatenate the reference lists, in the latter+case we want to keep it as is.++* If both headers have a version number, the result is the smaller of+  the two.++* The resulting sort order is the most specific one compatible with both+  input sort orders.  The stupid 'Unknown' state is compatible with+  everything.++* Reference sequences are appended and run through 'nub'.  The numbering+  of reference may thus change, which has to be dealt with in an+  appropriate way, see 'concatInputs', 'mergeInputsOn', and \"bam-meld\"+  for details.  (It is also possible that different sequences are left+  with the same name.  We cannot solve this right here, and there is no+  reliable way to do it in general.)++* Comments are appended and run through 'nub'.  This should work in+  most case, and if it doesn't, someone needs to \"samtools reheader\"+  the file anyway.++* Program chains are just collected, but when formatting, they are+  (effectively) run through 'nub' and are potentially assigned new+  unique identifiers.+-}+combineBamMeta :: BamMeta -> BamMeta -> BamMeta+combineBamMeta a b = BamMeta+    { meta_hdr        = meta_hdr a <> meta_hdr b+    , meta_refs       = meta_refs a `mappend` meta_refs b+    , meta_pgs        = meta_pgs a <> meta_pgs b+    , meta_other_shit = nubHash $ meta_other_shit a ++ meta_other_shit b+    , meta_comment    = nubHash $ meta_comment a ++ meta_comment b }++data BamHeader = BamHeader {+        hdr_version :: (Int, Int),+        hdr_sorting :: BamSorting,+        hdr_other_shit :: BamOtherShit+    } deriving (Show, Eq)++instance Monoid BamHeader where+    mempty = BamHeader (1,0) Unknown []+    mappend = (<>)++instance Semigroup BamHeader where+    a <> b = BamHeader { hdr_version    = max (hdr_version a) (hdr_version b)+                       , hdr_sorting    = hdr_sorting a <> hdr_sorting b+                       , hdr_other_shit = nubHashBy fst $ hdr_other_shit a ++ hdr_other_shit b }++data BamSQ = BamSQ {+        sq_name :: Bytes,+        sq_length :: Int,+        sq_other_shit :: BamOtherShit+    } deriving (Show, Eq, Generic)++instance Hashable BamSQ++bad_seq :: BamSQ+bad_seq = BamSQ (error "no SN field") (error "no LN field") []++data BamPG pp = BamPG {+        pg_pref_name :: Bytes,+        pg_prev_pg :: Maybe pp,+        pg_other_shit :: BamOtherShit+    } deriving (Show, Eq, Generic1)++newtype Fix f = Fix (f (Fix f))++instance Eq (f (Fix f)) => Eq (Fix f) where+    Fix f == Fix g  =  f == g++instance Show (f (Fix f)) => Show (Fix f) where+    showsPrec p (Fix f) = showsPrec p f++instance Hashable1 f => Hashable (Fix f) where+    hashWithSalt salt (Fix f) = liftHashWithSalt hashWithSalt salt f++instance Hashable1 BamPG++bad_pg :: BamPG pp+bad_pg = BamPG (error "no ID field") Nothing []+++-- | Possible sorting orders from bam header.  Thanks to samtools, which+-- doesn't declare sorted files properly, we have to have the stupid+-- 'Unknown' state, too.+data BamSorting = Unknown       -- ^ undeclared sort order+                | Unsorted      -- ^ definitely not sorted+                | Grouped       -- ^ grouped by query name+                | Queryname     -- ^ sorted by query name+                | Coordinate    -- ^ sorted by coordinate+    deriving (Show, Eq)++instance Semigroup BamSorting where+    Unknown    <>          b  =  b+    a          <>    Unknown  =  a+    Grouped    <>    Grouped  =  Grouped+    Grouped    <>  Queryname  =  Grouped+    Queryname  <>    Grouped  =  Grouped+    Queryname  <>  Queryname  =  Queryname+    Coordinate <> Coordinate  =  Coordinate+    _          <>          _  =  Unsorted+++type BamOtherShit = [(BamKey, Bytes)]++parseBamMeta :: P.Parser BamMeta+parseBamMeta = fixupMeta . foldl' (flip ($)) emptyHeader+               <$> many (parseBamMetaLine <* P.skipWhile (=='\t') <* P.char '\n')++-- Bam header in the process of being parsed.  Better suited for+-- collecting lines than 'BamMeta'.+data PreBamMeta = PreBamMeta {+        pmeta_hdr        :: BamHeader,+        pmeta_refs       :: [BamSQ],+        pmeta_pgs        :: HashMap Bytes (BamPG Bytes),+        pmeta_other_shit :: [(BamKey, BamOtherShit)],+        pmeta_comment    :: [Bytes] }++emptyHeader :: PreBamMeta+emptyHeader = PreBamMeta mempty [] H.empty [] []+++-- | Fixes a bam header after parsing.  It turns accumulated lists in to+-- vectors, and it handles the program lines.  Program lines come in as+-- an arbitrary graph.  It chould be a linear chain, but this+-- isn't guaranteed in practice.  We decompose the graph into chains by+-- tracing from nodes with no predecessor, or from an arbitrary node if+-- all nodes have predecessors.  Tracing stops once it would form a+-- cycle.+fixupMeta :: PreBamMeta -> BamMeta+fixupMeta PreBamMeta{..} = BamMeta+    { meta_hdr        = pmeta_hdr+    , meta_refs       = Refs . V.fromList . reverse $ pmeta_refs+    , meta_pgs        = snd $ evalRWS trace_pgs () pmeta_pgs+    , meta_other_shit = reverse pmeta_other_shit+    , meta_comment    = reverse pmeta_comment  }+  where+    -- keep tracing from roots until no nodes are left+    trace_pgs :: RWS () [Fix BamPG] (HashMap Bytes (BamPG Bytes)) ()+    trace_pgs = do+        gg <- get+        case foldl' (flip H.delete) gg+                    [ pp | p <- H.elems gg+                         , pp <- maybe [] pure (pg_prev_pg p) ] of+          orphans+            -- the empty graph has no roots:+            | H.null gg      -> return ()+            -- an arbitrary node is picked as root:+            | H.null orphans -> trace_pg H.empty (head $ H.keys gg) >> trace_pgs+            -- nodes without parents are roots:+            | otherwise      -> mapM_ (trace_pg H.empty) (H.keys orphans) >> trace_pgs++    -- Trace one PG line.  Do not trace into nodes in the 'closed' set,+    -- remove reached nodes from the 'open' set (the state) and add them+    -- to the 'closed' set.+    trace_pg :: HashMap Bytes () -> Bytes -> RWS () [Fix BamPG] (HashMap Bytes x) (Maybe (Fix BamPG))+    trace_pg closed name =+        case H.lookup name pmeta_pgs of+            _ | H.member name closed -> return Nothing+            Nothing                  -> return Nothing+            Just pg -> do+                modify $ H.delete name+                pp <- mapM (trace_pg (H.insert name () closed)) (pg_prev_pg pg)+                let self = Fix $ pg { pg_prev_pg = join pp }+                tell [ self ]+                return $ Just self+++parseBamMetaLine :: P.Parser (PreBamMeta -> PreBamMeta)+parseBamMetaLine = P.char '@' >> P.choice [hdLine, sqLine, pgLine, coLine, otherLine]+  where+    hdLine = P.string "HD\t" >>+             (\fns meta -> meta { pmeta_hdr = foldr ($) (pmeta_hdr meta) fns })+               <$> P.sepBy1 (P.choice [hdvn, hdso, hdother]) tabs++    sqLine = P.string "SQ\t" >>+             (\fns meta -> meta { pmeta_refs = foldr ($) bad_seq fns : pmeta_refs meta })+               <$> P.sepBy1 (P.choice [sqnm, sqln, sqother]) tabs++    pgLine = P.string "PG\t" >>+             (\fns meta -> let pg = foldr ($) bad_pg fns+                           in meta { pmeta_pgs = H.insert (pg_pref_name pg) pg (pmeta_pgs meta) })+               <$> P.sepBy1 (P.choice [pgid, pgpp, pgother]) tabs++    hdvn = P.string "VN:" >>+           (\a b hdr -> hdr { hdr_version = (a,b) })+             <$> P.decimal <*> ((P.char '.' <|> P.char ':') >> P.decimal)++    hdso = P.string "SO:" >>+           (\s hdr -> hdr { hdr_sorting = s })+             <$> P.choice [ Grouped     <$ P.string "grouped"+                          , Queryname   <$ P.string "queryname"+                          , Coordinate  <$ P.string "coordinate"+                          , Unsorted    <$ P.string "unsorted"+                          , Unknown     <$ P.skipWhile (\c -> c/='\t' && c/='\n') ]++    sqnm = P.string "SN:" >> (\s sq -> sq { sq_name = s }) <$> pall+    sqln = P.string "LN:" >> (\i sq -> sq { sq_length = i }) <$> P.decimal++    pgid = P.string "ID:" >> (\s pg -> pg { pg_pref_name =      s }) <$> pall+    pgpp = P.string "PP:" >> (\s pg -> pg { pg_prev_pg   = Just s }) <$> pall++    hdother = (\t hdr -> hdr { hdr_other_shit = t : hdr_other_shit hdr }) <$> tagother+    sqother = (\t sq  -> sq  { sq_other_shit  = t : sq_other_shit  sq  }) <$> tagother+    pgother = (\t p   -> p   { pg_other_shit  = t : pg_other_shit  p   }) <$> tagother++    coLine = P.string "CO\t" >>+             (\s meta -> s `seq` meta { pmeta_comment = s : pmeta_comment meta })+               <$> P.takeWhile (/= 'n')++    otherLine = (\k ts meta -> meta { pmeta_other_shit = (k,ts) : pmeta_other_shit meta })+                  <$> bamkey <*> (tabs >> P.sepBy1 tagother tabs)++    tagother :: P.Parser (BamKey,Bytes)+    tagother = (,) <$> bamkey <*> (P.char ':' >> pall)++    tabs = P.char '\t' >> P.skipWhile (== '\t')++    pall :: P.Parser Bytes+    pall = P.takeWhile (\c -> c/='\t' && c/='\n')++    bamkey :: P.Parser BamKey+    bamkey = (\a b -> fromString [a,b]) <$> P.anyChar <*> P.anyChar+++-- | Creates the textual form of Bam meta data.+--+-- Formatting is straight forward, only program lines are a bit+-- involved.  Our multiple chains may lead to common nodes, and we do+-- not want to print multiple identical lines.  At the same time, we may+-- need to print multiple different lines that carry the same id.  The+-- solution is to memoize printed lines, and to reuse their identity if+-- an identical line is needed.  When printing a line, it gets its+-- preferred identifier, but if it's already taken, a new identifier is+-- made up by first removing any trailing number and then by appending+-- numeric suffixes.++showBamMeta :: BamMeta -> Builder+showBamMeta (BamMeta h (Refs ss) pgs os cs) =+    show_bam_meta_hdr h <>+    foldMap show_bam_meta_seq ss <>+    show_bam_pgs <>+    foldMap show_bam_meta_other os <>+    foldMap show_bam_meta_comment cs+  where+    show_bam_meta_hdr (BamHeader (major,minor) so os') =+        "@HD\tVN:" <>+        intDec major <> char7 '.' <> intDec minor <>+        byteString (case so of Unsorted    -> "\tSO:unsorted"+                               Grouped     -> "\tSO:grouped"+                               Queryname   -> "\tSO:queryname"+                               Coordinate  -> "\tSO:coordinate"+                               Unknown     -> mempty) <>+        show_bam_others os'++    show_bam_meta_seq (BamSQ nm ln ts) =+        byteString "@SQ\tSN:" <> byteString nm <>+        byteString "\tLN:" <> intDec ln <> show_bam_others ts++    show_bam_meta_comment cm = byteString "@CO\t" <> byteString cm <> char7 '\n'++    show_bam_meta_other (BamKey k,ts) =+        char7 '@' <> word16LE k <> show_bam_others ts++    show_bam_others ts =+        foldMap show_bam_other ts <> char7 '\n'++    show_bam_other (BamKey k,v) =+        char7 '\t' <> word16LE k <> char7 ':' <> byteString v++    show_bam_pgs = snd $ evalRWS (mapM_ show_bam_pg pgs) () (H.empty, H.empty)++    show_bam_pg p@(Fix (BamPG pn pp po)) = do+        ppid <- case pp of Nothing -> return Nothing+                           Just p' -> Just <$> show_bam_pg p'++        gets (H.lookup p . fst) >>= \case+            Just pid -> return pid+            Nothing  -> do+                -- preferred name without a trailing dash-and-number+                let pn' = case dropWhile isDigit . reverse $ S.unpack pn of+                            '-':xs -> reverse xs+                            _      -> S.unpack pn++                -- find unused preferable PG:ID:  try prefered name,+                -- preferred name without number, preferred name+                -- without number and increasing numbers attached+                pid <- gets $ \(_,hs) ->+                            head . filter (not . flip H.member hs) $+                            pn : S.pack pn' : [ S.pack $ pn' ++ '-' : (show i) | i <- [2::Int ..] ]++                modify . first $ H.insert p pid+                modify . second $ H.insert pid ()++                tell $ byteString "@PG\tID:" <> byteString pid <>+                       maybe mempty (\x -> byteString "\tPP:" <> byteString x) ppid <>+                       show_bam_others po+                return pid+++-- | Reference sequence in Bam+-- Bam enumerates the reference sequences and then sorts by index.  We+-- need to track that index if we want to reproduce the sorting order.+newtype Refseq = Refseq { unRefseq :: Word32 } deriving (Eq, Ord, Ix, Bounded)++instance Show Refseq where+    showsPrec p (Refseq r) = showsPrec p r++instance Enum Refseq where+    succ = Refseq . succ . unRefseq+    pred = Refseq . pred . unRefseq+    toEnum = Refseq . fromIntegral+    fromEnum = fromIntegral . unRefseq+    enumFrom = map Refseq . enumFrom . unRefseq+    enumFromThen (Refseq a) (Refseq b) = map Refseq $ enumFromThen a b+    enumFromTo (Refseq a) (Refseq b) = map Refseq $ enumFromTo a b+    enumFromThenTo (Refseq a) (Refseq b) (Refseq c) = map Refseq $ enumFromThenTo a b c+++-- | Tests whether a reference sequence is valid.+-- Returns true unless the the argument equals @invalidRefseq@.+isValidRefseq :: Refseq -> Bool+isValidRefseq = (/=) invalidRefseq++-- | The invalid Refseq.+-- Bam uses this value to encode a missing reference sequence.+invalidRefseq :: Refseq+invalidRefseq = Refseq 0xffffffff++-- | The invalid position.+-- Bam uses this value to encode a missing position.+{-# INLINE invalidPos #-}+invalidPos :: Int+invalidPos = -1++-- | Tests whether a position is valid.+-- Returns true unless the the argument equals @invalidPos@.+{-# INLINE isValidPos #-}+isValidPos :: Int -> Bool+isValidPos = (/=) invalidPos++{-# INLINE unknownMapq #-}+unknownMapq :: Int+unknownMapq = 255++isKnownMapq :: Int -> Bool+isKnownMapq = (/=) unknownMapq++-- | A list of reference sequences.+newtype Refs = Refs { unRefs :: V.Vector BamSQ } deriving Show++instance Monoid Refs where+    mempty = Refs V.empty+    mappend = (<>)++instance Semigroup Refs where+    Refs a <> Refs b = Refs . V.fromList . nubHash $ V.toList a ++ V.toList b++getRef :: Refs -> Refseq -> BamSQ+getRef (Refs refs) (Refseq i) = fromMaybe (BamSQ "*" 0 []) $ refs V.!? fromIntegral i++flagPaired, flagProperlyPaired, flagUnmapped, flagMateUnmapped,+ flagReversed, flagMateReversed, flagFirstMate, flagSecondMate,+ flagAuxillary, flagSecondary, flagFailsQC, flagDuplicate,+ flagSupplementary :: Int++flagPaired         =   0x1+flagProperlyPaired =   0x2+flagUnmapped       =   0x4+flagMateUnmapped   =   0x8+flagReversed       =  0x10+flagMateReversed   =  0x20+flagFirstMate      =  0x40+flagSecondMate     =  0x80+flagAuxillary      = 0x100+flagSecondary      = 0x100+flagFailsQC        = 0x200+flagDuplicate      = 0x400+flagSupplementary  = 0x800++eflagTrimmed, eflagMerged, eflagAlternative, eflagExactIndex :: Int+eflagTrimmed     = 0x1+eflagMerged      = 0x2+eflagAlternative = 0x4+eflagExactIndex  = 0x8+++-- | Compares two sequence names the way samtools does.+-- samtools sorts by \"strnum_cmp\":+--+-- * if both strings start with a digit, parse the initial+--   sequence of digits and compare numerically, if equal,+--   continue behind the numbers+-- * else compare the first characters (possibly NUL), if equal+--   continue behind them+-- * else both strings ended and the shorter one counts as+--   smaller (and that part is stupid)++compareNames :: Bytes -> Bytes -> Ordering+compareNames n m = case (uncons n, uncons m) of+        ( Nothing, Nothing ) -> EQ+        ( Just  _, Nothing ) -> GT+        ( Nothing, Just  _ ) -> LT+        ( Just (c,n'), Just (d,m') )+            | is_digit c || is_digit d+            , Just (u,n'') <- S.readInt n+            , Just (v,m'') <- S.readInt m ->+                case u `compare` v of+                    LT -> LT+                    GT -> GT+                    EQ -> n'' `compareNames` m''+            | otherwise ->+                case c `compare` d of+                    LT -> LT+                    GT -> GT+                    EQ -> n' `compareNames` m'+  where+    is_digit c = c2w '0' <= c && c <= c2w '9'+++data MdOp = MdNum Int | MdRep Nucleotides | MdDel [Nucleotides] deriving Show++readMd :: Bytes -> Maybe [MdOp]+readMd s | S.null s           = return []+         | isDigit (S.head s) = do (n,t) <- S.readInt s+                                   (MdNum n :) <$> readMd t+         | S.head s == '^'    = let (a,b) = S.break isDigit (S.tail s)+                                in (MdDel (map toNucleotides $ S.unpack a) :) <$> readMd b+         | otherwise          = (MdRep (toNucleotides $ S.head s) :) <$> readMd (S.tail s)++-- | Normalizes a series of 'MdOp's and encodes them in the way BAM and+-- SAM expect it.+showMd :: [MdOp] -> Bytes+showMd = S.pack . flip s1 []+  where+    s1 (MdNum  i : MdNum  j : ms) = s1 (MdNum (i+j) : ms)+    s1 (MdNum  0            : ms) = s1 ms+    s1 (MdNum  i            : ms) = shows i . s1 ms++    s1 (MdRep  r            : ms) = shows r . s1 ms++    s1 (MdDel d1 : MdDel d2 : ms) = s1 (MdDel (d1++d2) : ms)+    s1 (MdDel []            : ms) = s1 ms+    s1 (MdDel ns : MdRep  r : ms) = (:) '^' . shows ns . (:) '0' . shows r . s1 ms+    s1 (MdDel ns            : ms) = (:) '^' . shows ns . s1 ms+    s1 [                        ] = id+++-- | Computes the "distinct bin" according to the BAM binning scheme.  If+-- an alignment starts at @pos@ and its CIGAR implies a length of @len@+-- on the reference, then it goes into bin @distinctBin pos len@.+distinctBin :: Int -> Int -> Int+distinctBin beg len = mkbin 14 $ mkbin 17 $ mkbin 20 $ mkbin 23 $ mkbin 26 0+  where end = beg + len - 1+        mkbin n x = if beg `shiftR` n /= end `shiftR` n then x+                    else ((1 `shiftL` (29-n))-1) `div` 7 + (beg `shiftR` n)
+ Bio/Bam/Index.hs view
@@ -0,0 +1,384 @@+module Bio.Bam.Index (+    BamIndex(..),+    withIndexedBam,+    readBamIndex,+    readBaiIndex,+    readTabix,++    Region(..),+    Subsequence(..),+    streamBamRefseq,+    streamBamRegions,+    streamBamSubseq,+    streamBamUnaligned+) where++import Bio.Bam.Header+import Bio.Bam.Reader+import Bio.Bam.Rec+import Bio.Bam.Regions              ( Region(..), Subsequence(..) )+import Bio.Prelude+import Bio.Streaming+import Bio.Streaming.Bgzf           ( bgunzip )+import System.Directory             ( doesFileExist )++import qualified Bio.Bam.Regions                as R+import qualified Bio.Streaming.Bytes            as S+import qualified Bio.Streaming.Parse            as P+import qualified Data.IntMap.Strict             as M+import qualified Data.ByteString                as B+import qualified Data.Vector                    as V+import qualified Data.Vector.Mutable            as W+import qualified Data.Vector.Unboxed            as U+import qualified Data.Vector.Unboxed.Mutable    as N+import qualified Data.Vector.Algorithms.Intro   as N+import qualified Streaming.Prelude              as Q++-- | Full index, unifying BAI and CSI style.  In both cases, we have the+-- binning scheme, parameters are fixed in BAI, but variable in CSI.+-- Checkpoints are created from the linear index in BAI or from the+-- `loffset' field in CSI.++data BamIndex a = BamIndex {+    -- | Minshift parameter from CSI+    minshift :: {-# UNPACK #-} !Int,++    -- | Depth parameter from CSI+    depth :: {-# UNPACK #-} !Int,++    -- | Best guess at where the unaligned records start+    unaln_off :: {-# UNPACK #-} !Int64,++    -- | Room for stuff (needed for tabix)+    extensions :: a,++    -- | Records for the binning index, where each bin has a list of+    -- segments belonging to it.+    refseq_bins :: {-# UNPACK #-} !(V.Vector Bins),++    -- | Known checkpoints of the form (pos,off) where off is the+    -- virtual offset of the first record crossing pos.+    refseq_ckpoints :: {-# UNPACK #-} !(V.Vector Ckpoints) }++  deriving Show++-- | Mapping from bin number to vector of clusters.+type Bins = IntMap Segments+type Segments = U.Vector (Int64,Int64)+++-- | Checkpoints.  Each checkpoint is a position with the virtual offset+-- where the first alignment crossing the position is found.  In BAI, we+-- get this from the 'ioffset' vector, in CSI we get it from the+-- 'loffset' field:  "Given a region [beg,end), we only need to visit+-- chunks whose end file offset is larger than 'ioffset' of the 16kB+-- window containing 'beg'."  (Sounds like a marginal gain, though.)++type Ckpoints = IntMap Int64+++-- | Decode only those reads that fall into one of several regions.+-- Strategy:  We will scan the file mostly linearly, but only those+-- regions that are actually needed.  We filter the decoded stuff so+-- that it actually overlaps our regions.+--+-- From the binning index, we get a list of segments per requested+-- region.  Using the checkpoints, we prune them:  if we have a+-- checkpoint to the left of the beginning of the interesting region, we+-- can move the start of each segment forward to the checkpoint.  If+-- that makes the segment empty, it can be droppped.+--+-- The resulting segment lists are merged, then traversed.  We seek to+-- the beginning of the earliest segment and start decoding.  Once the+-- virtual file position leaves the segment or the alignment position+-- moves past the end of the requested region, we move to the next.+-- Moving is a seek if it spans a sufficiently large gap or points+-- backwards, else we just keep going.++-- | A 'Segment' has a start and an end offset, and an "end coordinate"+-- from the originating region.+data Segment = Segment {-# UNPACK #-} !Int64 {-# UNPACK #-} !Int64 {-# UNPACK #-} !Int deriving Show++segmentLists :: BamIndex a -> Refseq -> R.Subsequence -> [[Segment]]+segmentLists bi@BamIndex{..} (Refseq ref) (R.Subsequence imap)+        | Just bins <- refseq_bins V.!? fromIntegral ref,+          Just cpts <- refseq_ckpoints V.!? fromIntegral ref+        = [ rgnToSegments bi beg end bins cpts | (beg,end) <- M.toList imap ]+segmentLists _ _ _ = []++-- from region to list of bins, then to list of segments+rgnToSegments :: BamIndex a -> Int -> Int -> Bins -> Ckpoints -> [Segment]+rgnToSegments bi@BamIndex{..} beg end bins cpts =+    [ Segment boff' eoff end+    | bin <- binList bi beg end+    , (boff,eoff) <- maybe [] U.toList $ M.lookup bin bins+    , let boff' = max boff cpt+    , boff' < eoff ]+  where+    !cpt = maybe 0 snd $ M.lookupLE beg cpts++-- list of bins for given range of coordinates, from Heng's horrible code+binList :: BamIndex a -> Int -> Int -> [Int]+binList BamIndex{..} beg end = binlist' 0 (minshift + 3*depth) 0+  where+    binlist' l s t = if l > depth then [] else [b..e] ++ go+      where+        b = t + beg `shiftR` s+        e = t + (end-1) `shiftR` s+        go = binlist' (l+1) (s-3) (t + 1 `shiftL` (3*l))+++-- | Merges two lists of segments.  Lists must be sorted, the merge sort+-- merges overlapping segments into one.+infix 4 ~~+(~~) :: [Segment] -> [Segment] -> [Segment]+Segment a b e : xs ~~ Segment u v f : ys+    |          b < u = Segment a b e : (xs ~~ Segment u v f : ys)     -- no overlap+    | a < u && b < v = Segment a v (max e f) : (xs ~~ ys)             -- some overlap+    |          b < v = Segment u v (max e f) : (xs ~~ ys)             -- contained+    | v < a          = Segment u v f : (xs ~~ Segment a b e : ys)     -- no overlap+    | u < a          = Segment u b (max e f) : (xs ~~ ys)             -- some overlap+    | otherwise      = Segment a b (max e f) : (xs ~~ ys)             -- contained+[] ~~ ys = ys+xs ~~ [] = xs+++data IndexFormatError = IndexFormatError Bytes deriving Typeable++instance Exception IndexFormatError+instance Show IndexFormatError where+    show (IndexFormatError m) = "index signature " ++ show m ++ " not recognized"++{- | Reads any index we can find for a file.++If the file name has a .bai or .csi extension, optionally followed by+.gz, we read it.  Else we look for the index by adding such an extension+and by replacing the extension with these two, and finally try the file+itself.  The first file that exists is used.+-}+readBamIndex :: FilePath -> IO (BamIndex ())+readBamIndex fp1 | any (`isSuffixOf` fp1) exts = streamFile fp1 readBaiIndex+                 | otherwise                   = tryAll exts+  where+    exts = words ".bai .bai.gz .csi .csi.gz"++    fp2 = reverse $ case dropWhile (/='.') f of [] -> f++d ; _:b -> b++d+    (f,d) = break (=='/') $ reverse fp1++    tryAll [    ] = streamFile fp1 readBaiIndex+    tryAll (e:es) = do x1 <- liftIO $ doesFileExist (fp1 ++ e)+                       x2 <- liftIO $ doesFileExist (fp2 ++ e)+                       case () of+                            _ | x1 -> streamFile (fp1 ++ e) readBaiIndex+                              | x2 -> streamFile (fp2 ++ e) readBaiIndex+                            _      -> tryAll es++-- | Reads an index in BAI or CSI format, recognized automatically.  The+-- index can be compressed, even though this isn't standard.+readBaiIndex :: MonadIO m => ByteStream m r -> m (BamIndex ())+readBaiIndex = either (const . liftIO $ throwM P.EofException) (return . fst) <=<+               P.parseIO (const $ P.getString 4 >>= switch) . S.gunzip+  where+    switch "BAI\1" = do nref <- fromIntegral `liftM` P.getWord32+                        getIndexArrays nref 14 5 (const return) getIntervals++    switch "CSI\1" = do minshift <- fromIntegral `liftM` P.getWord32+                        depth <- fromIntegral `liftM` P.getWord32+                        P.getWord32 >>= P.drop . fromIntegral -- aux data+                        nref <- fromIntegral `liftM` P.getWord32+                        getIndexArrays nref minshift depth (addOneCheckpoint minshift depth) return++    switch magic   = throwM $ IndexFormatError magic+++    -- Insert one checkpoint.  If we already have an entry (can happen+    -- if it comes from a different bin), we conservatively take the min+    addOneCheckpoint minshift depth bin cp = do+            loffset <- fromIntegral `liftM` P.getWord64+            let key = llim (fromIntegral bin) (3*depth) minshift+            return $! M.insertWith min key loffset cp++    -- compute left limit of bin+    llim bin dp sf | dp  ==  0 = 0+                   | bin >= ix = (bin - ix) `shiftL` sf+                   | otherwise = llim bin (dp-3) (sf+3)+            where ix = (1 `shiftL` dp - 1) `div` 7++withIndexedBam :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> BamIndex () -> Handle -> m r) -> m r+withIndexedBam f k = do+    idx <- liftIO $ readBamIndex f+    bracket (liftIO $ openBinaryFile f ReadMode) (liftIO . hClose) $ \hdl -> do+        (hdr,_) <- decodeBam $ streamHandle hdl+        k hdr idx hdl+++type TabIndex = BamIndex TabMeta++data TabMeta = TabMeta { format :: TabFormat+                       , col_seq :: Int                           -- Column for the sequence name+                       , col_beg :: Int                           -- Column for the start of a region+                       , col_end :: Int                           -- Column for the end of a region+                       , comment_char :: Char+                       , skip_lines :: Int+                       , names :: V.Vector Bytes }+  deriving Show++data TabFormat = Generic | SamFormat | VcfFormat | ZeroBased   deriving Show++-- | Reads a Tabix index.  Note that tabix indices are compressed, this+-- is taken care of automatically.+readTabix :: MonadIO m => ByteStream m r -> m TabIndex+readTabix = either (const . liftIO $ throwM P.EofException) (return . fst) <=<+            P.parseIO (const $ P.getString 4 >>= switch) . S.gunzip+  where+    switch "TBI\1" = do nref <- fromIntegral `liftM` P.getWord32+                        format       <- liftM toFormat     P.getWord32+                        col_seq      <- liftM fromIntegral P.getWord32+                        col_beg      <- liftM fromIntegral P.getWord32+                        col_end      <- liftM fromIntegral P.getWord32+                        comment_char <- liftM (chr . fromIntegral) P.getWord32+                        skip_lines   <- liftM fromIntegral P.getWord32+                        names        <- liftM (V.fromList . B.split 0) . P.getString . fromIntegral =<< P.getWord32++                        ix <- getIndexArrays nref 14 5 (const return) getIntervals+                        fin <- P.isFinished+                        if fin then return $! ix { extensions = TabMeta{..} }+                               else do unaln <- fromIntegral `liftM` P.getWord64+                                       return $! ix { unaln_off = unaln, extensions = TabMeta{..} }++    switch magic   = throwM $ IndexFormatError magic++    toFormat 1 = SamFormat+    toFormat 2 = VcfFormat+    toFormat x = if testBit x 16 then ZeroBased else Generic++-- Read the intervals.  Each one becomes a checkpoint.+getIntervals :: Monad m => (IntMap Int64, Int64) -> P.Parser r m (IntMap Int64, Int64)+getIntervals (cp,mx0) = do+    nintv <- fromIntegral `liftM` P.getWord32+    reduceM 0 nintv (cp,mx0) $ \(!im,!mx) int -> do+        oo <- fromIntegral `liftM` P.getWord64+        return (if oo == 0 then im else M.insert (int * 0x4000) oo im, max mx oo)+++getIndexArrays :: MonadIO m => Int -> Int -> Int+               -> (Word32 -> Ckpoints -> P.Parser r m Ckpoints)+               -> ((Ckpoints, Int64) -> P.Parser r m (Ckpoints, Int64))+               -> P.Parser r m (BamIndex ())+getIndexArrays nref minshift depth addOneCheckpoint addManyCheckpoints+    | nref  < 1 = return $ BamIndex minshift depth 0 () V.empty V.empty+    | otherwise = do+        rbins  <- liftIO $ W.new nref+        rckpts <- liftIO $ W.new nref+        mxR <- reduceM 0 nref 0 $ \mx0 r -> do+                nbins <- P.getWord32+                (!bins,!cpts,!mx1) <- reduceM 0 nbins (M.empty,M.empty,mx0) $ \(!im,!cp,!mx) _ -> do+                        bin <- P.getWord32 -- the "distinct bin"+                        cp' <- addOneCheckpoint bin cp+                        segsarr <- getSegmentArray+                        let !mx' = if U.null segsarr then mx else max mx (snd (U.last segsarr))+                        return (M.insert (fromIntegral bin) segsarr im, cp', mx')+                (!cpts',!mx2) <- addManyCheckpoints (cpts,mx1)+                liftIO $ W.write rbins r bins >> W.write rckpts r cpts'+                return mx2+        liftM2 (BamIndex minshift depth mxR ()) (liftIO $ V.unsafeFreeze rbins) (liftIO $ V.unsafeFreeze rckpts)++-- | Reads the list of segments from an index file and makes sure+-- it is sorted.+getSegmentArray :: MonadIO m => P.Parser r m Segments+getSegmentArray = do+    nsegs <- fromIntegral `liftM` P.getWord32+    segsarr <- liftIO $ N.new nsegs+    loopM 0 nsegs $ \i -> do beg <- fromIntegral `liftM` P.getWord64+                             end <- fromIntegral `liftM` P.getWord64+                             liftIO $ N.write segsarr i (beg,end)+    liftIO $ N.sort segsarr >> U.unsafeFreeze segsarr++{-# INLINE reduceM #-}+reduceM :: (Monad m, Enum ix, Eq ix) => ix -> ix -> a -> (a -> ix -> m a) -> m a+reduceM beg end acc cons = if beg /= end then cons acc beg >>= \n -> reduceM (succ beg) end n cons else return acc++{-# INLINE loopM #-}+loopM :: (Monad m, Enum ix, Eq ix) => ix -> ix -> (ix -> m ()) -> m ()+loopM beg end k = if beg /= end then k beg >> loopM (succ beg) end k else return ()++{-| Seeks to a virtual offset in a BGZF file and streams from there.++If the optional end offset is supplied, streaming stops when it is+reached.  Else, streaming goes on to the end of file.+-}+streamBgzf :: MonadIO m => Handle -> Int64 -> Maybe Int64 -> ByteStream m ()+streamBgzf hdl off eoff =+    S.drop (off .&. 0xffff) . bgunzip $ do+        when (off /= 0) (liftIO $ hSeek hdl AbsoluteSeek $ fromIntegral $ shiftR off 16)+        maybe id S.trim eoff $ streamHandle hdl+{-# INLINE streamBgzf #-}++{- | Streams one reference from a bam file.++Seeks to a given sequence in a Bam file and enumerates only those+records aligning to that reference.  We use the first checkpoint+available for the sequence, which an appropriate index.  Streams the+'BamRaw' records of the correct reference sequence only, and produces an+empty stream if the sequence isn't found.+-}+streamBamRefseq :: MonadIO m => BamIndex b -> Handle -> Refseq -> Stream (Of BamRaw) m ()+streamBamRefseq BamIndex{..} hdl (Refseq r)+    | Just ckpts <- refseq_ckpoints V.!? fromIntegral r+    , Just (voff, _) <- M.minView ckpts+    , voff /= 0 = void $+                  Q.takeWhile ((Refseq r ==) . b_rname . unpackBam) $+                  Q.unfoldr (P.parseIO getBamRaw) $+                  streamBgzf hdl voff Nothing+    | otherwise = pure ()++{- | Reads from a Bam file the part with unaligned reads.++Sort of the dual to 'streamBamRefseq'.  Since the index does not+actually point to the unaligned part at the end, we use a best guess at+where the unaligned stuff might start, then skip over any aligned+records.  Our \"fallback guess\" is to decode from the current position;+this only works if something else already consumed the Bam header.+-}+streamBamUnaligned :: MonadIO m => BamIndex b -> Handle -> Stream (Of BamRaw) m ()+streamBamUnaligned BamIndex{..} hdl =+    Q.filter (not . isValidRefseq . b_rname . unpackBam) $+    Q.unfoldr (P.parseIO getBamRaw) $+    streamBgzf hdl unaln_off Nothing++{- | Streams one 'Segment'.++Takes a 'Handle', a 'Segment' and a 'Stream' coming from that handle.+If skipping ahead in the stream looks cheap enough, that is done.  Else+we seek the handle to the start offset and stream from it.  Either way,+the part of the stream before it crosses either the end offset or the+max position is returned, and the remaining stream after it is returned+in its functorial value so it can be passed to another invocation of+e.g. 'streamBamSegment'.  Note that the stream passed in becomes+unusable.+-}+streamBamSegment :: MonadIO m+                 => Handle -> Segment -> Stream (Of BamRaw) m ()+                 -> Stream (Of BamRaw) m (Stream (Of BamRaw) m ())+streamBamSegment hdl (Segment beg end mpos) =+    lift . Q.uncons >=> \case+        -- don't seek if it's a forwards seek of less than 512k+        Just (br,brs) | near (virt_offset br)+            -> Q.span in_seg $ Q.cons br brs+        _   -> Q.span in_seg $ Q.unfoldr (P.parseIO getBamRaw) $ streamBgzf hdl beg (Just end)+  where+    near    o = beg <= fromIntegral o && beg + 0x800000000 > fromIntegral o+    in_seg br = virt_offset br <= end && b_pos (unpackBam br) <= mpos++streamBamSubseq :: MonadIO m+                => BamIndex b -> Handle -> Refseq -> R.Subsequence -> Stream (Of BamRaw) m ()+                -> Stream (Of BamRaw) m (Stream (Of BamRaw) m ())+streamBamSubseq bi hdl ref subs str = Q.filter olap $ foldM (flip $ streamBamSegment hdl) str segs+  where+    segs = foldr (~~) [] $ segmentLists bi ref subs+    olap br = case unpackBam br of+        BamRec{..} -> b_rname == ref && R.overlaps b_pos (b_pos + alignedLength b_cigar) subs++streamBamRegions :: MonadIO m => BamIndex b -> Handle -> [R.Region] -> Stream (Of BamRaw) m ()+streamBamRegions bi hdl = void . foldM (\s (r,is) -> streamBamSubseq bi hdl r is s) (pure ()) . R.toList . R.fromList+
+ Bio/Bam/Pileup.hs view
@@ -0,0 +1,475 @@+{-# LANGUAGE Rank2Types #-}++-- | Pileup, similar to Samtools+--+-- Pileup turns a sorted sequence of reads into a sequence of \"piles\",+-- one for each site where a genetic variant might be called.  We will+-- scan each read's CIGAR line and MD field in concert with the sequence+-- and effective quality.  Effective quality is the lowest available+-- quality score of QUAL, MAPQ, and BQ.  For aDNA calling, a base is+-- represented as four probabilities, derived from a position dependent+-- damage model.++module Bio.Bam.Pileup+    ( PrimChunks(..)+    , PrimBase(..)+    , PosPrimChunks+    , DamagedBase(..)+    , DmgToken(..)+    , dissect+    , CallStats(..)+    , V_Nuc(..)+    , V_Nucs(..)+    , IndelVariant(..)+    , BasePile+    , IndelPile+    , Pile'(..)+    , Pile+    , pileup+    ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude+import Bio.Streaming++import qualified Data.ByteString        as B+import qualified Data.Vector.Generic    as V+import qualified Data.Vector.Storable   as U+import qualified Streaming.Prelude      as Q++-- | The primitive pieces for genotype calling:  A position, a base+-- represented as four likelihoods, an inserted sequence, and the+-- length of a deleted sequence.  The logic is that we look at a base+-- followed by some indel, and all those indels are combined into a+-- single insertion and a single deletion.+data PrimChunks = Seek {-# UNPACK #-} !Int PrimBase                   -- ^ skip to position (at start or after N operation)+                | Indel [Nucleotides] [DamagedBase] PrimBase          -- ^ observed deletion and insertion between two bases+                | EndOfRead                                           -- ^ nothing anymore+  deriving Show++data PrimBase = Base { _pb_wait   :: {-# UNPACK #-} !Int              -- ^ number of bases to wait due to a deletion+                     , _pb_base   :: {-# UNPACK #-} !DamagedBase+                     , _pb_mapq   :: {-# UNPACK #-} !Qual             -- ^ map quality+                     , _pb_chunks ::                 PrimChunks }     -- ^ more chunks+  deriving Show++type PosPrimChunks = (Refseq, Int, Bool, PrimChunks)++-- | Represents our knowledge about a certain base, which consists of+-- the base itself (A,C,G,T, encoded as 0..3; no Ns), the quality score+-- (anything that isn't A,C,G,T becomes A with quality 0), and a+-- substitution matrix representing post-mortem but pre-sequencing+-- substitutions.+--+-- Unfortunately, none of this can be rolled into something more simple,+-- because damage and sequencing error behave so differently.+--+-- Damage information is polymorphic.  We might run with a simple+-- version (a matrix) for calling, but we need more (a matrix and a+-- mutable matrix, I think) for estimation.++data DamagedBase = DB { db_call    :: {-# UNPACK #-} !Nucleotide           -- ^ called base+                      , db_qual    :: {-# UNPACK #-} !Qual                 -- ^ quality of called base+                      , db_dmg_tk  :: {-# UNPACK #-} !DmgToken             -- ^ damage information+                      , db_dmg_pos :: {-# UNPACK #-} !Int                  -- ^ damage information+                      , db_ref     :: {-# UNPACK #-} !Nucleotides }        -- ^ reference base from MD field++newtype DmgToken = DmgToken { fromDmgToken :: Int }++instance Show DamagedBase where+    showsPrec _ (DB n q _ _ r)+        | nucToNucs n == r = shows n .                     (:) '@' . shows q+        | otherwise        = shows n . (:) '/' . shows r . (:) '@' . shows q+++-- | Decomposes a BAM record into chunks suitable for piling up.  We+-- pick apart the CIGAR and MD fields, and combine them with sequence+-- and quality as appropriate.  Clipped bases are removed/skipped as+-- needed.  We also apply a substitution matrix to each base, which must+-- be supplied along with the read.+{-# INLINE dissect #-}+dissect :: DmgToken -> BamRaw -> [PosPrimChunks]+dissect dtok br =+    if isUnmapped b || isDuplicate b || not (isValidRefseq b_rname)+    then [] else [(b_rname, b_pos, isReversed b, pchunks)]+  where+    b@BamRec{..} = unpackBam br+    pchunks = firstBase b_pos 0 0 (fromMaybe [] $ getMd b)++    !max_cig = V.length b_cigar+    !max_seq = V.length b_seq+    !baq     = extAsString "BQ" b++    -- This will compute the effective quality.  As far as I can see+    -- from the BAM spec V1.4, the qualities that matter are QUAL, MAPQ,+    -- and BAQ.  If QUAL is invalid, we replace it (arbitrarily) with+    -- 23 (assuming a rather conservative error rate of ~0.5%), BAQ is+    -- added to QUAL, and MAPQ is an upper limit for effective quality.++    get_seq :: Int -> (Nucleotides -> Nucleotides) -> DamagedBase+    get_seq i f = case b_seq `V.unsafeIndex` i of                                -- nucleotide+            n | n == nucsA -> DB nucA qe dtok dmg (f n)+              | n == nucsC -> DB nucC qe dtok dmg (f n)+              | n == nucsG -> DB nucG qe dtok dmg (f n)+              | n == nucsT -> DB nucT qe dtok dmg (f n)+              | otherwise  -> DB nucA (Q 0) dtok dmg (f n)+      where+        !q   = case b_qual `V.unsafeIndex` i of Q 0xff -> Q 30 ; x -> x         -- quality; invalid (0xff) becomes 30+        !q'  | i >= B.length baq = q                                            -- no BAQ available+             | otherwise = Q (unQ q + (B.index baq i - 64))                     -- else correct for BAQ+        !qe  = min q' b_mapq                                                    -- use MAPQ as upper limit+        !dmg = if i+i > max_seq then i-max_seq else i++    -- Look for first base following the read's start or a gap (CIGAR+    -- code N).  Indels are skipped, since these are either bugs in the+    -- aligner or the aligner getting rid of essentially unalignable+    -- bases.+    firstBase :: Int -> Int -> Int -> [MdOp] -> PrimChunks+    firstBase !pos !is !ic mds+        | is >= max_seq || ic >= max_cig = EndOfRead+        | otherwise = case b_cigar `V.unsafeIndex` ic of+            Ins :* cl ->            firstBase  pos (cl+is) (ic+1) mds+            SMa :* cl ->            firstBase  pos (cl+is) (ic+1) mds+            Del :* cl ->            firstBase (pos+cl) is  (ic+1) (drop_del cl mds)+            Nop :* cl ->            firstBase (pos+cl) is  (ic+1) mds+            HMa :*  _ ->            firstBase  pos     is  (ic+1) mds+            Pad :*  _ ->            firstBase  pos     is  (ic+1) mds+            Mat :*  0 ->            firstBase  pos     is  (ic+1) mds+            Mat :*  _ -> Seek pos $ nextBase 0 pos     is   ic 0  mds+      where+        -- We have to treat (MdNum 0), because samtools actually+        -- generates(!) it all over the place and if not handled as a+        -- special case, it looks like an inconsistent MD field.+        drop_del n (MdDel ns : mds')+            | n < length ns = MdDel (drop n ns) : mds'+            | n > length ns = drop_del (n - length ns) mds'+            | otherwise     = mds'+        drop_del n (MdNum 0 : mds') = drop_del n mds'+        drop_del _ mds'     = mds'++    -- Generate likelihoods for the next base.  When this gets called,+    -- we are looking at an M CIGAR operation and all the subindices are+    -- valid.+    -- I don't think we can ever get (MdDel []), but then again, who+    -- knows what crazy shit samtools decides to generate.  There is+    -- little harm in special-casing it.+    nextBase :: Int -> Int -> Int -> Int -> Int -> [MdOp] -> PrimBase+    nextBase !wt !pos !is !ic !io mds = case mds of+        MdNum   0 : mds' -> nextBase wt pos is ic io mds'+        MdDel  [] : mds' -> nextBase wt pos is ic io mds'+        MdNum   1 : mds' -> nextBase' (get_seq is id   ) mds'+        MdNum   n : mds' -> nextBase' (get_seq is id   ) (MdNum (n-1) : mds')+        MdRep ref : mds' -> nextBase' (get_seq is $ const ref  ) mds'+        MdDel   _ : _    -> nextBase' (get_seq is $ const nucsN) mds+        [              ] -> nextBase' (get_seq is $ const nucsN) [ ]+      where+        nextBase' ref mds' = Base wt ref b_mapq $ nextIndel  [] [] (pos+1) (is+1) ic (io+1) mds'++    -- Look for the next indel after a base.  We collect all indels (I+    -- and D codes) into one combined operation.  If we hit N or the+    -- read's end, we drop all of it (indels next to a gap indicate+    -- trouble).  Other stuff is skipped: we could check for stuff that+    -- isn't valid in the middle of a read (H and S), but then what+    -- would we do about it anyway?  Just ignoring it is much easier and+    -- arguably at least as correct.+    nextIndel :: [[DamagedBase]] -> [Nucleotides] -> Int -> Int -> Int -> Int -> [MdOp] -> PrimChunks+    nextIndel ins del !pos !is !ic !io mds+        | is >= max_seq || ic >= max_cig = EndOfRead+        | otherwise = case b_cigar `V.unsafeIndex` ic of+            Ins :* cl ->             nextIndel (isq cl) del   pos (cl+is) (ic+1) 0 mds+            SMa :* cl ->             nextIndel  ins     del   pos (cl+is) (ic+1) 0 mds+            Del :* cl ->             nextIndel ins (del++dsq) (pos+cl) is (ic+1) 0 mds'+                where (dsq,mds') = split_del cl mds+            Pad :*  _ ->             nextIndel  ins     del   pos     is  (ic+1) 0 mds+            HMa :*  _ ->             nextIndel  ins     del   pos     is  (ic+1) 0 mds+            Nop :* cl ->             firstBase               (pos+cl) is  (ic+1)   mds      -- ends up generating a 'Seek'+            Mat :* cl | io == cl  -> nextIndel  ins     del   pos     is  (ic+1) 0 mds+                      | otherwise -> indel del out $ nextBase (length del) pos is ic io mds -- ends up generating a 'Base'+      where+        indel d o k = foldr seq (Indel d o k) o+        out    = concat $ reverse ins+        isq cl = [ get_seq i $ const gap | i <- [is..is+cl-1] ] : ins++        -- We have to treat (MdNum 0), because samtools actually+        -- generates(!) it all over the place and if not handled as a+        -- special case, it looks like an incinsistend MD field.+        split_del n (MdDel ns : mds')+            | n < length ns = (take n ns, MdDel (drop n ns) : mds')+            | n > length ns = let (ns', mds'') = split_del (n - length ns) mds' in (ns++ns', mds'')+            | otherwise     = (ns, mds')+        split_del n (MdNum 0 : mds') = split_del n mds'+        split_del n mds'    = (replicate n nucsN, mds')++-- | Statistics about a genotype call.  Probably only useful for+-- filtering (so not very useful), but we keep them because it's easy to+-- track them.++data CallStats = CallStats+    { read_depth       :: {-# UNPACK #-} !Int       -- number of contributing reads+    , reads_mapq0      :: {-# UNPACK #-} !Int       -- number of (non-)contributing reads with MAPQ==0+    , sum_mapq         :: {-# UNPACK #-} !Int       -- sum of map qualities of contributing reads+    , sum_mapq_squared :: {-# UNPACK #-} !Int }     -- sum of squared map qualities of contributing reads+  deriving (Show, Eq, Generic)++instance Monoid CallStats where+    mempty      = CallStats { read_depth       = 0+                            , reads_mapq0      = 0+                            , sum_mapq         = 0+                            , sum_mapq_squared = 0 }+    mappend     = (<>)++instance Semigroup CallStats where+    x <> y = CallStats { read_depth       = read_depth x + read_depth y+                       , reads_mapq0      = reads_mapq0 x + reads_mapq0 y+                       , sum_mapq         = sum_mapq x + sum_mapq y+                       , sum_mapq_squared = sum_mapq_squared x + sum_mapq_squared y }++newtype V_Nuc  = V_Nuc  (U.Vector Nucleotide)  deriving (Eq, Ord, Show)+newtype V_Nucs = V_Nucs (U.Vector Nucleotides) deriving (Eq, Ord, Show)++data IndelVariant = IndelVariant { deleted_bases  :: !V_Nucs, inserted_bases :: !V_Nuc }+      deriving (Eq, Ord, Show, Generic)+++-- | Map quality and a list of encountered bases, with damage+-- information and reference base if known.+type BasePile  =                          [DamagedBase]++-- | Map quality and a list of encountered indel variants.  The deletion+-- has the reference sequence, if known, an insertion has the inserted+-- sequence with damage information.+type IndelPile = [( Qual, ([Nucleotides], [DamagedBase]) )]   -- a list of indel variants++-- | Running pileup results in a series of piles.  A 'Pile' has the+-- basic statistics of a 'VarCall', but no likelihood values and a+-- pristine list of variants instead of a proper call.  We emit one pile+-- with two 'BasePile's (one for each strand) and one 'IndelPile' (the+-- one immediately following) at a time.++data Pile' a b = Pile { p_refseq     :: {-# UNPACK #-} !Refseq+                      , p_pos        :: {-# UNPACK #-} !Int+                      , p_snp_stat   :: {-# UNPACK #-} !CallStats+                      , p_snp_pile   :: a+                      , p_indel_stat :: {-# UNPACK #-} !CallStats+                      , p_indel_pile :: b }+  deriving Show++-- | Raw pile.  Bases and indels are piled separately on forward and+-- backward strands.+type Pile = Pile' (BasePile, BasePile) (IndelPile, IndelPile)++-- | The pileup enumeratee takes 'BamRaw's, dissects them, interleaves+-- the pieces appropriately, and generates 'Pile's.  The output will+-- contain at most one 'BasePile' and one 'IndelPile' for each position,+-- piles are sorted by position.+--+-- This top level driver receives 'BamRaw's.  Unaligned reads and+-- duplicates are skipped (but not those merely failing quality checks).+-- Processing stops when the first read with invalid 'br_rname' is+-- encountered or a t end of file.++{-# INLINE pileup #-}+pileup :: Stream (Of PosPrimChunks) IO b -> Stream (Of Pile) IO b+pileup = runPileM pileup' finish (Refseq 0) 0 ([],[]) (Empty,Empty)+  where+    finish () _ _ ([],[]) (Empty,Empty) inp = lift (Q.effects inp)+    finish () _ _ _ _ _ = error "logic error: leftovers after pileup"+++-- | The pileup logic keeps a current coordinate (just two integers) and+-- two running queues: one of /active/ 'PrimBase's that contribute to+-- current genotype calling and on of /waiting/ 'PrimBase's that will+-- contribute at a later point.+--+-- This is the CPS version of multiple state and environment monads.  It+-- is somewhat faster than direct style and gives more control over when+-- evaluation happens.++newtype PileM m a = PileM { runPileM :: forall r . (a -> PileF m r) -> PileF m r }++-- | The things we drag along in 'PileM'.  Notes:+-- * The /active/ queue is a simple stack.  We add at the front when we+--   encounter reads, which reverses them.  When traversing it, we traverse+--   reads backwards, but since we accumulate the 'BasePile', it gets reversed+--   back.  The new /active/ queue, however, is no longer reversed (as it should+--   be).  So after the traversal, we reverse it again.  (Yes, it is harder to+--   understand than using a proper deque type, but it is cheaper.+--   There may not be much point in the reversing, though.)++type PileF m r = Refseq -> Int ->                               -- current position+                 ( [PrimBase], [PrimBase] ) ->                  -- active queues+                 ( Heap, Heap ) ->                              -- waiting queues+                 Stream (Of PosPrimChunks) m r ->               -- input stream+                 Stream (Of Pile) m r                           -- output stream++instance Functor (PileM m) where+    {-# INLINE fmap #-}+    fmap f (PileM m) = PileM $ \k -> m (k . f)++instance Applicative (PileM m) where+    {-# INLINE pure #-}+    pure a = PileM $ \k -> k a+    {-# INLINE (<*>) #-}+    u <*> v = PileM $ \k -> runPileM u (\a -> runPileM v (k . a))++instance Monad (PileM m) where+    {-# INLINE return #-}+    return a = PileM $ \k -> k a+    {-# INLINE (>>=) #-}+    m >>=  k = PileM $ \k' -> runPileM m (\a -> runPileM (k a) k')++--  -- XXX+-- {-# INLINE get_refseq #-}+-- get_refseq :: PileM m Refseq+-- get_refseq = PileM $ \k r -> k r r++-- {-# INLINE get_pos #-}+-- get_pos :: PileM m Int+-- get_pos = PileM $ \k r p -> k p r p++{-# INLINE upd_pos #-}+upd_pos :: (Int -> Int) -> PileM m ()+upd_pos f = PileM $ \k r p -> k () r $! f p++{-# INLINE yieldPile #-}+yieldPile :: Monad m => CallStats -> BasePile -> BasePile -> CallStats -> IndelPile -> IndelPile -> PileM m ()+yieldPile x1 x2a x2b x3 x4a x4b = PileM $ \ !kont !r !p !a !w !inp ->+    let pile = Pile r p x1 (x2a,x2b) x3 (x4a,x4b)+    in Q.cons pile $ kont () r p a w inp++-- | The actual pileup algorithm.  If /active/ contains something,+-- continue here.  Else find the coordinate to continue from, which is+-- the minimum of the next /waiting/ coordinate and the next coordinate+-- in input; if found, continue there, else we're all done.+pileup' :: Monad m => PileM m ()+pileup' = PileM $ \ !k !refseq !pos !active !waiting inp0 -> do++    inp <- lift $ inspect inp0+    let inp1        = either pure wrap inp+        cont2 rs po = runPileM pileup'' k     rs po  active waiting inp1+        leave       =                k () refseq pos active waiting inp1++    case (active, getMinKeysH waiting, inp) of+        ( (_:_,_),       _,                   _  ) -> cont2 refseq pos+        ( (_,_:_),       _,                   _  ) -> cont2 refseq pos+        (    _,    Just nw, Left              _  ) -> cont2 refseq nw+        (    _,    Nothing, Left              _  ) -> leave+        (    _,    Nothing, Right ((r,p,_,_):>_) ) -> cont2 r p+        (    _,    Just nw, Right ((r,p,_,_):>_) )+                            | (refseq,nw) <= (r,p) -> cont2 refseq nw+                            | otherwise            -> cont2 r p+  where+    getMinKeysH :: (Heap, Heap) -> Maybe Int+    getMinKeysH (a,b) = case (getMinKeyH a, getMinKeyH b) of+        ( Nothing, Nothing ) -> Nothing+        ( Just  x, Nothing ) -> Just  x+        ( Nothing, Just  y ) -> Just  y+        ( Just  x, Just  y ) -> Just (min x y)+++pileup'' :: Monad m => PileM m ()+pileup'' = do+    -- Input is still 'BamRaw', since these can be relied on to be+    -- sorted.  First see if there is any input at the current location,+    -- if so, dissect it and add it to the appropriate queue.+    p'feed_input+    p'check_waiting+    ((fin_bsL, fin_bpL), (fin_bsR, fin_bpR), (fin_isL, fin_ipL), (fin_isR, fin_ipR)) <- p'scan_active++    -- Output, but don't bother emitting empty piles.  Note that a plain+    -- basecall still yields an entry in the 'IndelPile'.  This is necessary,+    -- because actual indel calling will want to know how many reads /did not/+    -- show the variant.  However, if no reads show any variant, and here is the+    -- first place where we notice that, the pile is useless.+    let uninteresting (_,(d,i)) = null d && null i+    unless (null fin_bpL && null fin_bpR && all uninteresting fin_ipL && all uninteresting fin_ipR) $+        yieldPile (fin_bsL <> fin_bsR) fin_bpL fin_bpR+                  (fin_isL <> fin_isR) fin_ipL fin_ipR++    -- Bump coordinate and loop.  (Note that the bump to the next+    -- reference /sequence/ is done implicitly, because we will run out of+    -- reads and restart in 'pileup''.)+    upd_pos succ+    pileup'++-- | Feeds input as long as it starts at the current position+p'feed_input :: Monad m => PileM m ()+p'feed_input = PileM $ \kont rs po ac@(af,ar) wt@(wf,wr) ->+    lift . inspect >=> \case+        Right ((rs', po', str, prim) :> bs)+            | rs == rs' && po == po' ->+                case prim of+                    EndOfRead     -> runPileM p'feed_input kont rs po ac wt                                       bs+                    Indel _ _ !pb -> runPileM p'feed_input kont rs po (if str then (af,pb:ar) else (pb:af,ar)) wt bs+                    Seek   !p !pb -> runPileM p'feed_input kont rs po ac (if str then (wf,wr') else (wf',wr))     bs+                      where wf' = Node p pb Empty Empty `unionH` wf+                            wr' = Node p pb Empty Empty `unionH` wr+        inp -> kont () rs po ac wt $ either pure wrap inp++-- | Checks /waiting/ queue.  If there is anything waiting for the+-- current position, moves it to /active/ queue.+p'check_waiting :: PileM m ()+p'check_waiting = PileM $ \kont rs po (af0,ar0) (wf0,wr0) ->+        let go1 af wf = case viewMinH wf of+                Just (!mk, !pb, !wf') | mk == po -> go1 (pb:af) wf'+                _                                -> go2 af wf ar0 wr0++            go2 af wf ar wr = case viewMinH wr of+                Just (!mk, !pb, !wr') | mk == po -> go2 af wf (pb:ar) wr'+                _                                -> kont () rs po (af,ar) (wf,wr)++        in go1 af0 wf0+++-- | Separately scans the two /active/ queues and makes one 'BasePile'+-- from each.  Also sees what's next in the 'PrimChunks':  'Indel's+-- contribute to two separate 'IndelPile's, 'Seek's are pushed back to+-- the /waiting/ queue, 'EndOfRead's are removed, and everything else is+-- added to two fresh /active/ queues.+p'scan_active :: PileM m (( CallStats, BasePile ),  ( CallStats, BasePile ),+                          ( CallStats, IndelPile ), ( CallStats, IndelPile ))+p'scan_active = do+    (bpf,ipf) <- PileM $ \kont rs pos (af,ar) (wf,wr) -> go (\r af' wf' -> kont r rs pos (af',ar) (wf',wr)) [] wf mempty mempty af+    (bpr,ipr) <- PileM $ \kont rs pos (af,ar) (wf,wr) -> go (\r ar' wr' -> kont r rs pos (af,ar') (wf,wr')) [] wr mempty mempty ar+    return (bpf,bpr,ipf,ipr)+  where+    go k !ac !wt !bpile !ipile [                           ] = k (bpile, ipile) (reverse ac) wt+    go k !ac !wt !bpile !ipile (Base nwt qs mq pchunks : bs) =+        case pchunks of+            _ | nwt > 0     -> b' `seq` go k  (b':ac)   wt     bpile     ipile  bs+            Seek p' pb'     -> go k      ac (ins p' pb' wt) (z bpile)    ipile  bs+            Indel nd ni pb' -> go k (pb':ac)            wt  (z bpile) (y ipile) bs where y = put (,) mq (nd,ni)+            EndOfRead       -> go k      ac             wt  (z bpile)    ipile  bs+        where+            b' = Base (nwt-1) qs mq pchunks+            z  = put (\q x -> x { db_qual = min q (db_qual x) }) mq qs++    ins q v w = Node q v Empty Empty `unionH` w++    put f (Q !q) !x (!st,!vs) = ( st { read_depth       = read_depth st + 1+                                     , reads_mapq0      = reads_mapq0 st + (if q == 0 then 1 else 0)+                                     , sum_mapq         = sum_mapq st + fromIntegral q+                                     , sum_mapq_squared = sum_mapq_squared st + fromIntegral q * fromIntegral q }+                                , f (Q q) x : vs )+++-- | We need a simple priority queue.  Here's a skew heap (specialized+-- to strict 'Int' priorities and 'PrimBase' values).+data Heap = Empty | Node {-# UNPACK #-} !Int PrimBase Heap Heap++unionH :: Heap -> Heap -> Heap+Empty                 `unionH` t2                    = t2+t1                    `unionH` Empty                 = t1+t1@(Node k1 x1 l1 r1) `unionH` t2@(Node k2 x2 l2 r2)+   | k1 <= k2                                        = Node k1 x1 (t2 `unionH` r1) l1+   | otherwise                                       = Node k2 x2 (t1 `unionH` r2) l2++getMinKeyH :: Heap -> Maybe Int+getMinKeyH Empty          = Nothing+getMinKeyH (Node x _ _ _) = Just x++viewMinH :: Heap -> Maybe (Int, PrimBase, Heap)+viewMinH Empty          = Nothing+viewMinH (Node k v l r) = Just (k, v, l `unionH` r)+
+ Bio/Bam/Reader.hs view
@@ -0,0 +1,261 @@+-- | Parsers for BAM and SAM.++module Bio.Bam.Reader (+    decodeBam,+    decodeBamFile,+    decodeBamFiles,++    decodePlainBam,+    decodePlainSam,+    getBamMeta,+    getBamRaw,+    getSamRec,++    concatInputs,+    mergeInputsOn,+    guardRefCompat,+    coordinates,+    qnames+                      ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Bam.Writer               ( packBam )+import Bio.Streaming+import Bio.Streaming.Bgzf           ( getBgzfHdr, bgunzip )+import Bio.Prelude+import Data.Attoparsec.ByteString   ( anyWord8 )++import qualified Data.Attoparsec.ByteString.Char8   as P+import qualified Data.ByteString                    as B+import qualified Data.ByteString.Char8              as C+import qualified Data.HashMap.Strict                as M+import qualified Data.Vector.Generic                as V+import qualified Data.Vector.Storable               as W+import qualified Data.Vector.Unboxed                as U+import qualified Bio.Streaming.Bytes                as S+import qualified Bio.Streaming.Parse                as S+import qualified Streaming.Prelude                  as Q++{- | Decodes either BAM or SAM.++The input can be plain, gzip'ed or bgzf'd and either BAM or SAM.  BAM+is reliably recognized, anything else is treated as SAM.  The offsets+stored in BAM records make sense only for uncompressed or bgzf'd BAM.+-}+decodeBam :: MonadIO m+          => S.ByteStream m r+          -> m (BamMeta, Stream (Of BamRaw) m r)+decodeBam = getBgzfHdr >=> S.splitAt' 4 . pgunzip >=> unbam+  where+    unbam ("BAM\SOH" :> s) = decodePlainBam s+    unbam (magic     :> s) = decodePlainSam (S.consChunk magic s)++    pgunzip (Nothing, hdr, s) = S.gunzip (S.consChunk hdr s)+    pgunzip (Just _,  hdr, s) =  bgunzip (S.consChunk hdr s)+{-# INLINE decodeBam #-}++decodeBamFile :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r+decodeBamFile f k = streamFile f $ decodeBam >=> uncurry k+{-# INLINE decodeBamFile #-}++{- | Reads multiple bam files.++A continuation is run on the list of headers and streams.  Since no+attempt is made to unify the headers, this will work for completely+unrelated bam files.  All files are opened at the same time, which might+run into the file descriptor limit given some ridiculous workflows.+-}+decodeBamFiles :: (MonadMask m, MonadIO m) => [FilePath] -> ([(BamMeta, Stream (Of BamRaw) m ())] -> m r) -> m r+decodeBamFiles [      ] k = k []+decodeBamFiles ("-":fs) k = decodeBam (streamHandle stdin)   >>= \b -> decodeBamFiles fs $ \bs -> k (b:bs)+decodeBamFiles ( f :fs) k = streamFile f $ \s -> decodeBam s >>= \b -> decodeBamFiles fs $ \bs -> k (b:bs)+{-# INLINE decodeBamFiles #-}++decodePlainBam :: MonadIO m => S.ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)+decodePlainBam =+    S.parseIO (const getBamMeta) >=>+    either (const . liftIO $ throwM S.EofException)+           (return . fmap (Q.unfoldr (S.parseIO getBamRaw)))++getBamMeta :: Monad m => S.Parser r m BamMeta+getBamMeta = liftM2 mmerge get_bam_header get_ref_array+  where+    get_bam_header  = do hdr_len <- S.getWord32+                         S.isolate (fromIntegral hdr_len) (S.atto parseBamMeta)++    get_ref_array = do nref <- S.getWord32+                       V.fromList `liftM` replicateM (fromIntegral nref)+                            (do nm <- S.getWord32 >>= S.getString . fromIntegral+                                ln <- S.getWord32+                                return $! BamSQ (C.init nm) (fromIntegral ln) [])++    -- Need to merge information from header into actual reference list.+    -- The latter is the authoritative source for the *order* of the+    -- sequences, so leftovers from the header are discarded.  Merging+    -- is by name.  So we merge information from the header into the+    -- list, then replace the header information.+    mmerge meta refs =+        let tbl = M.fromList [ (sq_name sq, sq) | sq <- V.toList (unRefs (meta_refs meta)) ]+        in meta { meta_refs = Refs $ fmap (\s -> maybe s (mmerge' s) (M.lookup (sq_name s) tbl)) refs }++    mmerge' l r | sq_length l == sq_length r = l { sq_other_shit = sq_other_shit l ++ sq_other_shit r }+                | otherwise                  = l -- contradiction in header, but we'll just ignore it+{-# INLINABLE getBamMeta #-}+++data ShortRecord = ShortRecord deriving Typeable++instance Exception ShortRecord+instance Show ShortRecord where show _ = "short BAM record"++getBamRaw :: Monad m => Int64 -> S.Parser r m BamRaw+getBamRaw o = do+        bsize <- fromIntegral `liftM` S.getWord32+        when (bsize < 32) $ throwM ShortRecord+        s <- S.getString bsize+        unless (B.length s == bsize) S.abortParse+        return $ bamRaw o s+{-# INLINABLE getBamRaw #-}++{- | Streaming parser for SAM files.++It parses plain uncompressed SAM+and returns a result compatible with 'decodePlainBam'.  Since it is+supposed to work the same way as the BAM parser, it requires a+symbol table for the reference names.  This is extracted from the @SQ+lines in the header.  Note that reading SAM tends to be inefficient;+if you care about performance at all, use BAM.+-}+decodePlainSam :: MonadIO m => S.ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)+decodePlainSam s = do+    (hdr,rest) <- either (\r -> (mempty, pure r)) id `liftM` S.parseIO (const $ S.atto parseBamMeta) s+    let !refs = M.fromList $ zip [ nm | BamSQ { sq_name = nm } <- V.toList (unRefs (meta_refs hdr))] [toEnum 0..]+        ref x = M.lookupDefault invalidRefseq x refs+    return (hdr, Q.mapM (liftIO . either throwM packBam . getSamRec ref) (S.lines' rest))+++getSamRec :: (Bytes -> Refseq) -> Bytes -> Either S.ParseError BamRec+getSamRec ref s = case P.parseOnly record s of+    Left  e -> Left $ S.ParseError [unpack s] e+    Right b -> Right b+  where+    record = do b_qname <- word+                b_flag  <- num+                b_rname <- ref <$> word+                b_pos   <- subtract 1 <$> num+                b_mapq  <- Q <$> num'+                b_cigar <- W.fromList <$> cigar+                b_mrnm  <- rnext <*> pure b_rname+                b_mpos  <- subtract 1 <$> num+                b_isize <- snum+                b_seq   <- sequ+                b_qual  <- quals <*> pure b_seq+                b_exts  <- exts+                let b_virtual_offset = 0+                return BamRec{..}++    sep      = P.endOfInput <|> () <$ P.char '\t'+    word     = P.takeTill ('\t' ==) <* sep+    num      = P.decimal <* sep+    num'     = P.decimal <* sep+    snum     = P.signed P.decimal <* sep++    rnext    = id <$ P.char '=' <* sep <|> const . ref <$> word+    sequ     = (V.empty <$ P.char '*' <|>+               V.fromList . map toNucleotides . C.unpack <$> P.takeWhile is_nuc) <* sep++    quals    = defaultQs <$ P.char '*' <* sep <|> bsToVec <$> word+        where+            defaultQs sq = W.replicate (V.length sq) (Q 0xff)+            bsToVec qs _ = W.fromList . map (Q . subtract 33) $ B.unpack qs++    cigar    = [] <$ P.char '*' <* sep <|>+               P.manyTill (flip (:*) <$> P.decimal <*> cigop) sep++    cigop    = P.choice $ zipWith (\c r -> r <$ P.char c) "MIDNSHP" [Mat,Ins,Del,Nop,SMa,HMa,Pad]+    exts     = ext `P.sepBy` sep+    ext      = (\a b v -> (fromString [a,b],v)) <$> P.anyChar <*> P.anyChar <*> (P.char ':' *> value)++    value    = P.char 'A' *> P.char ':' *> (Char <$>               anyWord8) <|>+               P.char 'i' *> P.char ':' *> (Int  <$>     P.signed P.decimal) <|>+               P.char 'Z' *> P.char ':' *> (Text <$>   P.takeTill ('\t' ==)) <|>+               P.char 'H' *> P.char ':' *> (Bin  <$>               hexarray) <|>+               P.char 'f' *> P.char ':' *> (Float . realToFrac <$> P.double) <|>+               P.char 'B' *> P.char ':' *> (+                    P.satisfy (P.inClass "cCsSiI") *> (intArr   <$> many (P.char ',' *> P.signed P.decimal)) <|>+                    P.char 'f'                     *> (floatArr <$> many (P.char ',' *> P.double)))++    intArr   is = IntArr   $ U.fromList is+    floatArr fs = FloatArr $ U.fromList $ map realToFrac fs+    hexarray    = B.pack . repack . C.unpack <$> P.takeWhile (P.inClass "0-9A-Fa-f")+    repack (a:b:cs) = fromIntegral (digitToInt a * 16 + digitToInt b) : repack cs ; repack _ = []+    is_nuc = P.inClass "acgtswkmrybdhvnACGTSWKMRYBDHVN"+++data IncompatibleRefs = IncompatibleRefs FilePath FilePath deriving Typeable++instance Exception IncompatibleRefs+instance Show IncompatibleRefs where+    show (IncompatibleRefs a b) = "references in " ++ a ++ " and " ++ b ++ " are incompatible"++guardRefCompat :: MonadThrow m => (FilePath,BamMeta) -> (FilePath,BamMeta) -> m ()+guardRefCompat (f0,hdr0) (f1,hdr1) =+    unless (p hdr1 `isPrefixOf` p hdr0) $ throwM $ IncompatibleRefs f0 f1+  where+    p = V.toList . unRefs . meta_refs+++{- | Reads multiple bam inputs in sequence.++Only one file is opened at a time, so they must also be consumed in+sequence.  If you can afford to open all inputs simultaneously, you+probably want to use 'mergeInputsOn' instead.  The filename \"-\" refers+to stdin, if no filenames are given, stdin is read.  Since we can't look+ahead into further files, the header of the first input is used+for the result, and an exception is thrown if one of the subsequent+headers is incompatible with the first one.+-}+concatInputs :: (MonadIO m, MonadMask m) => [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r+concatInputs fs0 k = streamInputs fs0 (go1 $ fs0 ++ repeat "-")+  where+    go1 fs = inspect >=> \case+        Left () -> k mempty (pure ())+        Right s -> do (hdr,bs) <- decodeBam s+                      k hdr (bs >>= go (head fs) hdr (tail fs))++    go f0 hdr0 fs = lift . inspect >=> \case+        Left () -> pure ()+        Right s -> do (hdr,bs) <- lift $ decodeBam s+                      lift $ guardRefCompat (f0,hdr0) (head fs,hdr)+                      bs >>= go f0 hdr0 (tail fs)+{-# INLINABLE concatInputs #-}++{- | Reads multiple bam files and merges them.++If the inputs are all sorted by the thing being merged on, the output+will be sorted, too.  The headers are all merged sensibly, even if their+reference lists differ.  However, for performance reasons, we don't want+to change the rname and mrnm fields in potentially all records.  So+instead of allowing arbitrary reference lists to be merged, we throw an+exception unless every input is compatible with the effective reference+list.+-}+mergeInputsOn :: (Ord x, MonadIO m, MonadMask m)+              => (BamRaw -> x) -> [FilePath]+              -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r+mergeInputsOn _ [] k = decodeBam (streamHandle stdin) >>= uncurry k+mergeInputsOn p fs k = decodeBamFiles fs $ \bs -> do+    let hdr = foldMap fst bs+    sequence_ $ zipWith (\f (h,_) -> guardRefCompat ("*",hdr) (f,h)) fs bs+    k hdr (foldr (\a b -> void $ mergeStreamsOn p (snd a) b) (pure ()) bs)+{-# INLINABLE mergeInputsOn #-}++coordinates :: BamRaw -> (Refseq, Int)+coordinates = (b_rname &&& b_pos) . unpackBam+{-# INLINE coordinates #-}++qnames :: BamRaw -> Bytes+qnames = b_qname . unpackBam+{-# INLINE qnames #-}+
+ Bio/Bam/Rec.hs view
@@ -0,0 +1,378 @@+{-# LANGUAGE TypeFamilies #-}+-- | Parsers and Printers for BAM and SAM.++module Bio.Bam.Rec (+    BamRaw,+    bamRaw,+    virt_offset,+    raw_data,++    BamRec(..),+    unpackBam,+    nullBamRec,+    getMd,++    Cigar(..),+    CigOp(..),+    alignedLength,++    Nucleotides(..), Vector_Nucs_half,+    Extensions, Ext(..),+    extAsInt, extAsString, setQualFlag,+    deleteE, insertE, updateE, adjustE,++    isPaired,+    isProperlyPaired,+    isUnmapped,+    isMateUnmapped,+    isReversed,+    isMateReversed,+    isFirstMate,+    isSecondMate,+    isAuxillary,+    isSecondary,+    isFailsQC,+    isDuplicate,+    isSupplementary,+    isTrimmed,+    isMerged,+    isAlternative,+    isExactIndex,+    type_mask+) where++import Bio.Bam.Header+import Bio.Prelude+import Bio.Util.Storable++import Control.Monad.Primitive      ( unsafePrimToPrim, unsafeInlineIO )+import Foreign.C.Types              ( CInt(..), CSize(..) )+import Foreign.Marshal.Alloc        ( alloca )++import qualified Data.ByteString                    as B+import qualified Data.ByteString.Char8              as S+import qualified Data.ByteString.Internal           as B+import qualified Data.ByteString.Unsafe             as B+import qualified Data.Vector.Generic                as V+import qualified Data.Vector.Generic.Mutable        as VM+import qualified Data.Vector.Storable               as VS+import qualified Data.Vector.Unboxed                as U+++-- | Cigar line in BAM coding+-- Bam encodes an operation and a length into a single integer, we keep+-- those integers in an array.+data Cigar = !CigOp :* !Int deriving (Eq, Ord)+infix 9 :*++data CigOp = Mat | Ins | Del | Nop | SMa | HMa | Pad+    deriving ( Eq, Ord, Enum, Show, Bounded, Ix )++instance Show Cigar where+    showsPrec _ (op :* num) = shows num . (:) (S.index "MIDNSHP" (fromEnum op))++instance Storable Cigar where+    sizeOf    _ = 4+    alignment _ = 1++    peek p = do w <- fromIntegral <$> peekUnalnWord32LE p+                return $ toEnum (w .&. 0xf) :* shiftR w 4++    poke p (op :* num) = pokeUnalnWord32LE p . fromIntegral $ fromEnum op .|. shiftL num 4+++-- | Extracts the aligned length from a cigar line.+-- This gives the length of an alignment as measured on the reference,+-- which is different from the length on the query or the length of the+-- alignment.+{-# INLINE alignedLength #-}+alignedLength :: V.Vector v Cigar => v Cigar -> Int+alignedLength = V.foldl' (\a -> (a +) . l) 0+  where l (op :* n) = if op == Mat || op == Del || op == Nop then n else 0+++-- | internal representation of a BAM record+data BamRec = BamRec {+        b_qname :: Bytes,+        b_flag  :: Int,+        b_rname :: Refseq,+        b_pos   :: Int,+        b_mapq  :: Qual,+        b_cigar :: VS.Vector Cigar,+        b_mrnm  :: Refseq,+        b_mpos  :: Int,+        b_isize :: Int,+        b_seq   :: Vector_Nucs_half Nucleotides,+        b_qual  :: VS.Vector Qual,+        b_exts  :: Extensions,+        b_virtual_offset :: Int64 -- ^ virtual offset for indexing purposes+    } deriving Show++nullBamRec :: BamRec+nullBamRec = BamRec {+        b_qname = S.empty,+        b_flag  = flagUnmapped,+        b_rname = invalidRefseq,+        b_pos   = invalidPos,+        b_mapq  = Q 0,+        b_cigar = VS.empty,+        b_mrnm  = invalidRefseq,+        b_mpos  = invalidPos,+        b_isize = 0,+        b_seq   = V.empty,+        b_qual  = VS.empty,+        b_exts  = [],+        b_virtual_offset = 0+    }++getMd :: BamRec -> Maybe [MdOp]+getMd r = case lookup "MD" $ b_exts r of+    Just (Text mdfield) -> readMd mdfield+    Just (Char mdfield) -> readMd $ B.singleton mdfield+    _                   -> Nothing++-- | A vector that packs two 'Nucleotides' into one byte, just like Bam does.+data Vector_Nucs_half a = Vector_Nucs_half !Int !Int !(ForeignPtr Word8)++-- | A mutable vector that packs two 'Nucleotides' into one byte, just like Bam does.+data MVector_Nucs_half s a = MVector_Nucs_half !Int !Int !(ForeignPtr Word8)++type instance V.Mutable Vector_Nucs_half = MVector_Nucs_half++instance V.Vector Vector_Nucs_half Nucleotides where+    {-# INLINE basicUnsafeFreeze #-}+    basicUnsafeFreeze (MVector_Nucs_half o l fp) = return $  Vector_Nucs_half o l fp+    {-# INLINE basicUnsafeThaw #-}+    basicUnsafeThaw    (Vector_Nucs_half o l fp) = return $ MVector_Nucs_half o l fp++    {-# INLINE basicLength #-}+    basicLength          (Vector_Nucs_half _ l  _) = l+    {-# INLINE basicUnsafeSlice #-}+    basicUnsafeSlice s l (Vector_Nucs_half o _ fp) = Vector_Nucs_half (o + s) l fp++    {-# INLINE basicUnsafeIndexM #-}+    basicUnsafeIndexM (Vector_Nucs_half o _ fp) i+        | even (o+i) = return . Ns $! (b `shiftR` 4) .&. 0xF+        | otherwise  = return . Ns $!  b             .&. 0xF+      where !b = unsafeInlineIO $ withForeignPtr fp $ \p -> peekByteOff p ((o+i) `shiftR` 1)++instance VM.MVector MVector_Nucs_half Nucleotides where+    {-# INLINE basicLength #-}+    basicLength          (MVector_Nucs_half _ l  _) = l+    {-# INLINE basicUnsafeSlice #-}+    basicUnsafeSlice s l (MVector_Nucs_half o _ fp) = MVector_Nucs_half (o + s) l fp++    {-# INLINE basicOverlaps #-}+    basicOverlaps (MVector_Nucs_half _ _ fp1) (MVector_Nucs_half _ _ fp2) = fp1 == fp2+    {-# INLINE basicUnsafeNew #-}+    basicUnsafeNew l = unsafePrimToPrim $ MVector_Nucs_half 0 l <$> mallocForeignPtrBytes ((l+1) `shiftR` 1)++    {-# INLINE basicInitialize #-}+    basicInitialize v@(MVector_Nucs_half o l fp)++        | even    o = do unsafePrimToPrim $ withForeignPtr fp $ \p ->+                            memset (plusPtr p (o `shiftR` 1)) 0 (fromIntegral $ l `shiftR` 1)+                         when (odd l) $ VM.basicUnsafeWrite v (l-1) (Ns 0)++        | otherwise = do when (odd o) $ VM.basicUnsafeWrite v 0 (Ns 0)+                         unsafePrimToPrim $ withForeignPtr fp $ \p ->+                            memset (plusPtr p ((o+1) `shiftR` 1)) 0 (fromIntegral $ (l-1) `shiftR` 1)+                         when (even l) $ VM.basicUnsafeWrite v (l-1) (Ns 0)+++    {-# INLINE basicUnsafeRead #-}+    basicUnsafeRead (MVector_Nucs_half o _ fp) i+        | even (o+i) = liftM (Ns . (.&.) 0xF . (`shiftR` 4)) b+        | otherwise  = liftM (Ns . (.&.) 0xF               ) b+      where b = unsafePrimToPrim $ withForeignPtr fp $ \p -> peekByteOff p ((o+i) `shiftR` 1)++    {-# INLINE basicUnsafeWrite #-}+    basicUnsafeWrite (MVector_Nucs_half o _ fp) i (Ns x) =+        unsafePrimToPrim $ withForeignPtr fp $ \p -> do+            y <- peekByteOff p ((o+i) `shiftR` 1)+            let y' | even (o+i) = x `shiftL` 4 .|. y .&. 0x0F+                   | otherwise  = x            .|. y .&. 0xF0+            pokeByteOff p ((o+i) `shiftR` 1) y'++foreign import ccall unsafe "string.h memset" memset+    :: Ptr Word8 -> CInt -> CSize -> IO ()++instance Show (Vector_Nucs_half Nucleotides) where+    show = show . V.toList++-- | Bam record in its native encoding along with virtual address.+data BamRaw = BamRaw { virt_offset :: {-# UNPACK #-} !Int64+                     , raw_data    :: {-# UNPACK #-} !Bytes }++-- | Smart constructor.  Makes sure we got a at least a full record.+{-# INLINE bamRaw #-}+bamRaw :: Int64 -> Bytes -> BamRaw+bamRaw o s = if good then BamRaw o s else error $ "broken BAM record " ++ shows (S.length s, m) " " ++ show (S.unpack (S.take 10 s))+  where+    good | S.length s < 32 = False+         | otherwise       = S.length s >= sum m+    m = [ 32, l_rnm, l_seq, (l_seq+1) `div` 2, l_cig * 4 ]+    l_rnm = fromIntegral (B.unsafeIndex s  8) - 1+    l_cig = fromIntegral (B.unsafeIndex s 12)             .|. fromIntegral (B.unsafeIndex s 13) `shiftL`  8+    l_seq = fromIntegral (B.unsafeIndex s 16)             .|. fromIntegral (B.unsafeIndex s 17) `shiftL`  8 .|.+            fromIntegral (B.unsafeIndex s 18) `shiftL` 16 .|. fromIntegral (B.unsafeIndex s 19) `shiftL` 24++{-# INLINE[1] unpackBam #-}+unpackBam :: BamRaw -> BamRec+unpackBam br = BamRec {+        b_rname =      Refseq $ getWord32  0,+        b_pos   =               getInt32   4,+        b_mapq  =           Q $ getInt8    9,+        b_flag  =               getInt16  14,+        b_mrnm  =      Refseq $ getWord32 20,+        b_mpos  =               getInt32  24,+        b_isize =               getInt32  28,++        b_qname = B.unsafeTake l_read_name $ B.unsafeDrop 32 $ raw_data br,+        b_cigar = VS.unsafeCast $ VS.unsafeFromForeignPtr fp (off0+off_c) (4*l_cigar),+        b_seq   = Vector_Nucs_half (2 * (off_s+off0)) l_seq fp,+        b_qual  = VS.unsafeCast $ VS.unsafeFromForeignPtr fp (off0+off_q) l_seq,++        b_exts  = unpackExtensions $ S.drop off_e $ raw_data br,+        b_virtual_offset = virt_offset br }+  where+        (fp, off0, _) = B.toForeignPtr $ raw_data br+        off_c =    33 + l_read_name+        off_s = off_c + 4 * l_cigar+        off_q = off_s + (l_seq + 1) `div` 2+        off_e = off_q +  l_seq++        l_read_name = getInt8    8 - 1+        l_seq       = getWord32 16+        l_cigar     = getInt16  12++        getInt8 :: Num a => Int -> a+        getInt8  o = fromIntegral (B.unsafeIndex (raw_data br) o)++        getInt16 :: Num a => Int -> a+        getInt16 o = unsafeDupablePerformIO $ B.unsafeUseAsCString (raw_data br) $+                     fmap fromIntegral . peekUnalnWord16LE . flip plusPtr o++        getWord32 :: Num a => Int -> a+        getWord32 o = unsafeDupablePerformIO $ B.unsafeUseAsCString (raw_data br) $+                      fmap fromIntegral . peekUnalnWord32LE . flip plusPtr o++        -- ensures proper sign extension+        getInt32 :: Num a => Int -> a+        getInt32 o = fromIntegral (getWord32 o :: Int32)++-- | A collection of extension fields.  A 'BamKey' is actually two ASCII+-- characters.+type Extensions = [( BamKey, Ext )]++-- | Deletes all occurences of some extension field.+deleteE :: BamKey -> Extensions -> Extensions+deleteE k = filter ((/=) k . fst)++-- | Blindly inserts an extension field.  This can create duplicates+-- (and there is no telling how other tools react to that).+insertE :: BamKey -> Ext -> Extensions -> Extensions+insertE k v = (:) (k,v)++-- | Deletes all occurences of an extension field, then inserts it with+-- a new value.  This is safer than 'insertE', but also more expensive.+updateE :: BamKey -> Ext -> Extensions -> Extensions+updateE k v = insertE k v . deleteE k++-- | Adjusts a named extension by applying a function.+adjustE :: (Ext -> Ext) -> BamKey -> Extensions -> Extensions+adjustE _ _ [         ]             = []+adjustE f k ((k',v):es) | k  ==  k' = (k', f v) : es+                        | otherwise = (k',   v) : adjustE f k es++data Ext = Int Int | Float Float | Text Bytes | Bin Bytes | Char Word8+         | IntArr (U.Vector Int) | FloatArr (U.Vector Float)+    deriving (Show, Eq, Ord)++{-# INLINE unpackExtensions #-}+unpackExtensions :: Bytes -> Extensions+unpackExtensions = go+  where+    go s | S.length s < 4 = []+         | otherwise = let key = fromString [ S.index s 0, S.index s 1 ]+                       in case S.index s 2 of+                         'Z' -> case S.break (== '\0') (S.drop 3 s) of (l,r) -> (key, Text l) : go (S.drop 1 r)+                         'H' -> case S.break (== '\0') (S.drop 3 s) of (l,r) -> (key, Bin  l) : go (S.drop 1 r)+                         'A' -> (key, Char (B.index s 3)) : go (S.drop 4 s)+                         'B' -> let tp = S.index s 3+                                    n  = getInt 'I' (S.drop 4 s)+                                in case tp of+                                      'f' -> (key, FloatArr (U.fromListN (n+1) [ getFloat (S.drop i s) | i <- [8, 12 ..] ]))+                                             : go (S.drop (12+4*n) s)+                                      _   -> (key, IntArr (U.fromListN (n+1) [ getInt tp (S.drop i s) | i <- [8, 8 + size tp ..] ]))+                                             : go (S.drop (8 + size tp * (n+1)) s)+                         'f' -> (key, Float (getFloat (S.drop 3 s))) : go (S.drop 7 s)+                         tp  -> (key, Int  (getInt tp (S.drop 3 s))) : go (S.drop (3 + size tp) s)++    size 'C' = 1+    size 'c' = 1+    size 'S' = 2+    size 's' = 2+    size 'I' = 4+    size 'i' = 4+    size 'f' = 4+    size  _  = 0++    getInt 'C' s | S.length s >= 1 = fromIntegral              ((B.index s 0) :: Word8)+    getInt 'c' s | S.length s >= 1 = fromIntegral (fromIntegral (B.index s 0) ::  Int8)+    getInt 'S' s | S.length s >= 2 = fromIntegral                         (i :: Word16)+        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord16LE+    getInt 's' s | S.length s >= 2 = fromIntegral            (fromIntegral i ::  Int16)+        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord16LE+    getInt 'I' s | S.length s >= 4 = fromIntegral                         (i :: Word32)+        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord32LE+    getInt 'i' s | S.length s >= 4 = fromIntegral            (fromIntegral i ::  Int32)+        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord32LE+    getInt _ _ = 0++    getFloat s = unsafeDupablePerformIO $ alloca $ \buf ->+                 pokeByteOff buf 0 (getInt 'I' s :: Word32) >> peek buf+++isPaired, isProperlyPaired, isUnmapped, isMateUnmapped, isReversed,+    isMateReversed, isFirstMate, isSecondMate, isAuxillary, isSecondary,+    isFailsQC, isDuplicate, isSupplementary,+    isTrimmed, isMerged, isAlternative, isExactIndex :: BamRec -> Bool++isPaired         = flip testBit  0 . b_flag+isProperlyPaired = flip testBit  1 . b_flag+isUnmapped       = flip testBit  2 . b_flag+isMateUnmapped   = flip testBit  3 . b_flag+isReversed       = flip testBit  4 . b_flag+isMateReversed   = flip testBit  5 . b_flag+isFirstMate      = flip testBit  6 . b_flag+isSecondMate     = flip testBit  7 . b_flag+isAuxillary      = flip testBit  8 . b_flag+isSecondary      = flip testBit  8 . b_flag+isFailsQC        = flip testBit  9 . b_flag+isDuplicate      = flip testBit 10 . b_flag+isSupplementary  = flip testBit 11 . b_flag++isTrimmed        = flip testBit 0 . extAsInt 0 "FF"+isMerged         = flip testBit 1 . extAsInt 0 "FF"+isAlternative    = flip testBit 2 . extAsInt 0 "FF"+isExactIndex     = flip testBit 3 . extAsInt 0 "FF"++type_mask :: Int+type_mask = flagFirstMate .|. flagSecondMate .|. flagPaired++extAsInt :: Int -> BamKey -> BamRec -> Int+extAsInt d nm br = case lookup nm (b_exts br) of Just (Int i) -> i ; _ -> d++extAsString :: BamKey -> BamRec -> Bytes+extAsString nm br = case lookup nm (b_exts br) of+    Just (Char c) -> B.singleton c+    Just (Text s) -> s+    _             -> B.empty++setQualFlag :: Char -> BamRec -> BamRec+setQualFlag c br = br { b_exts = updateE "ZQ" (Text s') $ b_exts br }+  where+    s  = extAsString "ZQ" br+    s' = if c `S.elem` s then s else c `S.cons` s+
+ Bio/Bam/Regions.hs view
@@ -0,0 +1,53 @@+module Bio.Bam.Regions+    ( Region(..)+    , Regions(..)+    , Subsequence(..)++    , toList+    , fromList+    , overlaps+    ) where++import Bio.Bam.Header ( Refseq(..) )+import Bio.Prelude hiding ( toList )++import qualified Data.IntMap.Strict as IM++data Region = Region { refseq :: !Refseq, start :: !Int, end :: !Int }+  deriving (Eq, Ord, Show)++-- | A subset of a genome.  The idea is to map the reference sequence+-- (represented by its number) to a 'Subseqeunce'.+newtype Regions = Regions (IntMap Subsequence) deriving Show++-- | A mostly contiguous subset of a sequence, stored as a set of+-- non-overlapping intervals in an 'IntMap' from start position to end+-- position (half-open intervals, naturally).+newtype Subsequence = Subsequence (IntMap Int) deriving Show++toList :: Regions -> [(Refseq, Subsequence)]+toList (Regions m) = [ (Refseq $ fromIntegral k, v) | (k,v) <- IM.toList m ]++fromList :: [Region] -> Regions+fromList = foldl' (flip add) (Regions IM.empty)++add :: Region -> Regions -> Regions+add (Region (Refseq r) b e) (Regions m) =+    let single = Just . Subsequence $ IM.singleton b e+    in Regions $ IM.alter (maybe single (Just . addInt b e)) (fromIntegral r) m+++addInt :: Int -> Int -> Subsequence -> Subsequence+addInt b e (Subsequence m0) = Subsequence $ merge_into b e m0+  where+    merge_into x y m = case IM.lookupLT y m of+        Just (u,v) | x < u && y <= v -> merge_into x v $ IM.delete u m    -- extend to the left+                   | x < u           -> merge_into x y $ IM.delete u m    -- subsume+                   | y <= v          -> m                                 -- subsumed+                   | x <= v          -> merge_into u y $ IM.delete u m    -- extend to the right+        _                            -> IM.insert  x y m                  -- no overlap++overlaps :: Int -> Int -> Subsequence -> Bool+overlaps b e (Subsequence m) = case IM.lookupLT e m of+        Just (_,v) -> b < v+        Nothing    -> False
+ Bio/Bam/Rmdup.hs view
@@ -0,0 +1,691 @@+module Bio.Bam.Rmdup(+            rmdup, Collapse, cons_collapse, cheap_collapse,+            cons_collapse_keep, cheap_collapse_keep,+            check_sort, normalizeTo, wrapTo,+            ECig(..), toECig, setMD, toCigar+    ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude hiding ( left, right )+import Bio.Streaming++import qualified Data.ByteString        as B+import qualified Data.ByteString.Char8  as T+import qualified Data.Map.Strict        as M+import qualified Data.Vector.Generic    as V+import qualified Data.Vector.Storable   as W+import qualified Data.Vector.Unboxed    as U+import qualified Streaming.Prelude      as Q++data Collapse = Collapse {+        collapse  :: [BamRec] -> (Decision,[BamRec]),    -- cluster to consensus and stuff or representative and stuff+        originals :: [BamRec] -> [BamRec] }              -- treatment of the redundant original reads++data Decision = Consensus      { fromDecision :: BamRec }+              | Representative { fromDecision :: BamRec }++cons_collapse :: Qual -> Collapse+cons_collapse maxq = Collapse (do_collapse maxq) (const [])++cons_collapse_keep :: Qual -> Collapse+cons_collapse_keep maxq = Collapse (do_collapse maxq) (map (\b -> b { b_flag = b_flag b .|. flagDuplicate }))++cheap_collapse :: Collapse+cheap_collapse = Collapse do_cheap_collapse (const [])++cheap_collapse_keep :: Collapse+cheap_collapse_keep = Collapse do_cheap_collapse (map (\b -> b { b_flag = b_flag b .|. flagDuplicate }))+++-- | Removes duplicates from an aligned, sorted BAM stream.+--+-- The incoming stream must be sorted by coordinate, and we check for+-- violations of that assumption.  We cannot assume that length was+-- taken into account when sorting (samtools doesn't do so), so+-- duplicates may be separated by reads that start at the same position+-- but have different length or different strand.+--+-- We are looking at three different kinds of reads:  paired reads, true+-- single ended reads, merged or trimmed reads.  They are somewhat+-- different, but here's the situation if we wanted to treat them+-- separately.  These conditions define a set of duplicates:+--+-- Merged or trimmed:  We compare the leftmost coordinates and the+-- aligned length.  If the library prep is strand-preserving, we also+-- compare the strand.+--+-- Paired: We compare both left-most coordinates (b_pos and b_mpos).  If+-- the library prep is strand-preserving, only first-mates can be+-- duplicates of first-mates.  Else a first-mate can be the duplicate of+-- a second-mate.  There may be pairs with one unmapped mate.  This is+-- not a problem as they get assigned synthetic coordinates and will be+-- handled smoothly.+--+-- True singles:  We compare only the leftmost coordinate.  It does not+-- matter if the library prep is strand-preserving, the strand always+-- matters.+--+-- Across these classes, we can see more duplicates:+--+-- Merged/trimmed and paired:  these can be duplicates if the merging+-- failed for the pair.  We would need to compare the outer coordinates+-- of the merged reads to the two 5' coordinates of the pair.  However,+-- since we don't have access to the mate, we cannot actually do+-- anything right here.  This case should be solved externally by+-- merging those pairs that overlap in coordinate space.+--+-- Single and paired:  in the single case, we only have one coordinate+-- to compare.  This will inevitably lead to trouble, as we could find+-- that the single might be the duplicate of two pairs, but those two+-- pairs are definitely not duplicates of each other.  We solve it by+-- removing the single read(s).+--+-- Single and merged/trimmed:  same trouble as in the single+paired+-- case.  We remove the single to solve it.+--+--+-- In principle, we might want to allow some wiggle room in the+-- coordinates.  So far, this has not been implemented.  It adds the+-- complication that groups of separated reads can turn into a set of+-- duplicates because of the appearance of a new reads.  Needs some+-- thinking about... or maybe it's not too important.+--+-- Once a set of duplicates is collected, we perform a majority vote on+-- the correct CIGAR line.  Of all those reads that agree on this CIGAR+-- line, a consensus is called, quality scores are adjusted and clamped+-- to a maximum, the MD field is updated and the XP field is assigned+-- the number of reads in the original cluster.  The new MAPQ becomes+-- the RMSQ of the map qualities of all reads.+--+-- Treatment of Read Groups:  We generalize by providing a "label"+-- function; only reads that have the same label are considered+-- duplicates of each other.  The typical label function would extract+-- read groups, libraries or samples.++rmdup :: (Monad m, Ord l) => (BamRec -> l) -> Bool -> Collapse+      -> Q.Stream (Of BamRec) m r -> Q.Stream (Of (Int,BamRec)) m r+rmdup label strand_preserved collapse_cfg =+    -- Easiest way to go about this:  We simply collect everything that+    -- starts at some specific coordinate and group it appropriately.+    -- Treat the groups separately, output, go on.+    check_sort (b_cpos . snd) "internal error, output isn't sorted anymore" .+    mapGroups ( sortBy (comparing $ V.length . b_seq . snd)+              . concatMap (do_rmdup strand_preserved collapse_cfg)+              . M.elems . accumMap label id ) .+    check_sort b_cpos "input must be sorted for rmdup to work"+  where+    b_cpos = b_rname &&& b_pos++    mapGroups f = lift . inspect >=> either pure (\(a :> s) -> mg1 f [] a s)++    mg1 f acc a s =+        lift (inspect s) >>= \case+            Left r  -> Q.each (f (a:acc)) >> pure r+            Right (b :> s')+                | b_cpos a == b_cpos b -> mg1 f (b:acc) a s'+                | otherwise            -> Q.each (f (a:acc)) >> mg1 f [] b s'++check_sort :: (Monad m, Ord b) => (a -> b) -> String -> Stream (Of a) m r -> Stream (Of a) m r+check_sort f msg = lift . inspect >=> either pure step+  where+    step (a :> s) = lift (inspect s) >>= either (Q.cons a . pure) (step' a)+    step' a (b :> s)+        | f a > f b = fail $ "rmdup: " ++ msg+        | otherwise = Q.cons a $ step (b :> s)+{-# INLINE check_sort #-}++{- | Workhorse for duplicate removal.++ - Unmapped fragments should not be considered to be duplicates of+   mapped fragments.  The /unmapped/ flag can serve for that:  while+   there are two classes of /unmapped/ reads (those that are not mapped+   and those that are mapped to an invalid position), the two sets will+   always have different coordinates.  (Unfortunately, correct duplicate+   removal now relies on correct /unmapped/ and /mate unmapped/ flags,+   and we don't get them from unmodified BWA.  So correct operation+   requires patched BWA or a run of @bam-fixpair@.)++   (1) Other definitions (e.g. lack of CIGAR) don't work, because that+       information won't be available for the mate.++   (2) This would amount to making the /unmapped/ flag part of the+       coordinate, but samtools is not going to take it into account+       when sorting.++   (3) Instead, both flags become part of the /mate pos/ grouping+       criterion.++ - First Mates should (probably) not be considered duplicates of Second+   Mates.  This is unconditionally true for libraries with A\/B-style+   adapters (definitely 454, probably Mathias' ds protocol) and the ss+   protocol, it is not true for fork adapter protocols (vanilla Illumina+   protocol).  So it has to be an option, which would ideally be derived+   from header information.++ - This code ignores read groups, but it will do a majority vote on the+   @RG@ field and call consensi for the index sequences.  If you believe+   that duplicates across read groups are impossible, you must call it+   with an appropriately filtered stream.++ - Half-Aligned Pairs (meaning one known coordinate, while the validity+   of the alignments is immaterial) are rather complicated:++   (1) Given that only one coordinate is known (5' of the aligned mate),+       we want to treat them like true singles.  But the unaligned mate+       should be kept if possible, though it should not contribute to a+       consensus sequence.  We assume nothing about the unaligned mate,+       not even that it /shouldn't/ be aligned, never mind the fact that+       it /couldn't/ be.  (The difference is in the finite abilities of+       real world aligners, naturally.)++   (2) Therefore, aligned reads with unaligned mates go to the same+       potential duplicate set as true singletons.  If at least one pair+       exists that might be a duplicate of those, all singletons and+       half-aligned mates are discarded.  Else a consensus is computed+       and replaces the aligned mates.++   (3) The unaligned mates end up in the same place in a BAM stream as+       the aligned mates (therefore we see them and can treat them+       locally).  We cannot call a consensus, since these molecules may+       well have different length, so we select one.  It doesn't really+       matter which one is selected, and since we're treating both mates+       at the same time, it doesn't even need to be reproducible without+       local information.  This is made to be the mate of the consensus.++   (4) See 'merge_singles' for how it's actually done.+-}++do_rmdup :: Bool -> Collapse -> [BamRec] -> [(Int,BamRec)]+do_rmdup strand_preserved Collapse{..} rds =+        results ++ map ((,) 0) (originals (leftovers ++ r1 ++ r2 ++ r3))+    where+        (results, leftovers) = merge_singles singles' unaligned' $+                [ (str, second fromDecision b) | ((_,str  ),b) <- M.toList merged' ] +++                [ (str, second fromDecision b) | ((_,str,_),b) <- M.toList pairs' ]++        (raw_pairs, raw_singles)       = partition isPaired rds+        (merged, true_singles)         = partition (liftA2 (||) isMerged isTrimmed) raw_singles++        (pairs, raw_half_pairs)        = partition b_totally_aligned raw_pairs+        (half_unaligned, half_aligned) = partition isUnmapped raw_half_pairs++        mkMap :: Ord a => (BamRec -> a) -> [BamRec] -> (M.Map a (Int,Decision), [BamRec])+        mkMap f x = let m1 = M.map (length &&& collapse) $ accumMap f id x+                    in (M.map (second fst) m1, concatMap (snd.snd) $ M.elems m1)++        (pairs',r1)   = mkMap (\b -> (b_mate_pos b,   b_strand b, b_mate b)) pairs+        (merged',r2)  = mkMap (\b -> (alignedLength (b_cigar b), b_strand b))           merged+        (singles',r3) = mkMap                         b_strand (true_singles++half_aligned)+        unaligned'    = accumMap b_strand id half_unaligned++        b_strand b = strand_preserved && isReversed  b+        b_mate   b = strand_preserved && isFirstMate b+++-- | Merging information about true singles, merged singles,+-- half-aligned pairs, actually aligned pairs.+--+-- We collected aligned reads with unaligned mates together with aligned+-- true singles (@singles@).  We collected the unaligned mates, which+-- necessarily have the exact same alignment coordinates, separately+-- (@unaligned@).  If we don't find a matching true pair (that case is+-- already handled smoothly), we keep the highest quality unaligned+-- mate, pair it with the consensus of the aligned mates and aligned+-- singletons, and give it the lexically smallest name of the+-- half-aligned pairs.++-- NOTE:  I need to decide when to run 'make_singleton'.  Basically,+-- when we call a consensus for half-aligned pairs and keep+-- everything(?).  Then we don't have a mate for the consensus... though+-- we could decide to duplicate one mate read to get it.++merge_singles :: M.Map Bool (Int,Decision)              -- strand --> true singles & half aligned+              -> M.Map Bool [BamRec]                    -- strand --> half unaligned+              -> [ (Bool, (Int, BamRec)) ]              -- strand --> paireds & mergeds+              -> ([(Int,BamRec)],[BamRec])              -- results, leftovers++merge_singles singles unaligneds = go+  where+    -- Say we generated a consensus or passed something through.  If+    -- there is a singleton consensus with the same strand, we should+    -- add in its XP field and discard it.  If there is a singleton+    -- representative, we add in its XP field and put it into the+    -- leftovers.  If there is unaligned stuff here that has the same+    -- strand, it goes to the leftovers.+    go ( (str, (m,v)) : paireds) =+        let (r,l) = merge_singles (M.delete str singles) (M.delete str unaligneds) paireds+            unal  = M.findWithDefault [] str unaligneds ++ l++        in case M.lookup str singles of+            Nothing                    -> (              (m,v) : r,     unal )+            Just (n, Consensus      w) -> ( (n, add_xp_of w v) : r,     unal )+            Just (n, Representative w) -> ( (n, add_xp_of w v) : r, w : unal )++    -- No more pairs, delegate the problem+    go [] = merge_halves unaligneds (M.toList singles)++    add_xp_of w v = v { b_exts = updateE "XP" (Int $ extAsInt 1 "XP" w `oplus` extAsInt 1 "XP" v) (b_exts v) }++-- | Merging of half-aligned reads.  The first argument is a map of+-- unaligned reads (their mates are aligned to the current position),+-- the second is a list of reads that are aligned (their mates are not+-- aligned).+--+-- So, suppose we're looking at a 'Representative' that was passed+-- through.  We need to emit it along with its mate, which may be hidden+-- inside a list.  (Alternatively, we could force it to single, but that+-- fails if we're passing everything along somehow.)+--+-- Suppose we're looking at a 'Consensus'.  We could pair it with some+-- mate (which we'd need to duplicate), or we could turn it into a+-- singleton.  Duplication is ugly, so in this case, we force it to+-- singleton.++merge_halves :: M.Map Bool [BamRec]                     -- strand --> half unaligned+             -> [(Bool, (Int,Decision))]                -- strand --> true singles & half aligned+             -> ([(Int,BamRec)],[BamRec])               -- results, leftovers++-- Emitting a consensus: make it a single.  Nothing goes to leftovers;+-- we may still need it for something else to be emitted.  (While that+-- would be strange, making sure the BAM file stays completely valid is+-- probably better.)+merge_halves unaligneds ((_, (n, Consensus v)) : singles) = ( (n, v { b_flag = b_flag v .&. complement pflags }) : r, l )+  where+    (r,l)  = merge_halves unaligneds singles+    pflags = flagPaired .|. flagProperlyPaired .|. flagMateUnmapped .|. flagMateReversed .|. flagFirstMate .|. flagSecondMate+++-- Emitting a representative:  find the mate in the list of unaligned+-- reads (take up to one match to be robust), and emit that, too, as a+-- result.  Everything else goes to leftovers.  If the representative+-- happens to be unpaired, no mate is found and that case therefore is+-- handled smoothly.+merge_halves unaligneds ((str, (n, Representative v)) : singles) = ((n,v) : map ((,)1) (take 1 same) ++ r, drop 1 same ++ diff ++ l)+  where+    (r,l)          = merge_halves (M.delete str unaligneds) singles+    (same,diff)    = partition (is_mate_of v) $ M.findWithDefault [] str unaligneds+    is_mate_of a b = b_qname a == b_qname b && isPaired a && isPaired b && isFirstMate a == isSecondMate b++-- No more singles, all unaligneds are leftovers.+merge_halves unaligneds [] = ( [], concat $ M.elems unaligneds )+++++type MPos = (Refseq, Int, Bool, Bool)++b_mate_pos :: BamRec -> MPos+b_mate_pos b = (b_mrnm b, b_mpos b, isUnmapped b, isMateUnmapped b)++b_totally_aligned :: BamRec -> Bool+b_totally_aligned b = not (isUnmapped b || isMateUnmapped b)+++accumMap :: Ord k => (a -> k) -> (a -> v) -> [a] -> M.Map k [v]+accumMap f g = go M.empty+  where+    go m [    ] = m+    go m (a:as) = let ws = M.findWithDefault [] (f a) m ; g' = g a+                  in g' `seq` go (M.insert (f a) (g':ws) m) as+++{- We need to deal sensibly with each field, but different fields have+   different needs.  We can take the value from the first read to+   preserve determinism or because all reads should be equal anyway,+   aggregate over all reads computing either RMSQ or the most common+   value, delete a field because it wouldn't make sense anymore or+   because doing something sensible would be hard and we're going to+   ignore it anyway, or we calculate some special value; see below.+   Unknown fields will be taken from the first read, which seems to be a+   safe default.++   QNAME and most fields              taken from first+   FLAG qc fail                       majority vote+        dup                           deleted+   MAPQ                               rmsq+   CIGAR, SEQ, QUAL, MD, NM, XP       generated+   XA                                 concatenate all++   BQ, CM, FZ, Q2, R2, XM, XO, XG, YQ, EN+         deleted because they would become wrong++   CQ, CS, E2, FS, OQ, OP, OC, U2, H0, H1, H2, HI, NH, IH, ZQ+         delete because they will be ignored anyway++   AM, AS, MQ, PQ, SM, UQ+         compute rmsq++   X0, X1, XT, XS, XF, XE, BC, LB, RG, XI, YI, XJ, YJ+         majority vote -}++do_collapse :: Qual -> [BamRec] -> (Decision, [BamRec])+do_collapse maxq [br] | V.all (<= maxq) (b_qual br) = ( Representative br, [  ] )     -- no modifcation, pass through+                      | otherwise                   = ( Consensus   lq_br, [br] )     -- qualities reduced, must keep original+  where+    lq_br = br { b_qual  = V.map (min maxq) $ b_qual br+               , b_virtual_offset = 0+               , b_qname = b_qname br `B.snoc` c2w 'c' }++do_collapse maxq  brs = ( Consensus b0 { b_exts  = modify_extensions $ b_exts b0+                                       , b_flag  = failflag .&. complement flagDuplicate+                                       , b_mapq  = Q $ rmsq $ map (unQ . b_mapq) $ good brs+                                       , b_cigar = cigar'+                                       , b_seq   = V.fromList $ map fst cons_seq_qual+                                       , b_qual  = V.fromList $ map snd cons_seq_qual+                                       , b_qname = b_qname b0 `B.snoc` 99+                                       , b_virtual_offset = 0 }, brs )              -- many modifications, must keep everything+  where+    !b0 = minimumBy (comparing b_qname) brs+    !most_fail = 2 * length (filter isFailsQC brs) > length brs+    !failflag | most_fail = b_flag b0 .|. flagFailsQC+              | otherwise = b_flag b0 .&. complement flagFailsQC++    rmsq xs = case foldl' (\(!n,!d) x -> (n + fromIntegral x * fromIntegral x, d + 1)) (0,0) xs of+        (!n,!d) -> round $ sqrt $ (n::Double) / fromIntegral (d::Int)++    maj xs = head . maximumBy (comparing length) . group . sort $ xs+    nub' = concatMap head . group . sort++    -- majority vote on the cigar lines, then filter+    !cigar' = maj $ map b_cigar brs+    good = filter ((==) cigar' . b_cigar)++    cons_seq_qual = [ consensus maxq [ (V.unsafeIndex (b_seq b) i, q)+                                     | b <- good brs, let q = if V.null (b_qual b) then Q 23 else b_qual b V.! i ]+                    | i <- [0 .. len - 1] ]+        where !len = V.length . b_seq . head $ good brs++    md' = case [ (b_seq b,md,b) | b <- good brs, Just md <- [ getMd b ] ] of+                [               ] -> []+                (seq1, md1,b) : _ -> case mk_new_md' [] (V.toList cigar') md1 (V.toList seq1) (map fst cons_seq_qual) of+                    Right x -> x+                    Left (MdFail cigs ms osq nsq) -> error $ unlines+                                    [ "Broken MD field when trying to construct new MD!"+                                    , "QNAME: " ++ show (b_qname b)+                                    , "POS:   " ++ shows (unRefseq (b_rname b)) ":" ++ show (b_pos b)+                                    , "CIGAR: " ++ show cigs+                                    , "MD:    " ++ show ms+                                    , "refseq:  " ++ show osq+                                    , "readseq: " ++ show nsq ]+++    nm' = sum $ [ n | Ins :* n <- W.toList cigar' ] ++ [ n | Del :* n <- W.toList cigar' ] ++ [ 1 | MdRep _ <- md' ]+    xa' = nub' [ T.split ';' xas | Just (Text xas) <- map (lookup "XA" . b_exts) brs ]++    modify_extensions es = foldr ($!) es $+        [ let vs = mapMaybe (lookup k . b_exts) brs+          in if null vs then id else updateE k $! maj vs | k <- do_maj ] +++        [ let vs = [ v | Just (Int v) <- map (lookup k . b_exts) brs ]+          in if null vs then id else updateE k $! Int (rmsq vs) | k <- do_rmsq ] +++        map deleteE useless +++        [ updateE "NM" $! Int nm'+        , updateE "XP" $! Int (foldl' (\a b -> a `oplus` extAsInt 1 "XP" b) 0 brs)+        , if null xa' then id else updateE "XA" $! (Text $ T.intercalate (T.singleton ';') xa')+        , if null md' then id else updateE "MD" $! (Text $ showMd md') ]++    useless = map fromString $ words "BQ CM FZ Q2 R2 XM XO XG YQ EN CQ CS E2 FS OQ OP OC U2 H0 H1 H2 HI NH IH ZQ"+    do_rmsq = map fromString $ words "AM AS MQ PQ SM UQ"+    do_maj  = map fromString $ words "X0 X1 XT XS XF XE BC LB RG XI XJ YI YJ"++minViewBy :: (a -> a -> Ordering) -> [a] -> (a,[a])+minViewBy  _  [    ] = error "minViewBy on empty list"+minViewBy cmp (x:xs) = go x [] xs+  where+    go m acc [    ] = (m,acc)+    go m acc (a:as) = case m `cmp` a of GT -> go a (m:acc) as+                                        _  -> go m (a:acc) as++data MdFail = MdFail [Cigar] [MdOp] [Nucleotides] [Nucleotides]++mk_new_md' :: [MdOp] -> [Cigar] -> [MdOp] -> [Nucleotides] -> [Nucleotides] -> Either MdFail [MdOp]+mk_new_md' acc [] [] [] [] = Right $ normalize [] acc+    where+        normalize          a  (MdNum  0:os) = normalize               a  os+        normalize (MdNum n:a) (MdNum  m:os) = normalize (MdNum  (n+m):a) os+        normalize          a  (MdDel []:os) = normalize               a  os+        normalize (MdDel u:a) (MdDel  v:os) = normalize (MdDel (v++u):a) os+        normalize          a  (       o:os) = normalize            (o:a) os+        normalize          a  [           ] = a++mk_new_md' acc ( _ :* 0 : cigs) mds  osq nsq = mk_new_md' acc cigs mds osq nsq+mk_new_md' acc cigs (MdNum  0 : mds) osq nsq = mk_new_md' acc cigs mds osq nsq+mk_new_md' acc cigs (MdDel [] : mds) osq nsq = mk_new_md' acc cigs mds osq nsq++mk_new_md' acc (Mat :* u : cigs) (MdRep b : mds) (_:osq) (n:nsq)+    | b == n    = mk_new_md' (MdNum 1 : acc) (Mat :* (u-1):cigs) mds osq nsq+    | otherwise = mk_new_md' (MdRep b : acc) (Mat :* (u-1):cigs) mds osq nsq++mk_new_md' acc (Mat :* u : cigs) (MdNum v : mds) (o:osq) (n:nsq)+    | o == n    = mk_new_md' (MdNum 1 : acc) (Mat :* (u-1):cigs) (MdNum (v-1) : mds) osq nsq+    | otherwise = mk_new_md' (MdRep o : acc) (Mat :* (u-1):cigs) (MdNum (v-1) : mds) osq nsq++mk_new_md' acc (Del :* n : cigs) (MdDel bs : mds) osq nsq | n == length bs = mk_new_md' (MdDel bs : acc)         cigs               mds  osq nsq+mk_new_md' acc (Del :* n : cigs) (MdDel (b:bs) : mds) osq nsq = mk_new_md' (MdDel     [b] : acc) (Del :* (n-1) : cigs) (MdDel    bs:mds) osq nsq+mk_new_md' acc (Del :* n : cigs) (MdRep   b    : mds) osq nsq = mk_new_md' (MdDel     [b] : acc) (Del :* (n-1) : cigs)              mds  osq nsq+mk_new_md' acc (Del :* n : cigs) (MdNum   m    : mds) osq nsq = mk_new_md' (MdDel [nucsN] : acc) (Del :* (n-1) : cigs) (MdNum (m-1):mds) osq nsq++mk_new_md' acc (Ins :* n : cigs) md osq nsq = mk_new_md' acc cigs md (drop n osq) (drop n nsq)+mk_new_md' acc (SMa :* n : cigs) md osq nsq = mk_new_md' acc cigs md (drop n osq) (drop n nsq)+mk_new_md' acc (HMa :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq+mk_new_md' acc (Pad :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq+mk_new_md' acc (Nop :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq++mk_new_md' _acc cigs ms osq nsq = Left $ MdFail cigs ms osq nsq++consensus :: Qual -> [ (Nucleotides, Qual) ] -> (Nucleotides, Qual)+consensus (Q maxq) nqs =+    let accs :: U.Vector Int+        accs = U.accum (+) (U.replicate 16 0) [ (fromIntegral n, fromIntegral q) | (Ns n,Q q) <- nqs ]+    in case sortBy (flip $ comparing snd) $ zip [Ns 0 ..] $ U.toList accs of+        (n0,q0) : (_,q1) : _ ->+            let qr = fromIntegral $ (q0-q1) `min` fromIntegral maxq+            in if qr > 3 then (n0, Q qr) else (nucsN, Q 0)+        _ -> error "can't happen"+++-- Cheap version: simply takes the lexically first record, adds XP field+do_cheap_collapse :: [BamRec] -> ( Decision, [BamRec] )+do_cheap_collapse [b] = ( Representative                     b, [] )+do_cheap_collapse  bs = ( Representative $ replaceXP new_xp b0, bx )+  where+    (b0, bx) = minViewBy (comparing b_qname) bs+    new_xp   = foldl' (\a b -> a `oplus` extAsInt 1 "XP" b) 0 bs++replaceXP :: Int -> BamRec -> BamRec+replaceXP new_xp b0 = b0 { b_exts = updateE "XP" (Int new_xp) $ b_exts b0 }++oplus :: Int -> Int -> Int+_ `oplus` (-1) = -1+(-1) `oplus` _ = -1+a `oplus` b = a + b++-- | Normalize a read's alignment to fall into the canonical region+-- of [0..l].  Takes the name of the reference sequence and its length.+-- Returns @Left x@ if the coordinate decreased so the result is out of+-- order now, @Right x@ if the coordinate is unchanged.+normalizeTo :: Bytes -> Int -> BamRec -> Either BamRec BamRec+normalizeTo nm l b = lr $ b { b_pos  = b_pos b `mod` l+                            , b_mpos = b_mpos b `mod` l+                            , b_mapq = if dups_are_fine then Q 37 else b_mapq b+                            , b_exts = if dups_are_fine then deleteE "XA" (b_exts b) else b_exts b }+  where+    lr = if b_pos b >= l then Left else Right+    dups_are_fine  = all_match_XA (extAsString "XA" b)+    all_match_XA s = case T.split ';' s of [xa1, xa2] | T.null xa2 -> one_match_XA xa1+                                           [xa1]                   -> one_match_XA xa1+                                           _                       -> False+    one_match_XA s = case T.split ',' s of (sq:pos:_) | sq == nm   -> pos_match_XA pos ; _ -> False+    pos_match_XA s = case T.readInt s   of Just (p,z) | T.null z   -> int_match_XA p ;   _ -> False+    int_match_XA p | p >= 0    =  (p-1) `mod` l == b_pos b `mod` l && not (isReversed b)+                   | otherwise = (-p-1) `mod` l == b_pos b `mod` l && isReversed b+++-- | Wraps a read to be fully contained in the canonical interval+-- [0..l].  If the read overhangs, it is duplicated and both copies are+-- suitably masked.  A piece with changed coordinate that is now out of+-- order is returned as @Left x@, if the order is fine, it is returned+-- as @Right x@.+wrapTo :: Int -> BamRec -> [Either BamRec BamRec]+wrapTo l b = if overhangs then do_wrap else [Right b]+  where+    overhangs = not (isUnmapped b) && b_pos b < l && l < b_pos b + alignedLength (b_cigar b)++    do_wrap = case split_ecig (l - b_pos b) $ toECig (b_cigar b) (fromMaybe [] $ getMd b) of+                  (left,right) -> [ Right $ b { b_cigar = toCigar  left }            `setMD` left+                                  , Left  $ b { b_cigar = toCigar right, b_pos = 0 } `setMD` right ]++-- | Split an 'ECig' into two at some position.  The position is counted+-- in terms of the reference (therefore, deletions count, insertions+-- don't).  The parts that would be skipped if we were splitting lists+-- are replaced by soft masks.+split_ecig :: Int -> ECig -> (ECig, ECig)+split_ecig _    WithMD = (WithMD,       WithMD)+split_ecig _ WithoutMD = (WithoutMD, WithoutMD)+split_ecig 0       ecs = (mask_all ecs,    ecs)++split_ecig i (Ins' n ecs) = case split_ecig i ecs of (u,v) -> (Ins' n u, SMa' n v)+split_ecig i (SMa' n ecs) = case split_ecig i ecs of (u,v) -> (SMa' n u, SMa' n v)+split_ecig i (HMa' n ecs) = case split_ecig i ecs of (u,v) -> (HMa' n u, HMa' n v)+split_ecig i (Pad' n ecs) = case split_ecig i ecs of (u,v) -> (Pad' n u,        v)++split_ecig i (Mat' n ecs)+    | i >= n    = case split_ecig (i-n) ecs of (u,v) -> (Mat' n u, SMa' n v)+    | otherwise = (Mat' i $ SMa' (n-i) $ mask_all ecs, SMa' i $ Mat' (n-i) ecs)++split_ecig i (Rep' x ecs) = case split_ecig (i-1) ecs of (u,v) -> (Rep' x u, SMa' 1 v)+split_ecig i (Del' x ecs) = case split_ecig (i-1) ecs of (u,v) -> (Del' x u,        v)++split_ecig i (Nop' n ecs)+    | i >= n    = case split_ecig (i-n) ecs of (u,v) -> (Nop' n u,        v)+    | otherwise = (Nop' i $ mask_all ecs, Nop' (n-i) ecs)++mask_all :: ECig -> ECig+mask_all      WithMD = WithMD+mask_all   WithoutMD = WithoutMD+mask_all (Nop' _ ec) =          mask_all ec+mask_all (HMa' _ ec) =          mask_all ec+mask_all (Pad' _ ec) =          mask_all ec+mask_all (Del' _ ec) =          mask_all ec+mask_all (Rep' _ ec) = SMa' 1 $ mask_all ec+mask_all (Mat' n ec) = SMa' n $ mask_all ec+mask_all (Ins' n ec) = SMa' n $ mask_all ec+mask_all (SMa' n ec) = SMa' n $ mask_all ec++-- | Extended CIGAR.  This subsumes both the CIGAR string and the+-- optional MD field.  If we have MD on input, we generate it on output,+-- too.  And in between, we break everything into /very small/+-- operations.++data ECig = WithMD                      -- terminate, do generate MD field+          | WithoutMD                   -- terminate, don't bother with MD+          | Mat' Int ECig+          | Rep' Nucleotides ECig+          | Ins' Int ECig+          | Del' Nucleotides ECig+          | Nop' Int ECig+          | SMa' Int ECig+          | HMa' Int ECig+          | Pad' Int ECig+++toECig :: W.Vector Cigar -> [MdOp] -> ECig+toECig = go . W.toList+  where+    go        cs  (MdNum  0:mds) = go cs mds+    go        cs  (MdDel []:mds) = go cs mds+    go (_:*0 :cs)           mds  = go cs mds+    go [        ] [            ] = WithMD               -- all was fine to the very end+    go [        ]              _ = WithoutMD            -- here it wasn't fine++    go (Mat :* n : cs) (MdRep x:mds)   = Rep'   x   $ go     (Mat :* (n-1) : cs)             mds+    go (Mat :* n : cs) (MdNum m:mds)+       | n < m                         = Mat'   n   $ go                     cs (MdNum (m-n):mds)+       | n > m                         = Mat'   m   $ go     (Mat :* (n-m) : cs)             mds+       | otherwise                     = Mat'   n   $ go                     cs              mds+    go (Mat :* n : cs)            _    = Mat'   n   $ go'                    cs++    go (Ins :* n : cs)               mds  = Ins'   n   $ go                  cs              mds+    go (Del :* n : cs) (MdDel (x:xs):mds) = Del'   x   $ go  (Del :* (n-1) : cs) (MdDel xs:mds)+    go (Del :* n : cs)                 _  = Del' nucsN $ go' (Del :* (n-1) : cs)++    go (Nop :* n : cs) mds = Nop' n $ go cs mds+    go (SMa :* n : cs) mds = SMa' n $ go cs mds+    go (HMa :* n : cs) mds = HMa' n $ go cs mds+    go (Pad :* n : cs) mds = Pad' n $ go cs mds++    -- We jump here once the MD fiels ran out early or was messed up.+    -- We no longer bother with it (this also happens if the MD isn't+    -- present to begin with).+    go' (_ :* 0 : cs)   = go' cs+    go' [           ]   = WithoutMD                        -- we didn't have MD or it was broken++    go' (Mat :* n : cs) = Mat'   n   $ go'                 cs+    go' (Ins :* n : cs) = Ins'   n   $ go'                 cs+    go' (Del :* n : cs) = Del' nucsN $ go' (Del :* (n-1) : cs)++    go' (Nop :* n : cs) = Nop'   n   $ go' cs+    go' (SMa :* n : cs) = SMa'   n   $ go' cs+    go' (HMa :* n : cs) = HMa'   n   $ go' cs+    go' (Pad :* n : cs) = Pad'   n   $ go' cs+++-- We normalize matches, deletions and soft masks, because these are the+-- operations we generate.  Everything else is either already normalized+-- or nobody really cares anyway.+toCigar :: ECig -> W.Vector Cigar+toCigar = V.fromList . go+  where+    go       WithMD = []+    go    WithoutMD = []++    go (Ins' n ecs) = Ins :* n : go ecs+    go (Nop' n ecs) = Nop :* n : go ecs+    go (HMa' n ecs) = HMa :* n : go ecs+    go (Pad' n ecs) = Pad :* n : go ecs+    go (SMa' n ecs) = go_sma n ecs+    go (Mat' n ecs) = go_mat n ecs+    go (Rep' _ ecs) = go_mat 1 ecs+    go (Del' _ ecs) = go_del 1 ecs++    go_sma !n (SMa' m ecs) = go_sma (n+m) ecs+    go_sma !n         ecs  = SMa :* n : go ecs++    go_mat !n (Mat' m ecs) = go_mat (n+m) ecs+    go_mat !n (Rep' _ ecs) = go_mat (n+1) ecs+    go_mat !n         ecs  = Mat :* n : go ecs++    go_del !n (Del' _ ecs) = go_del (n+1) ecs+    go_del !n         ecs  = Del :* n : go ecs++++-- | Create an MD field from an extended CIGAR and place it in a record.+-- We build it piecemeal (in 'go'), call out to 'addNum', 'addRep',+-- 'addDel' to make sure the operations are not generated in a+-- degenerate manner, and finally check if we're even supposed to create+-- an MD field.+setMD :: BamRec -> ECig -> BamRec+setMD b ec = case go ec of+    Just md -> b { b_exts = updateE "MD" (Text $ showMd md) (b_exts b) }+    Nothing -> b { b_exts = deleteE "MD"                    (b_exts b) }+  where+    go  WithMD      = Just []+    go  WithoutMD   = Nothing++    go (Ins' _ ecs) = go ecs+    go (Nop' _ ecs) = go ecs+    go (SMa' _ ecs) = go ecs+    go (HMa' _ ecs) = go ecs+    go (Pad' _ ecs) = go ecs+    go (Mat' n ecs) = (if n ==  0 then id else fmap (addNum n)) $ go ecs+    go (Rep' x ecs) = (if isGap x then id else fmap (addRep x)) $ go ecs+    go (Del' x ecs) = (if isGap x then id else fmap (addDel x)) $ go ecs++    addNum n (MdNum m : mds) = MdNum (n+m) : mds+    addNum n            mds  = MdNum   n   : mds++    addRep x            mds  = MdRep   x   : mds++    addDel x (MdDel y : mds) = MdDel (x:y) : mds+    addDel x            mds  = MdDel  [x]  : mds
+ Bio/Bam/Trim.hs view
@@ -0,0 +1,442 @@+-- | Trimming of reads as found in BAM files.  Implements trimming low+-- quality sequence from the 3' end.++module Bio.Bam.Trim+        ( trim_3+        , trim_3'+        , trim_low_quality+        , default_fwd_adapters+        , default_rev_adapters+        , find_merge+        , mergeBam+        , find_trim+        , trimBam+        , mergeTrimBam+        , twoMins+        , merged_seq+        , merged_qual+        ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Bam.Rmdup        ( ECig(..), setMD, toECig )+import Bio.Prelude+import Bio.Streaming+import Foreign.C.Types      ( CInt(..) )++import qualified Data.ByteString                        as B+import qualified Data.Vector.Generic                    as V+import qualified Data.Vector.Storable                   as W++-- | Trims from the 3' end of a sequence.+-- @trim_3' p b@ trims the 3' end of the sequence in @b@ at the+-- earliest position such that @p@ evaluates to true on every suffix+-- that was trimmed off.  Note that the 3' end may be the beginning of+-- the sequence if it happens to be stored in reverse-complemented form.+-- Also note that trimming from the 3' end may not make sense for reads+-- that were constructed by merging paired end data (but we cannot take+-- care of that here).  Further note that trimming may break dependent+-- information, notably the "mate" information of the mate and many+-- optional fields.++trim_3' :: ([Nucleotides] -> [Qual] -> Bool) -> BamRec -> BamRec+trim_3' p b | b_flag b `testBit` 4 = trim_rev+            | otherwise            = trim_fwd+  where+    trim_fwd = let l = subtract 1 . length . takeWhile (uncurry p) $+                            zip (inits . reverse . V.toList $ b_seq b)+                                (inits . reverse . V.toList $ b_qual b)+               in trim_3 l b++    trim_rev = let l = subtract 1 . length . takeWhile (uncurry p) $+                            zip (inits . V.toList $ b_seq  b)+                                (inits . V.toList $ b_qual b)+               in trim_3 l b++trim_3 :: Int -> BamRec -> BamRec+trim_3 l b | b_flag b `testBit` 4 = trim_rev+           | otherwise            = trim_fwd+  where+    trim_fwd = let (_, cigar') = trim_back_cigar (b_cigar b) l+                   c = modMd (takeECig (V.length (b_seq  b) - l)) b+               in c { b_seq   = V.take (V.length (b_seq  c) - l) (b_seq  c)+                    , b_qual  = V.take (V.length (b_qual c) - l) (b_qual c)+                    , b_cigar = cigar'+                    , b_exts  = map (\(k,e) -> case e of+                                        Text t | k `elem` trim_set+                                          -> (k, Text (B.take (B.length t - l) t))+                                        _ -> (k,e)+                                    ) (b_exts c) }++    trim_rev = let (off, cigar') = trim_fwd_cigar (b_cigar b) l+                   c = modMd (dropECig l) b+               in c { b_seq   = V.drop l (b_seq  c)+                    , b_qual  = V.drop l (b_qual c)+                    , b_pos   = b_pos c + off+                    , b_cigar = cigar'+                    , b_exts  = map (\(k,e) -> case e of+                                        Text t | k `elem` trim_set+                                          -> (k, Text (B.drop l t))+                                        _ -> (k,e)+                                    ) (b_exts c) }++    trim_set = ["BQ","CQ","CS","E2","OQ","U2"]++    modMd :: (ECig -> ECig) -> BamRec -> BamRec+    modMd f br = maybe br (setMD br . f . toECig (b_cigar br)) (getMd br)++    endOf :: ECig -> ECig+    endOf  WithMD     = WithMD+    endOf  WithoutMD  = WithoutMD+    endOf (Mat' _ es) = endOf es+    endOf (Ins' _ es) = endOf es+    endOf (SMa' _ es) = endOf es+    endOf (Rep' _ es) = endOf es+    endOf (Del' _ es) = endOf es+    endOf (Nop' _ es) = endOf es+    endOf (HMa' _ es) = endOf es+    endOf (Pad' _ es) = endOf es++    takeECig :: Int -> ECig -> ECig+    takeECig 0  es          = endOf es+    takeECig _  WithMD      = WithMD+    takeECig _  WithoutMD   = WithoutMD+    takeECig n (Mat' m  es) = Mat' n  $ if n > m then takeECig (n-m) es else WithMD+    takeECig n (Ins' m  es) = Ins' n  $ if n > m then takeECig (n-m) es else WithMD+    takeECig n (SMa' m  es) = SMa' n  $ if n > m then takeECig (n-m) es else WithMD+    takeECig n (Rep' ns es) = Rep' ns $ takeECig (n-1) es+    takeECig n (Del' ns es) = Del' ns $ takeECig n es+    takeECig n (Nop' m  es) = Nop' m  $ takeECig n es+    takeECig n (HMa' m  es) = HMa' m  $ takeECig n es+    takeECig n (Pad' m  es) = Pad' m  $ takeECig n es++    dropECig :: Int -> ECig -> ECig+    dropECig 0  es         = es+    dropECig _  WithMD     = WithMD+    dropECig _  WithoutMD  = WithoutMD+    dropECig n (Mat' m es) = if n > m then dropECig (n-m) es else Mat' n WithMD+    dropECig n (Ins' m es) = if n > m then dropECig (n-m) es else Ins' n WithMD+    dropECig n (SMa' m es) = if n > m then dropECig (n-m) es else SMa' n WithMD+    dropECig n (Rep' _ es) = dropECig (n-1) es+    dropECig n (Del' _ es) = dropECig n es+    dropECig n (Nop' _ es) = dropECig n es+    dropECig n (HMa' _ es) = dropECig n es+    dropECig n (Pad' _ es) = dropECig n es+++trim_back_cigar, trim_fwd_cigar :: V.Vector v Cigar => v Cigar -> Int -> ( Int, v Cigar )+trim_back_cigar c l = (o, V.fromList $ reverse c') where (o,c') = sanitize_cigar . trim_cigar l $ reverse $ V.toList c+trim_fwd_cigar  c l = (o, V.fromList           c') where (o,c') = sanitize_cigar $ trim_cigar l $ V.toList c++sanitize_cigar :: (Int, [Cigar]) -> (Int, [Cigar])+sanitize_cigar (o, [        ])                          = (o, [])+sanitize_cigar (o, (op:*l):xs) | op == Pad              = sanitize_cigar (o,xs)         -- del P+                               | op == Del || op == Nop = sanitize_cigar (o + l, xs)    -- adjust D,N+                               | op == Ins              = (o, (SMa :* l):xs)            -- I --> S+                               | otherwise              = (o, (op :* l):xs)             -- rest is fine++trim_cigar :: Int -> [Cigar] -> (Int, [Cigar])+trim_cigar 0 cs = (0, cs)+trim_cigar _ [] = (0, [])+trim_cigar l ((op:*ll):cs) | bad_op op = let (o,cs') = trim_cigar l cs in (o + reflen op ll, cs')+                           | otherwise = case l `compare` ll of+    LT -> (reflen op  l, (op :* (ll-l)):cs)+    EQ -> (reflen op ll,                cs)+    GT -> let (o,cs') = trim_cigar (l - ll) cs in (o + reflen op ll, cs')++  where+    reflen op' = if ref_op op' then id else const 0+    bad_op o = o /= Mat && o /= Ins && o /= SMa+    ref_op o = o == Mat || o == Del+++-- | Trim predicate to get rid of low quality sequence.+-- @trim_low_quality q ns qs@ evaluates to true if all qualities in @qs@+-- are smaller (i.e. worse) than @q@.+trim_low_quality :: Qual -> a -> [Qual] -> Bool+trim_low_quality q = const $ all (< q)+++-- | Finds the merge point.  Input is list of forward adapters, list of+-- reverse adapters, sequence1, quality1, sequence2, quality2; output is+-- merge point and two qualities (YM, YN).+find_merge :: [W.Vector Nucleotides] -> [W.Vector Nucleotides]+           -> W.Vector Nucleotides -> W.Vector Qual+           -> W.Vector Nucleotides -> W.Vector Qual+           -> (Int, Int, Int)+find_merge ads1 ads2 r1 q1 r2 q2 = (mlen, score2 - score1, plain_score - score1)+  where+    plain_score = 6 * (V.length r1 + V.length r2)+    (score1, mlen, score2) = twoMins plain_score (V.length r1 + V.length r2) $+                             merge_score ads1 ads2 r1 q1 r2 q2++-- | Overlap-merging of read pairs.  We shall compute the likelihood+-- for every possible overlap, then select the most likely one (unless it+-- looks completely random), compute a quality from the second best+-- merge, then merge and clamp the quality accordingly.+-- (We could try looking for chimaera after completing the merge, if+-- only we knew which ones to expect?)+--+-- Two reads go in, with two adapter lists.  We return 'Nothing' if all+-- merges looked mostly random.  Else we return the two original reads,+-- flagged as 'eflagVestigial' *and* the merged version, flagged as+-- 'eflagMerged' and optionally 'eflagTrimmed'.  All reads contain the+-- computed qualities (in YM and YN), which we also return.+--+-- The merging automatically limits quality scores some of the time.  We+-- additionally impose a hard limit of 63 to avoid difficulties+-- representing the result, and even that is ridiculous.  Sane people+-- would further limit the returned quality!  (In practice, map quality+-- later imposes a limit anyway, so no worries...)++mergeBam :: Int -> Int -> [W.Vector Nucleotides] -> [W.Vector Nucleotides] -> BamRec -> BamRec -> [BamRec]+mergeBam lowq highq ads1 ads2 r1 r2+    | V.null (b_seq r1) && V.null (b_seq r2) = [              ]+    | qual1 < lowq || mlen < 0               = [ r1', r2'     ]+    | qual1 >= highq && mlen == 0            = [              ]+    | qual1 >= highq                         = [           rm ]+    | mlen < len_r1-20 || mlen < len_r2-20   = [           rm ]+    | otherwise         = map flag_alternative [ r1', r2', rm ]+  where+    len_r1    = V.length  $ b_seq  r1+    len_r2    = V.length  $ b_seq  r2++    b_seq_r1  = V.convert $ b_seq  r1+    b_seq_r2  = V.convert $ b_seq  r2+    b_qual_r1 = V.convert $ b_qual r1+    b_qual_r2 = V.convert $ b_qual r2++    (mlen, qual1, qual2) = find_merge ads1 ads2 b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2++    flag_alternative br = br { b_exts = updateE "FF" (Int $ extAsInt 0 "FF" br .|. eflagAlternative) $ b_exts br }+    store_quals      br = br { b_exts = updateE "YM" (Int qual1) $ updateE "YN" (Int qual2) $ b_exts br }+    pair_flags = flagPaired.|.flagProperlyPaired.|.flagMateUnmapped.|.flagMateReversed.|.flagFirstMate.|.flagSecondMate++    r1' = store_quals r1+    r2' = store_quals r2+    rm  = store_quals $ merged_read mlen (fromIntegral $ min 63 qual1)++    merged_read l qmax = nullBamRec {+                b_qname = b_qname r1,+                b_flag  = flagUnmapped .|. complement pair_flags .&. b_flag r1,+                b_seq   = V.convert $ merged_seq l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2,+                b_qual  = V.convert $ merged_qual qmax l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2,+                b_exts  = let ff = if l < len_r1 then eflagTrimmed else 0+                          in updateE "FF" (Int $ extAsInt 0 "FF" r1 .|. eflagMerged .|. ff) $ b_exts r1 }++{-# INLINE merged_seq #-}+merged_seq :: (V.Vector v Nucleotides, V.Vector v Qual)+           => Int -> v Nucleotides -> v Qual -> v Nucleotides -> v Qual -> v Nucleotides+merged_seq l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2 = V.concat+        [ V.take (l - len_r2) b_seq_r1+        , V.zipWith4 zz          (V.take l $ V.drop (l - len_r2) b_seq_r1)+                                 (V.take l $ V.drop (l - len_r2) b_qual_r1)+                     (V.reverse $ V.take l $ V.drop (l - len_r1) b_seq_r2)+                     (V.reverse $ V.take l $ V.drop (l - len_r1) b_qual_r2)+        , V.reverse $ V.take (l - len_r1) b_seq_r2 ]+  where+    len_r1 = V.length b_qual_r1+    len_r2 = V.length b_qual_r2+    zz !n1 (Q !q1) !n2 (Q !q2) | n1 == compls n2 =        n1+                               | q1 > q2         =        n1+                               | otherwise       = compls n2++{-# INLINE merged_qual #-}+merged_qual :: (V.Vector v Nucleotides, V.Vector v Qual)+            => Word8 -> Int -> v Nucleotides -> v Qual -> v Nucleotides -> v Qual -> v Qual+merged_qual qmax l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2 = V.concat+        [ V.take (l - len_r2) b_qual_r1+        , V.zipWith4 zz           (V.take l $ V.drop (l - len_r2) b_seq_r1)+                                  (V.take l $ V.drop (l - len_r2) b_qual_r1)+                      (V.reverse $ V.take l $ V.drop (l - len_r1) b_seq_r2)+                      (V.reverse $ V.take l $ V.drop (l - len_r1) b_qual_r2)+        , V.reverse $ V.take (l - len_r1) b_qual_r2 ]+  where+    len_r1 = V.length b_qual_r1+    len_r2 = V.length b_qual_r2+    zz !n1 (Q !q1) !n2 (Q !q2) | n1 == compls n2 = Q $ min qmax (q1 + q2)+                               | q1 > q2         = Q $           q1 - q2+                               | otherwise       = Q $           q2 - q1++++-- | Finds the trimming point.  Input is list of forward adapters,+-- sequence, quality; output is trim point and two qualities (YM, YN).+find_trim :: [W.Vector Nucleotides]+          -> W.Vector Nucleotides -> W.Vector Qual+          -> (Int, Int, Int)+find_trim ads1 r1 q1 = (mlen, score2 - score1, plain_score - score1)+  where+    plain_score = 6 * V.length r1+    (score1, mlen, score2) = twoMins plain_score (V.length r1) $+                             merge_score ads1 [V.empty] r1 q1 V.empty V.empty++-- | Trimming for a single read:  we need one adapter only (the one coming+-- /after/ the read), here provided as a list of options, and then we+-- merge with an empty second read.  Results in up to two reads (the+-- original, possibly flagged, and the trimmed one, definitely flagged,+-- and two qualities).+trimBam :: Int -> Int -> [W.Vector Nucleotides] -> BamRec -> [BamRec]+trimBam lowq highq ads1 r1+    | V.null (b_seq r1)              = [          ]+    | mlen == 0 && qual1 >= highq    = [          ]+    | qual1 < lowq || mlen < 0       = [ r1'      ]+    | qual1 >= highq                 = [      r1t ]+    | otherwise = map flag_alternative [ r1', r1t ]+  where+    -- the "merge" score if there is no trimming++    b_seq_r1 = V.convert $ b_seq r1+    b_qual_r1 = V.convert $ b_qual r1++    (mlen, qual1, qual2) = find_trim ads1 b_seq_r1 b_qual_r1++    flag_alternative br = br { b_exts = updateE "FF" (Int $ extAsInt 0 "FF" br .|. eflagAlternative) $ b_exts br }+    store_quals      br = br { b_exts = updateE "YM" (Int qual1) $ updateE "YN" (Int qual2) $ b_exts br }++    r1'  = store_quals r1+    r1t  = store_quals $ trimmed_read mlen++    trimmed_read l = nullBamRec {+            b_qname = b_qname r1,+            b_flag  = flagUnmapped .|. b_flag r1,+            b_seq   = V.take l $ b_seq  r1,+            b_qual  = V.take l $ b_qual r1,+            b_exts  = updateE "FF" (Int $ extAsInt 0 "FF" r1 .|. eflagTrimmed) $ b_exts r1 }+++-- | For merging, we don't need the complete adapters (length around 70!),+-- only a sufficient prefix.  Taking only the more-or-less constant+-- part (length around 30), there aren't all that many different+-- adapters in the world.  To deal with pretty much every library, we+-- only need the following forward adapters, which will be the default+-- (defined here in the direction they would be sequenced in):  Genomic+-- R2, Multiplex R2, Fraft P7.++default_fwd_adapters :: [ W.Vector Nucleotides ]+default_fwd_adapters = map (W.fromList. map toNucleotides)+         [ {- Genomic R2   -}  "AGATCGGAAGAGCGGTTCAG"+         , {- Multiplex R2 -}  "AGATCGGAAGAGCACACGTC"+         , {- Graft P7     -}  "AGATCGGAAGAGCTCGTATG" ]++-- | Like 'default_rev_adapters', these are the few adapters needed for+-- the reverse read (defined in the direction they would be sequenced in+-- as part of the second read):  Genomic R1, CL 72.++default_rev_adapters :: [ W.Vector Nucleotides ]+default_rev_adapters = map (W.fromList. map toNucleotides)+         [ {- Genomic_R1   -}  "AGATCGGAAGAGCGTCGTGT"+         , {- CL72         -}  "GGAAGAGCGTCGTGTAGGGA" ]++-- We need to compute the likelihood of a read pair given an assumed+-- insert length.  The likelihood of the first read is the likelihood of+-- a match with the adapter where it overlaps the 3' adapter, elsewhere+-- it's 1/4 per position.  The likelihood of the second read is the+-- likelihood of a match with the adapter where it overlaps the adapter,+-- the likehood of a read-read match where it overlaps read one, 1/4 per+-- position elsewhere.  (Yes, this ignores base composition.  It doesn't+-- matter enough.)++merge_score+    :: [ W.Vector Nucleotides ]                 -- 3' adapters as they appear in the first read+    -> [ W.Vector Nucleotides ]                 -- 5' adapters as they appear in the second read+    -> W.Vector Nucleotides -> W.Vector Qual    -- first read, qual+    -> W.Vector Nucleotides -> W.Vector Qual    -- second read, qual+    -> Int                                      -- assumed insert length+    -> Int                                      -- score (roughly sum of qualities at mismatches)+merge_score fwd_adapters rev_adapters !read1 !qual1 !read2 !qual2 !l+    =   6 * (l `min` V.length read1)                                        -- read1, part before adapter+      + 6 * (max 0 (l - V.length read1))                                    -- read2, part before overlap++      + foldl' (\acc fwd_ad -> min acc+                    (match_adapter l read1 qual1 fwd_ad +                   -- read1, match with forward adapter+                     6 * (max 0 (V.length read1 - V.length fwd_ad - l)))    -- read1, part after (known) adapter+               ) maxBound fwd_adapters++      + foldl' (\acc rev_ad -> min acc+                    (match_adapter l read2 qual2 rev_ad +                   -- read2, match with reverse adapter+                     6 * (max 0 (V.length read2 - V.length rev_ad - l)))    -- read2, part after (known) adapter+               ) maxBound rev_adapters++      + match_reads l read1 qual1 read2 qual2++{-# INLINE match_adapter #-}+match_adapter :: Int -> W.Vector Nucleotides -> W.Vector Qual -> W.Vector Nucleotides -> Int+match_adapter !off !rd !qs !ad+    | V.length rd /= V.length qs = error "read/qual length mismatch"+    | efflength <= 0             = 0+    | otherwise+        = fromIntegral . unsafePerformIO $+          W.unsafeWith rd $ \p_rd ->+          W.unsafeWith qs $ \p_qs ->+          W.unsafeWith ad $ \p_ad ->+          prim_match_ad (fromIntegral off)+                        (fromIntegral efflength)+                        p_rd p_qs p_ad+  where+    !efflength =  (V.length rd - off) `min` V.length ad++foreign import ccall unsafe "prim_match_ad"+    prim_match_ad :: CInt -> CInt+                  -> Ptr Nucleotides -> Ptr Qual+                  -> Ptr Nucleotides -> IO CInt+++-- | Computes overlap score for two reads (with qualities) assuming an+-- insert length.+{-# INLINE match_reads #-}+match_reads :: Int -> W.Vector Nucleotides -> W.Vector Qual -> W.Vector Nucleotides -> W.Vector Qual -> Int+match_reads !l !rd1 !qs1 !rd2 !qs2+    | V.length rd1 /= V.length qs1 || V.length rd2 /= V.length qs2 = error "read/qual length mismatch"+    | efflength <= 0                                               = 0+    | otherwise+        = fromIntegral . unsafePerformIO $+          W.unsafeWith rd1 $ \p_rd1 ->+          W.unsafeWith qs1 $ \p_qs1 ->+          W.unsafeWith rd2 $ \p_rd2 ->+          W.unsafeWith qs2 $ \p_qs2 ->+          prim_match_reads (fromIntegral minidx1)+                           (fromIntegral maxidx2)+                           (fromIntegral efflength)+                           p_rd1 p_qs1 p_rd2 p_qs2+  where+    -- vec1, forward+    !minidx1 = (l - V.length rd2) `max` 0+    -- vec2, backward+    !maxidx2 = l `min` V.length rd2+    -- effective length+    !efflength = ((V.length rd1 + V.length rd2 - l) `min` l) `max` 0+++foreign import ccall unsafe "prim_match_reads"+    prim_match_reads :: CInt -> CInt -> CInt+                     -> Ptr Nucleotides -> Ptr Qual+                     -> Ptr Nucleotides -> Ptr Qual -> IO CInt+++{-# INLINE twoMins #-}+twoMins :: (Bounded a, Ord a) => a -> Int -> (Int -> a) -> (a,Int,a)+twoMins a0 imax f = go a0 (-1) maxBound 0 0+  where+    go !m1 !i1 !m2 !i2 !i+        | i == imax = (m1,i1,m2)+        | otherwise =+            case f i of+                x | x < m1    -> go  x  i m1 i1 (i+1)+                  | x < m2    -> go m1 i1  x  i (i+1)+                  | otherwise -> go m1 i1 m2 i2 (i+1)+++mergeTrimBam :: Monad m+             => Int -> Int -> [W.Vector Nucleotides] -> [W.Vector Nucleotides]+             -> Stream (Of BamRec) m r -> Stream (Of BamRec) m r+mergeTrimBam lowq highq fwd_ads rev_ads = go+  where+    go = lift . inspect >=> either pure go1++    go1 (r1 :> s) | isPaired r1 = lift (inspect s) >>= go2 r1+                  | otherwise   = each (trimBam lowq highq fwd_ads r1) >> go s++    go2 r1 (Left          _) = error $ "Lone mate found: " ++ show (b_qname r1)+    go2 r1 (Right (r2 :> s)) = each (mergeBam lowq highq fwd_ads rev_ads r1 r2) >> go s+
+ Bio/Bam/Writer.hs view
@@ -0,0 +1,247 @@+-- | Printers for BAM and SAM.  BAM is properly supported, SAM can be+-- piped to standard output.++module Bio.Bam.Writer (+    IsBamRec(..),+    encodeBamWith,++    packBam,+    writeBamFile,+    writeBamHandle,+    pipeBamOutput,+    pipeSamOutput+                      ) where++import Bio.Bam.Header+import Bio.Bam.Rec+import Bio.Prelude+import Bio.Streaming+import Bio.Streaming.Bgzf++import Data.ByteString.Builder.Prim ( (>*<) )+import Data.ByteString.Internal     ( ByteString(..) )+import Data.ByteString.Lazy         ( foldrChunks )+import Foreign.Marshal.Alloc        ( alloca )++import qualified Bio.Streaming.Bytes                as S+import qualified Data.ByteString                    as B+import qualified Data.ByteString.Builder            as B+import qualified Data.ByteString.Builder.Extra      as B+import qualified Data.ByteString.Builder.Prim       as E+import qualified Data.Vector.Generic                as V+import qualified Data.Vector.Storable               as W+import qualified Data.Vector.Unboxed                as U+import qualified Streaming.Prelude                  as Q++{- | write in SAM format to stdout++This is useful for piping to other tools (say, AWK scripts) or for+debugging.  No convenience functions to send SAM to a file or to+compress it exist, because these are stupid ideas.+-}+pipeSamOutput :: (IsBamRec a, MonadIO m) => BamMeta -> Stream (Of a) m r -> m r+pipeSamOutput meta s = do+    liftIO . B.hPutBuilder stdout $ showBamMeta meta+    Q.mapM_ (liftIO . B.hPutBuilder stdout . encodeSamEntry (meta_refs meta) . unpackBamRec) s+{-# INLINE pipeSamOutput #-}++encodeSamEntry :: Refs -> BamRec -> B.Builder+encodeSamEntry refs b =+    B.byteStringCopy (b_qname b)                         <> B.char7 '\t' <>+    B.intDec         (b_flag b .&. 0xffff)               <> B.char7 '\t' <>+    B.byteStringCopy (sq_name $ getRef refs $ b_rname b) <> B.char7 '\t' <>+    B.intDec         (b_pos b + 1)                       <> B.char7 '\t' <>+    B.word8Dec       (unQ $ b_mapq b)                    <> B.char7 '\t' <>+    buildCigar       (b_cigar b)                         <> B.char7 '\t' <>+    buildMrnm        (b_mrnm b) (b_rname b)              <> B.char7 '\t' <>+    B.intDec         (b_mpos b + 1)                      <> B.char7 '\t' <>+    B.intDec         (b_isize b)                         <> B.char7 '\t' <>+    buildSeq         (b_seq b)                           <> B.char7 '\t' <>+    buildQual        (b_qual b)                          <>+    foldMap buildExt (b_exts b)                          <> B.char7 '\n'+  where+    buildCigar = E.primUnfoldrBounded+                    (E.intDec >*< E.liftFixedToBounded E.word8)+                    (vuncons $ \(op :* num) -> (num, B.index "MIDNSHP" (fromEnum op)))++    buildMrnm mrnm rname+        | isValidRefseq mrnm && mrnm == rname  =  B.char7 '='+        | otherwise                            =  B.byteString (sq_name $ getRef refs mrnm)++    buildSeq  = E.primUnfoldrFixed E.word8 (vuncons $ \(Ns x) -> B.index "-ACMGRSVTWYHKDBN" $ fromIntegral $ x .&. 15)+    buildQual = E.primUnfoldrFixed E.word8 (vuncons $ \(Q  q) -> q + 33)++    buildExt (BamKey k,v) = B.char7 '\t' <>+                            B.word8 (fromIntegral k .&. 0xff) <>+                            B.word8 (shiftR (fromIntegral k) 8 .&. 0xff) <>+                            B.char7 ':' <>+                            buildExtVal v++    buildExtVal (Int      i) = B.char7 'i' <> B.char7 ':' <> B.intDec i+    buildExtVal (Float    f) = B.char7 'f' <> B.char7 ':' <> B.floatDec f+    buildExtVal (Text     t) = B.char7 'Z' <> B.char7 ':' <> B.byteStringCopy t+    buildExtVal (Bin      x) = B.char7 'H' <> B.char7 ':' <> B.byteStringHex x+    buildExtVal (Char     c) = B.char7 'A' <> B.char7 ':' <> B.word8 c+    buildExtVal (IntArr   a) = B.char7 'B' <> B.char7 ':' <> B.char7 'i' <> buildArr   B.intDec a+    buildExtVal (FloatArr a) = B.char7 'B' <> B.char7 ':' <> B.char7 'f' <> buildArr B.floatDec a++    buildArr p = U.foldr (\x k -> B.char7 ',' <> p x <> k) mempty++    vuncons f v | V.null  v = Nothing+                | otherwise = Just (f (V.unsafeHead v), V.unsafeTail v)+++class IsBamRec a where+    pushBam :: a -> BgzfTokens -> BgzfTokens+    unpackBamRec :: a -> BamRec++instance IsBamRec BamRaw where+    {-# INLINE pushBam #-}+    pushBam = pushBamRaw+    {-# INLINE unpackBamRec #-}+    unpackBamRec = unpackBam++instance IsBamRec BamRec where+    {-# INLINE pushBam #-}+    pushBam = pushBamRec+    {-# INLINE unpackBamRec #-}+    unpackBamRec = id++instance (IsBamRec a, IsBamRec b) => IsBamRec (Either a b) where+    {-# INLINE pushBam #-}+    pushBam = either pushBam pushBam+    {-# INLINE unpackBamRec #-}+    unpackBamRec = either unpackBamRec unpackBamRec++-- | Encodes BAM records straight into a dynamic buffer, then BGZF's it.+-- Should be fairly direct and perform well.+encodeBamWith :: (IsBamRec a, MonadIO m) => Int -> BamMeta -> Stream (Of a) m r -> ByteStream m r+encodeBamWith lv meta = encodeBgzf lv . enc_bam+  where+    enc_bam bs = Q.cons pushHeader $ Q.map (Endo . pushBam) bs++    pushHeader :: Endo BgzfTokens+    pushHeader = Endo $ TkString "BAM\1"+                      . TkSetMark                        -- the length byte+                      . pushBuilder (showBamMeta meta)+                      . TkEndRecord                      -- fills the length in+                      . TkWord32 (fromIntegral . V.length . unRefs $ meta_refs meta)+                      . appEndo (foldMap (Endo . pushRef) (unRefs $ meta_refs meta))++    pushRef :: BamSQ -> BgzfTokens -> BgzfTokens+    pushRef bs = TkWord32 (fromIntegral $ B.length (sq_name bs) + 1)+               . TkString (sq_name bs)+               . TkWord8 0+               . TkWord32 (fromIntegral $ sq_length bs)++    pushBuilder :: B.Builder -> BgzfTokens -> BgzfTokens+    pushBuilder b tk = foldrChunks TkString tk (B.toLazyByteString b)+{-# INLINE encodeBamWith #-}++pushBamRaw :: BamRaw -> BgzfTokens -> BgzfTokens+pushBamRaw r = TkWord32 (fromIntegral $ B.length $ raw_data r) .+               TkString (raw_data r)+{-# INLINE pushBamRaw #-}++-- | Writes BAM encoded stuff to a file.+-- In reality, it cleverly writes to a temporary file and renames it+-- when done.+writeBamFile :: (IsBamRec a, MonadIO m, MonadMask m) => FilePath -> BamMeta -> Stream (Of a) m r -> m r+writeBamFile fp meta = S.writeFile fp . encodeBamWith 6 meta++-- | Write BAM encoded stuff to stdout.+-- This sends uncompressed(!) BAM to stdout.  Useful for piping to other+-- tools.  The output is still wrapped in a BGZF stream, because that's+-- what all tools expect; but the individuals blocks are not compressed.+pipeBamOutput :: (IsBamRec a, MonadIO m) => BamMeta -> Stream (Of a) m r -> m r+pipeBamOutput meta = S.hPut stdout . encodeBamWith 0 meta+{-# INLINE pipeBamOutput #-}++-- | Writes BAM encoded stuff to a 'Handle'.+writeBamHandle :: (IsBamRec a, MonadIO m) => Handle -> BamMeta -> Stream (Of a) m r -> m r+writeBamHandle hdl meta = S.hPut hdl . encodeBamWith 6 meta++{-# RULES+    "pushBam/unpackBam"     forall b . pushBamRec (unpackBam b) = pushBamRaw b+  #-}++{-# INLINE[1] pushBamRec #-}+pushBamRec :: BamRec -> BgzfTokens -> BgzfTokens+pushBamRec BamRec{..} =+      TkSetMark+    . TkWord32 (unRefseq b_rname)+    . TkWord32 (fromIntegral b_pos)+    . TkWord8  (fromIntegral $ B.length b_qname + 1)+    . TkWord8  (unQ b_mapq)+    . TkWord16 (fromIntegral bin)+    . TkWord16 (fromIntegral $ W.length b_cigar)+    . TkWord16 (fromIntegral b_flag)+    . TkWord32 (fromIntegral $ V.length b_seq)+    . TkWord32 (unRefseq b_mrnm)+    . TkWord32 (fromIntegral b_mpos)+    . TkWord32 (fromIntegral b_isize)+    . TkString b_qname+    . TkWord8 0+    . W.foldr ((.) . TkWord8) id (W.unsafeCast b_cigar :: W.Vector Word8)+    . pushSeq b_seq+    . W.foldr ((.) . TkWord8 . unQ) id b_qual+    . foldr ((.) . pushExt) id b_exts+    . TkEndRecord+  where+    bin = distinctBin b_pos (alignedLength b_cigar)++    pushSeq :: V.Vector vec Nucleotides => vec Nucleotides -> BgzfTokens -> BgzfTokens+    pushSeq v = case v V.!? 0 of+                    Nothing -> id+                    Just a  -> case v V.!? 1 of+                        Nothing -> TkWord8 (unNs a `shiftL` 4)+                        Just b  -> TkWord8 (unNs a `shiftL` 4 .|. unNs b) . pushSeq (V.drop 2 v)++    pushExt :: (BamKey, Ext) -> BgzfTokens -> BgzfTokens+    pushExt (BamKey k, e) = case e of+        Text  t -> common 'Z' . TkString t . TkWord8 0+        Bin   t -> common 'H' . TkString t . TkWord8 0+        Char  c -> common 'A' . TkWord8 c+        Float f -> common 'f' . TkWord32 (fromFloat f)++        Int i   -> case put_some_int (U.singleton i) of+                        (c,op) -> common c . op i++        IntArr  ia -> case put_some_int ia of+                        (c,op) -> common 'B' . TkWord8 (fromIntegral $ ord c)+                                  . TkWord32 (fromIntegral $ U.length ia-1)+                                  . U.foldr ((.) . op) id ia++        FloatArr fa -> common 'B' . TkWord8 (fromIntegral $ ord 'f')+                       . TkWord32 (fromIntegral $ U.length fa-1)+                       . U.foldr ((.) . TkWord32 . fromFloat) id fa+      where+        common :: Char -> BgzfTokens -> BgzfTokens+        common z = TkWord16 k . TkWord8 (fromIntegral $ ord z)++        put_some_int :: U.Vector Int -> (Char, Int -> BgzfTokens -> BgzfTokens)+        put_some_int is+            | U.all (between        0    0xff) is = ('C', TkWord8  . fromIntegral)+            | U.all (between   (-0x80)   0x7f) is = ('c', TkWord8  . fromIntegral)+            | U.all (between        0  0xffff) is = ('S', TkWord16 . fromIntegral)+            | U.all (between (-0x8000) 0x7fff) is = ('s', TkWord16 . fromIntegral)+            | U.all                      (> 0) is = ('I', TkWord32 . fromIntegral)+            | otherwise                           = ('i', TkWord32 . fromIntegral)++        between :: Int -> Int -> Int -> Bool+        between l r x = l <= x && x <= r++        fromFloat :: Float -> Word32+        fromFloat float = unsafeDupablePerformIO $ alloca $ \buf ->+                          pokeByteOff buf 0 float >> peek buf++packBam :: BamRec -> IO BamRaw+packBam br = do bb <- newBuffer 1000+                (bb', TkEnd) <- store_loop bb (pushBamRec br TkEnd)+                return . bamRaw 0 $ PS (buffer bb') 4 (used bb' - 4)+  where+    store_loop bb tk = do (bb',tk') <- fillBuffer bb tk+                          case tk' of TkEnd -> return (bb',tk')+                                      _     -> do bb'' <- expandBuffer (128*1024) bb'+                                                  store_loop bb'' tk'+
+ Bio/Base.hs view
@@ -0,0 +1,339 @@+{-# LANGUAGE CPP #-}+-- | Common data types used everywhere.  This module is a collection of+-- very basic "bioinformatics" data types that are simple, but don't+-- make sense to define over and over.++module Bio.Base(+    Nucleotide(..), Nucleotides(..),+    Qual(..), toQual, fromQual, fromQualRaised, probToQual,+    Prob'(..), Prob, toProb, fromProb, qualToProb, pow,++    Pair(..),+    Word8,+    nucA, nucC, nucG, nucT,+    nucsA, nucsC, nucsG, nucsT, nucsN, gap,+    toNucleotide, toNucleotides, nucToNucs,+    showNucleotide, showNucleotides,+    isGap,+    isBase,+    isProperBase,+    properBases,+    compl, compls,++    Position(..),+    shiftPosition,+    p_is_reverse,++    Range(..),+    shiftRange,+    reverseRange,+    extendRange,+    insideRange,+    wrapRange+) where++import BasePrelude+#if MIN_VERSION_base(4,9,0)+                             hiding ( log1pexp, log1mexp )+#endif+import Bio.Util.Numeric             ( log1pexp, log1mexp )++import qualified Data.ByteString.Char8 as C+import qualified Data.Vector.Unboxed   as U++-- | A nucleotide base.  We only represent A,C,G,T.  The contained+-- 'Word8' ist guaranteed to be 0..3.+newtype Nucleotide = N { unN :: Word8 } deriving ( Eq, Ord, Enum, Ix, Storable )++instance Bounded Nucleotide where+    minBound = N 0+    maxBound = N 3++-- | A nucleotide base in an alignment.+-- Experience says we're dealing with Ns and gaps all the type, so+-- purity be damned, they are included as if they were real bases.+--+-- To allow @Nucleotides@s to be unpacked and incorporated into+-- containers, we choose to represent them the same way as the BAM file+-- format:  as a 4 bit wide field.  Gaps are encoded as 0 where they+-- make sense, N is 15.  The contained 'Word8' is guaranteed to be+-- 0..15.++newtype Nucleotides = Ns { unNs :: Word8 } deriving ( Eq, Ord, Enum, Ix, Storable )++instance Bounded Nucleotides where+    minBound = Ns  0+    maxBound = Ns 15++nucToNucs :: Nucleotide -> Nucleotides+nucToNucs (N x) = Ns $ 1 `shiftL` fromIntegral (x .&. 3)++-- | Qualities are stored in deciban, also known as the Phred scale.  To+-- represent a value @p@, we store @-10 * log_10 p@.  Operations work+-- directly on the \"Phred\" value, as the name suggests.  The same goes+-- for the 'Ord' instance:  greater quality means higher \"Phred\"+-- score, meand lower error probability.++newtype Qual = Q { unQ :: Word8 } deriving ( Eq, Ord, Storable, Bounded )++instance Show Qual where+    showsPrec p (Q q) = (:) 'q' . showsPrec p q++toQual :: (Floating a, RealFrac a) => a -> Qual+toQual a = Q $ round (-10 * log a / log 10)++fromQual :: Qual -> Double+fromQual (Q q) = 10 ** (- fromIntegral q / 10)++fromQualRaised :: Double -> Qual -> Double+fromQualRaised k (Q q) = 10 ** (- k * fromIntegral q / 10)++-- | A positive floating point value stored in log domain.  We store the+-- natural logarithm (makes computation easier), but allow conversions+-- to the familiar \"Phred\" scale used for 'Qual' values.+newtype Prob' a = Pr { unPr :: a } deriving ( Eq, Ord, Storable )++-- | Common way of using 'Prob''.+type Prob = Prob' Double++instance RealFloat a => Show (Prob' a) where+    showsPrec _ (Pr p) = (:) 'q' . showFFloat (Just 1) q+      where q = - 10 * p / log 10++instance (Floating a, Ord a) => Num (Prob' a) where+    {-# INLINE fromInteger #-}+    fromInteger a = Pr (log (fromInteger a))+    {-# INLINE (+) #-}+    Pr x + Pr y = Pr $ if x >= y then x + log1pexp (y-x) else y + log1pexp (x-y)+    {-# INLINE (-) #-}+    Pr x - Pr y = Pr $ if x >= y then x + log1mexp (y-x) else error "no negative error probabilities"+    {-# INLINE (*) #-}+    Pr a * Pr b = Pr $ a + b+    {-# INLINE negate #-}+    negate    _ = Pr $ error "no negative error probabilities"+    {-# INLINE abs #-}+    abs       x = x+    {-# INLINE signum #-}+    signum    _ = Pr 0++instance (Floating a, Fractional a, Ord a) => Fractional (Prob' a) where+    fromRational a = Pr (log (fromRational a))+    Pr a  /  Pr b = Pr (a - b)+    recip  (Pr a) = Pr (negate a)++infixr 8 `pow`+pow :: Num a => Prob' a -> a -> Prob' a+pow (Pr a) e = Pr $ a * e+++toProb :: Floating a => a -> Prob' a+toProb p = Pr (log p)++fromProb :: Floating a => Prob' a -> a+fromProb (Pr q) = exp q++qualToProb :: Floating a => Qual -> Prob' a+qualToProb (Q q) = Pr (- log 10 * fromIntegral q / 10)++probToQual :: (Floating a, RealFrac a) => Prob' a -> Qual+probToQual (Pr p) = Q (round (- 10 * p / log 10))++nucA, nucC, nucG, nucT :: Nucleotide+nucA = N 0+nucC = N 1+nucG = N 2+nucT = N 3++gap, nucsA, nucsC, nucsG, nucsT, nucsN :: Nucleotides+gap   = Ns 0+nucsA = Ns 1+nucsC = Ns 2+nucsG = Ns 4+nucsT = Ns 8+nucsN = Ns 15+++-- | Coordinates in a genome.+-- The position is zero-based, no questions about it.  Think of the+-- position as pointing to the crack between two bases: looking forward+-- you see the next base to the right, looking in the reverse direction+-- you see the complement of the first base to the left.+--+-- To encode the strand, we (virtually) reverse-complement any sequence+-- and prepend it to the normal one.  That way, reversed coordinates+-- have a negative sign and automatically make sense.  Position 0 could+-- either be the beginning of the sequence or the end on the reverse+-- strand... that ambiguity shouldn't really matter.++data Position = Pos {+        p_seq   :: {-# UNPACK #-} !C.ByteString,    -- ^ sequence (e.g. some chromosome)+        p_start :: {-# UNPACK #-} !Int              -- ^ offset, zero-based+    } deriving (Show, Eq, Ord)++p_is_reverse :: Position -> Bool+p_is_reverse = (< 0) . p_start++-- | Ranges in genomes+-- We combine a position with a length.  In 'Range pos len', 'pos' is+-- always the start of a stretch of length 'len'.  Positions therefore+-- move in the opposite direction on the reverse strand.  To get the+-- same stretch on the reverse strand, shift r_pos by r_length, then+-- reverse direction (or call reverseRange).+data Range = Range {+        r_pos    :: {-# UNPACK #-} !Position,+        r_length :: {-# UNPACK #-} !Int+    } deriving (Show, Eq, Ord)+++-- | Converts a character into a 'Nucleotides'.+-- The usual codes for A,C,G,T and U are understood, '-' and '.' become+-- gaps and everything else is an N.+toNucleotide :: Char -> Nucleotide+toNucleotide c = if ord c < 128 then N (ar `U.unsafeIndex` ord c) else N 0+  where+    ar = U.replicate 128 0 U.//+          ( [ (ord          x,  n) | (x, N n) <- pairs ] +++            [ (ord (toUpper x), n) | (x, N n) <- pairs ] )++    pairs = [ ('a', nucA), ('c', nucC), ('g', nucG), ('t', nucT) ]+++-- | Converts a character into a 'Nucleotides'.+-- The usual codes for A,C,G,T and U are understood, '-' and '.' become+-- gaps and everything else is an N.+toNucleotides :: Char -> Nucleotides+toNucleotides c = if ord c < 128 then Ns (ar `U.unsafeIndex` ord c) else nucsN+  where+    ar = U.replicate 128 (unNs nucsN) U.//+          ( [ (ord          x,  n) | (x, Ns n) <- pairs ] +++            [ (ord (toUpper x), n) | (x, Ns n) <- pairs ] )++    Ns a `plus` Ns b = Ns (a .|. b)++    pairs = [ ('a', nucsA), ('c', nucsC), ('g', nucsG), ('t', nucsT),+              ('u', nucsT), ('-', gap),  ('.', gap),+              ('b', nucsC `plus` nucsG `plus` nucsT),+              ('d', nucsA `plus` nucsG `plus` nucsT),+              ('h', nucsA `plus` nucsC `plus` nucsT),+              ('v', nucsA `plus` nucsC `plus` nucsG),+              ('k', nucsG `plus` nucsT),+              ('m', nucsA `plus` nucsC),+              ('s', nucsC `plus` nucsG),+              ('w', nucsA `plus` nucsT),+              ('r', nucsA `plus` nucsG),+              ('y', nucsC `plus` nucsT) ]++-- | Tests if a 'Nucleotides' is a base.+-- Returns 'True' for everything but gaps.+isBase :: Nucleotides -> Bool+isBase (Ns n) = n /= 0++-- | Tests if a 'Nucleotides' is a proper base.+-- Returns 'True' for A,C,G,T only.+isProperBase :: Nucleotides -> Bool+isProperBase x = x == nucsA || x == nucsC || x == nucsG || x == nucsT++properBases :: [ Nucleotides ]+properBases = [ nucsA, nucsC, nucsG, nucsT ]++-- | Tests if a 'Nucleotides' is a gap.+-- Returns true only for the gap.+isGap :: Nucleotides -> Bool+isGap x = x == gap+++{-# INLINE showNucleotide #-}+showNucleotide :: Nucleotide -> Char+showNucleotide (N x) = C.index "ACGT" $ fromIntegral $ x .&. 3++{-# INLINE showNucleotides #-}+showNucleotides :: Nucleotides -> Char+showNucleotides (Ns x) = C.index  "-ACMGRSVTWYHKDBN" $ fromIntegral $ x .&. 15++instance Show Nucleotide where+    show     x = [ showNucleotide x ]+    showList l = (map showNucleotide l ++)++instance Read Nucleotide where+    readsPrec _ ('a':cs) = [(nucA, cs)]+    readsPrec _ ('A':cs) = [(nucA, cs)]+    readsPrec _ ('c':cs) = [(nucC, cs)]+    readsPrec _ ('C':cs) = [(nucC, cs)]+    readsPrec _ ('g':cs) = [(nucG, cs)]+    readsPrec _ ('G':cs) = [(nucG, cs)]+    readsPrec _ ('t':cs) = [(nucT, cs)]+    readsPrec _ ('T':cs) = [(nucT, cs)]+    readsPrec _ ('u':cs) = [(nucT, cs)]+    readsPrec _ ('U':cs) = [(nucT, cs)]+    readsPrec _     _    = [          ]++    readList ('-':cs) = readList cs+    readList (c:cs) | isSpace c = readList cs+                    | otherwise = case reads (c:cs) of+                            [] -> [ ([],c:cs) ]+                            xs -> [ (n:ns,r2) | (n,r1) <- xs, (ns,r2) <- readList r1 ]+    readList [] = [([],[])]++instance Show Nucleotides where+    show     x = [ showNucleotides x ]+    showList l = (map showNucleotides l ++)++instance Read Nucleotides where+    readsPrec _ (c:cs) = [(toNucleotides c, cs)]+    readsPrec _ [    ] = []+    readList s = let (hd,tl) = span (\c -> isAlpha c || isSpace c || '-' == c) s+                 in [(map toNucleotides $ filter (not . isSpace) hd, tl)]++-- | Complements a Nucleotides.+{-# INLINE compl #-}+compl :: Nucleotide -> Nucleotide+compl (N n) = N $ n `xor` 3++-- | Complements a Nucleotides.+{-# INLINE compls #-}+compls :: Nucleotides -> Nucleotides+compls (Ns x) = Ns $ ar `U.unsafeIndex` fromIntegral (x .&. 15)+  where+    !ar = U.fromListN 16 [ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 ]+++-- | Moves a @Position@.  The position is moved forward according to the+-- strand, negative indexes move backward accordingly.+shiftPosition :: Int -> Position -> Position+shiftPosition a p = p { p_start = p_start p + a }++-- | Moves a @Range@.  This is just @shiftPosition@ lifted.+shiftRange :: Int -> Range -> Range+shiftRange a r = r { r_pos = shiftPosition a (r_pos r) }++-- | Reverses a 'Range' to give the same @Range@ on the opposite strand.+reverseRange :: Range -> Range+reverseRange (Range (Pos sq pos) len) = Range (Pos sq (-pos-len)) len++-- | Extends a range.  The length of the range is simply increased.+extendRange :: Int -> Range -> Range+extendRange a r = r { r_length = r_length r + a }++-- | Expands a subrange.+-- @(range1 `insideRange` range2)@ interprets @range1@ as a subrange of+-- @range2@ and computes its absolute coordinates.  The sequence name of+-- @range1@ is ignored.+insideRange :: Range -> Range -> Range+insideRange r1@(Range (Pos _ start1) length1) r2@(Range (Pos sq start2) length2)+    | start2 < 0         = reverseRange (insideRange r1 (reverseRange r2))+    | start1 <= length2  = Range (Pos sq (start2 + start1)) (min length1 (length2 - start1))+    | otherwise          = Range (Pos sq (start2 + length2)) 0+++-- | Wraps a range to a region.  This simply normalizes the start+-- position to be in the interval '[0,n)', which only makes sense if the+-- @Range@ is to be mapped onto a circular genome.  This works on both+-- strands and the strand information is retained.+wrapRange :: Int -> Range -> Range+wrapRange n (Range (Pos sq s) l) = Range (Pos sq (s `mod` n)) l+++-- | A strict pair.+data Pair a b = !a :!: !b deriving(Eq, Ord, Show, Read, Bounded, Ix)+infixl 2 :!:+
+ Bio/Prelude.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE CPP #-}+module Bio.Prelude (+    module BasePrelude,+    module Bio.Base,+    module Bio.Util.Text,+    module Control.Monad.Catch,+    module Control.Monad.IO.Class,+    module Control.Monad.Trans.Class,+    module Data.Bifunctor,+    module Data.List.NonEmpty,+    module Data.Semigroup,+    module System.IO,++    Bytes, LazyBytes,+    Generic1(..),+    Hashable(..),+    Hashable1(..),+    Hashable2(..),+    HashMap,+    HashSet,+    IntMap,+    IntSet,+    NonEmpty(..),+    Semigroup(..),+    Text, LazyText+                   ) where++import BasePrelude hiding ( (<>), EOF+                          , bracket, bracket_, bracketOnError+                          , catch, catches, catchIOError, catchJust+                          , Handler, handle, handleJust+                          , finally, try, tryJust, onException+                          , mask, mask_, uninterruptibleMask, uninterruptibleMask_+#if MIN_VERSION_base(4,9,0)+                          , log1p, log1pexp, log1mexp, expm1+#endif+                          )++import Bio.Base+import Bio.Util.Text+import Control.Monad.Catch+import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Bifunctor+import Data.ByteString              ( ByteString )+import Data.List.NonEmpty           ( NonEmpty(..) )+import Data.Semigroup               ( Semigroup(..) )+import Data.Text                    ( Text )+import Data.Hashable                ( Hashable(..) )+import Data.Hashable.Lifted         ( Hashable1(..), Hashable2(..) )+import Data.HashMap.Strict          ( HashMap )+import Data.HashSet                 ( HashSet )+import Data.IntMap                  ( IntMap )+import Data.IntSet                  ( IntSet )+import GHC.Generics                 ( Generic1(..) )+import System.IO                    ( hPrint, hPutStr, hPutStrLn, stderr, stdout, stdin+                                    , openBinaryFile, withBinaryFile, IOMode(..)+                                    , hFlush, hSeek, hClose, SeekMode(..) )++import qualified Data.ByteString.Lazy   as BL+import qualified Data.Text.Lazy         as TL++type Bytes     =    ByteString+type LazyBytes = BL.ByteString+type LazyText  = TL.Text
+ Bio/Streaming.hs view
@@ -0,0 +1,195 @@+module Bio.Streaming+    ( MonadIO(..)+    , MonadMask+    , ByteStream++    , streamFile+    , streamHandle+    , streamInput+    , streamInputs+    , withOutputFile++    , protectTerm+    , psequence+    , progressGen+    , progressNum+    , progressPos++    , mergeStreams+    , mergeStreamsBy+    , mergeStreamsOn++    , module Streaming+    , module Streaming.Prelude )+  where++import Bio.Bam.Header+import Bio.Prelude+import Bio.Streaming.Bytes+import Bio.Util.Numeric                     ( showNum )+import Streaming                     hiding ( (<>) )+import Streaming.Internal                   ( Stream(..) )+import Streaming.Prelude                    ( each )+import System.IO++import qualified Streaming.Prelude      as Q++{- | Default buffer size in elements.++Since we often want to merge many files, a read should take more time+than a seek.  Assuming a rotating hard drive, this sets the sensible+buffer size to somewhat more than one MB.  A smaller buffer size would+surely work on SSDs, but the large buffer doesn't hurt either.+-}+defaultBufSize :: Int+defaultBufSize = 2*1024*1024++streamFile :: (MonadIO m, MonadMask m) => FilePath -> (ByteStream m () -> m r) -> m r+streamFile f k = bracket (liftIO $ openBinaryFile f ReadMode) (liftIO . hClose) (k . streamHandle)+{-# INLINE streamFile #-}++streamHandle :: MonadIO m => Handle -> ByteStream m ()+streamHandle = hGetContentsN defaultBufSize+{-# INLINE streamHandle #-}++-- | Reads 'stdin' if the filename is \"-\", else reads the named file.+streamInput :: (MonadIO m, MonadMask m) => FilePath -> (ByteStream m () -> m r) -> m r+streamInput "-" k = k (streamHandle stdin)+streamInput  f  k = streamFile f k+{-# INLINE streamInput #-}++{- | Reads multiple inputs in sequence.++Only one file is opened at a time, so they must also be consumed in+sequence.  The filename \"-\" refers to stdin, if no filenames are+given, stdin is read.+-}+streamInputs :: MonadIO m => [FilePath] -> (Stream (ByteStream m) m () -> r) -> r+streamInputs [] k = k $ yields (streamHandle stdin)+streamInputs fs k = k $ mapM_ go fs+  where+    go "-" = yields (streamHandle stdin)+    go  f  = yields $ do h <- liftIO $ openBinaryFile f ReadMode+                         streamHandle h+                         liftIO $ hClose h+{-# INLINE streamInputs #-}++{- | Protects the terminal from binary junk.++If @s@ is a 'Stream', then @protectTerm s@ throws an error if 'stdout'+is a terminal device, followed by the same 'Stream'.  This is most+usefully composed with functions that might otherwise write binary data+to an interactive terminal.+-}+protectTerm :: (Functor f, MonadIO m) => Stream f m r -> Stream f m r+protectTerm str = do+    t <- liftIO $ hIsTerminalDevice stdout+    when t . liftIO . throwM $ ErrorCall "cowardly refusing to write binary data to terminal"+    str+{-# INLINE protectTerm #-}++{- Like 'Streaming.sequence', but parallel.++This runs each element of a stream of actions.  A configurable number of+actions are buffered and run asynchronously.+-}+psequence :: MonadIO m => Int -> Stream (Of (IO a)) m b -> Stream (Of a) m b+psequence np = go emptyQ+  where+    -- if the queue is full, wait for the head element to complete+    go !qq s = case popQ qq of+        Just (a,qq') | lengthQ qq == np -> reap a >>= wrap . (:> go qq' s)+        _                               -> lift (inspect s) >>= go' qq++    -- if we have room for input, we get input+    go' !qq (Right (k :> s)) = liftIO (spawn k) >>= \a -> go (pushQ a qq) s+    go' !qq (Left         r) = goE r qq++    -- input ended, empty the queue+    goE r !qq = case popQ qq of+        Nothing      -> pure r+        Just (a,qq') -> reap a >>= wrap . (:> goE r qq')++    spawn :: IO a -> IO (MVar (Either SomeException a))+    spawn k = newEmptyMVar                  >>= \mv ->+              forkIO (try k >>= putMVar mv) >>+              return mv++    reap mv = liftIO (takeMVar mv) >>= either (liftIO . throwM) return+++-- A very simple queue data type.+-- Invariants: q = QQ l f b --> l == length f + length b+--                          --> l == 0 ==> null f++data QQ a = QQ !Int [a] [a]++emptyQ :: QQ a+emptyQ = QQ 0 [] []++lengthQ :: QQ a -> Int+lengthQ (QQ l _ _) = l++pushQ :: a -> QQ a -> QQ a+pushQ a (QQ l [] b) = QQ (l+1) (reverse (a:b)) []+pushQ a (QQ l  f b) = QQ (l+1) f (a:b)++popQ :: QQ a -> Maybe (a, QQ a)+popQ (QQ _ [    ] _) = Nothing+popQ (QQ l [ a  ] b) = Just (a, QQ (l-1) (reverse b) [])+popQ (QQ l (a:fs) b) = Just (a, QQ (l-1) fs b)+++mergeStreams :: (Monad m, Ord a)+             => Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)+mergeStreams = mergeStreamsBy compare+{-# INLINE mergeStreams #-}++mergeStreamsOn :: (Monad m, Ord b)+               => (a -> b) -> Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)+mergeStreamsOn f = mergeStreamsBy (comparing f)+{-# INLINE mergeStreamsOn #-}++mergeStreamsBy :: Monad m+               => (a -> a -> Ordering)+               -> Stream (Of a) m r -> Stream (Of a) m s -> Stream (Of a) m (r, s)+mergeStreamsBy cmp = go+  where+    go str0 str1 = case str0 of+      Return r0         -> (\r1 -> (r0, r1)) <$> str1+      Effect m          -> Effect $ liftM (\str -> go str str1) m+      Step (a :> rest0) -> case str1 of+        Return r1         -> (\r0 -> (r0, r1)) <$> str0+        Effect m          -> Effect $ liftM (go str0) m+        Step (b :> rest1) -> case cmp a b of+          LT -> Step (a :> go rest0 str1)+          EQ -> Step (a :> go rest0 str1) -- left-biased+          GT -> Step (b :> go str0 rest1)+{-# INLINABLE mergeStreamsBy #-}++-- | A general progress indicator that prints some message after a set+-- number of records have passed through.+progressGen :: MonadIO m+            => (Int -> a -> String) -> Int -> (String -> IO ())+            -> Q.Stream (Q.Of a) m r -> Q.Stream (Q.Of a) m r+progressGen msg sz put = go 0+  where+    go   !n = lift . Q.next >=> either pure (step $ succ n)+    step !n (a,s) = do when (n `mod` sz == 0) . liftIO . put $ "\27[K" ++ msg n a ++ "\r"+                       Q.cons a (go n s)++-- | A simple progress indicator that prints the number of records.+progressNum :: MonadIO m+            => String -> Int -> (String -> IO ())+            -> Q.Stream (Q.Of a) m r -> Q.Stream (Q.Of a) m r+progressNum msg = progressGen (\n _ -> msg ++ " " ++ showNum n)++-- | A simple progress indicator that prints a position every set number+-- of passed records.+progressPos :: MonadIO m+            => (a -> (Refseq, Int)) -> String -> Refs -> Int -> (String -> IO ())+            -> Q.Stream (Q.Of a) m r -> Q.Stream (Q.Of a) m r+progressPos f msg refs =+    progressGen $ \_ a -> let (!rs1, !po1) = f a+                              !nm = unpack . sq_name $ getRef refs rs1+                          in msg ++ " " ++ nm ++ ":" ++ showNum po1
+ Bio/Streaming/Bgzf.hs view
@@ -0,0 +1,331 @@+{-# LANGUAGE ForeignFunctionInterface #-}+-- | Buffer builder to assemble Bgzf blocks.  The idea is to serialize+-- stuff (BAM and BCF) into a buffer, then bgzf chunks from the buffer.+-- We use a large buffer, and we always make sure there is plenty of+-- space in it (to avoid redundant checks).++module Bio.Streaming.Bgzf (+    bgunzip,+    getBgzfHdr,+    BB(..),+    newBuffer,+    fillBuffer,+    expandBuffer,+    encodeBgzf,+    BgzfTokens(..),+    BclArgs(..),+    BclSpecialType(..),+    loop_dec_int,+    loop_bcl_special+                            ) where++import Bio.Prelude+import Bio.Streaming+import Foreign.C.Types                     ( CInt(..) )+import Foreign.Marshal.Utils               ( copyBytes, with )++import qualified Bio.Streaming.Bytes        as S+import qualified Data.ByteString            as B+import qualified Data.ByteString.Internal   as B+import qualified Data.ByteString.Unsafe     as B+import qualified Data.Vector.Storable       as V++{-| Decompresses a bgzip stream.  Individual chunks are decompressed in+    parallel.  Leftovers are discarded (some compressed HETFA files+    appear to have junk at the end). -}++bgunzip :: MonadIO m => ByteStream m r -> ByteStream m r+bgunzip s = do+    np <- liftIO $ getNumCapabilities+    S.fromChunks $ psequence (2*np) $ lift (getBgzfHdr s) >>= go+  where+    go (Nothing,    _hdr, s1) = lift (S.effects s1)+    go (Just bsize, _hdr, s1) = do+            blk :> s2 <- lift $ S.splitAt' bsize s1+            wrap (decompressChunk blk :> (lift (getBgzfHdr s2) >>= go))+{-# INLINABLE bgunzip #-}++getBgzfHdr :: Monad m => ByteStream m r -> m (Maybe Int, B.ByteString, ByteStream m r)+getBgzfHdr s0 = do+        hdr :> s1 <- S.splitAt' 18 s0+        if or [ B.length hdr < 18+              , B.index hdr 0 /= 139+              , B.index hdr 1 /= 31+              , B.index hdr 3 .&. 4 /= 4+              , B.index hdr 10 /= 6+              , B.index hdr 11 /= 0+              , B.index hdr 12 /= 66+              , B.index hdr 13 /= 67 ]+          then return (Nothing, hdr, s1)+          else do+            let bsize = fromIntegral (B.index hdr 16) + fromIntegral (B.index hdr 17) `shiftL` 8 - 16+            return (Just bsize, hdr, s1)+{-# INLINE getBgzfHdr #-}++-- | We manage a large buffer (multiple megabytes), of which we fill an+-- initial portion.  We remember the size, the used part, and two marks+-- where we later fill in sizes for the length prefixed BAM or BCF+-- records.  We move the buffer down when we yield a piece downstream,+-- and when we run out of space, we simply move to a new buffer.+-- Garbage collection should take care of the rest.  Unused 'mark' must+-- be set to (maxBound::Int) so it doesn't interfere with flushing.++data BB = BB { buffer :: {-# UNPACK #-} !(ForeignPtr Word8)+             , size   :: {-# UNPACK #-} !Int            -- total size of buffer+             , off    :: {-# UNPACK #-} !Int            -- offset of active portion+             , used   :: {-# UNPACK #-} !Int            -- used portion (inactive & active)+             , mark   :: {-# UNPACK #-} !Int            -- offset of mark+             , mark2  :: {-# UNPACK #-} !Int }          -- offset of mark2++-- | Things we are able to encode.  Taking inspiration from+-- binary-serialise-cbor, we define these as a lazy list-like thing and+-- consume it in a interpreter.++data BgzfTokens = TkWord32   {-# UNPACK #-} !Word32       BgzfTokens -- a 4-byte int+                | TkWord16   {-# UNPACK #-} !Word16       BgzfTokens -- a 2-byte int+                | TkWord8    {-# UNPACK #-} !Word8        BgzfTokens -- a byte+                | TkFloat    {-# UNPACK #-} !Float        BgzfTokens -- a float+                | TkDouble   {-# UNPACK #-} !Double       BgzfTokens -- a double+                | TkString   {-# UNPACK #-} !B.ByteString BgzfTokens -- a raw string+                | TkDecimal  {-# UNPACK #-} !Int          BgzfTokens -- roughly ':%d'++                | TkSetMark                               BgzfTokens -- sets the first mark+                | TkEndRecord                             BgzfTokens -- completes a BAM record+                | TkEndRecordPart1                        BgzfTokens -- completes part 1 of a BCF record+                | TkEndRecordPart2                        BgzfTokens -- completes part 2 of a BCF record+                | TkEnd                                              -- nothing more, for now++                | TkBclSpecial !BclArgs                   BgzfTokens+                | TkLowLevel {-# UNPACK #-} !Int (BB -> IO BB) BgzfTokens++data BclSpecialType = BclNucsBin  | BclNucsAsc  | BclNucsAscRev  | BclNucsWide+                    | BclQualsBin | BclQualsAsc | BclQualsAscRev++data BclArgs = BclArgs BclSpecialType+                       {-# UNPACK #-} !(V.Vector Word8)   -- bcl matrix+                       {-# UNPACK #-} !Int                -- stride+                       {-# UNPACK #-} !Int                -- first cycle+                       {-# UNPACK #-} !Int                -- last cycle+                       {-# UNPACK #-} !Int                -- cluster index++-- | Creates a buffer.+newBuffer :: Int -> IO BB+newBuffer sz = mallocForeignPtrBytes sz >>= \ar -> return $ BB ar sz 0 0 maxBound maxBound++-- | Creates a new buffer, copying the active content from an old one,+-- with higher capacity.  The size of the new buffer is twice the free+-- space in the old buffer, but at least @minsz@.+expandBuffer :: Int -> BB -> IO BB+expandBuffer minsz b = do+    let sz' = max (2 * (size b - used b)) minsz+    arr1 <- mallocForeignPtrBytes sz'+    withForeignPtr arr1 $ \d ->+        withForeignPtr (buffer b) $ \s ->+             copyBytes d (plusPtr s (off b)) (used b - off b)+    return BB{ buffer = arr1+             , size   = sz'+             , off    = 0+             , used   = used b - off b+             , mark   = if mark  b == maxBound then maxBound else mark  b - off b+             , mark2  = if mark2 b == maxBound then maxBound else mark2 b - off b }++compressChunk :: Int -> ForeignPtr Word8 -> Int -> Int -> IO B.ByteString+compressChunk lv fptr off slen =+    withForeignPtr fptr                 $ \ptr ->+    B.createAndTrim 65536               $ \buf ->+    with 65536                          $ \p_len -> do+        rc <- compress_chunk buf p_len (plusPtr ptr off) (fromIntegral slen) (fromIntegral lv)+        when (rc /= 0 && rc /= 1) . error $ "compress_chunk failed: " ++ show rc+        fromIntegral <$> peek p_len++decompressChunk :: B.ByteString -> IO B.ByteString+decompressChunk ck =+    B.unsafeUseAsCString ck                         $ \psrc ->+    peekByteOff psrc (B.length ck - 4)            >>= \dlen ->+    B.create (fromIntegral (dlen::Word32))          $ \pdest -> do+        rc <- decompress_chunk pdest (fromIntegral dlen) (castPtr psrc) (fromIntegral $ B.length ck)+        when (rc /= 0) . error $ "decompress_chunk failed: " ++ show rc+++-- | Expand a chain of tokens into a buffer, sending finished pieces+-- downstream as soon as possible.+encodeBgzf :: MonadIO m => Int -> Stream (Of (Endo BgzfTokens)) m b -> S.ByteStream m b+encodeBgzf lv str = do+    np <- liftIO $ getNumCapabilities+    bb <- liftIO $ newBuffer (1024*1024)+    S.fromChunks $ psequence (2*np) $ lift (inspect str) >>= go bb+  where+    go :: MonadIO m+       => BB+       -> Either b (Of (Endo BgzfTokens) (Stream (Of (Endo BgzfTokens)) m b))+       -> Stream (Of (IO Bytes)) m b+    go bb0 (Left r) = final_flush bb0 r+    go bb0 (Right (f :> s))+        -- initially, we make sure we have reasonable space.  this may not be enough.+        | size bb0 - used bb0 < 1024 = liftIO (expandBuffer (1024*1024) bb0) >>= \bb' -> go' bb' (appEndo f TkEnd) s+        | otherwise                  =                                                   go' bb0 (appEndo f TkEnd) s++    go' bb0 tk s = liftIO (fillBuffer bb0 tk) >>= \(bb',tk') -> flush_blocks tk' bb' s++    -- We can flush anything that is between 'off' and the lower of 'mark'+    -- and 'used'.  When done, we bump 'off'.+    flush_blocks tk bb s+        | min (mark bb) (used bb) - off bb < maxBlockSize =+            case tk of TkEnd -> lift (inspect s) >>= go bb+                       _     -> -- we arrive here because we ran out of buffer space, so we expand it.+                                liftIO (expandBuffer (1024*1024) bb) >>= \bb' -> go' bb' tk s+        | otherwise =+            wrap $  compressChunk lv (buffer bb) (off bb) maxBlockSize+                 :> flush_blocks tk bb { off = off bb + maxBlockSize } s++    final_flush bb r+        | used bb > off bb =+            wrap $  compressChunk lv (buffer bb) (off bb) (used bb - off bb)+                 :> wrap (return bgzfEofMarker :> pure r)+        | otherwise =+            wrap (return bgzfEofMarker :> pure r)++    -- maximum block size for Bgzf: 64k with some room for+    -- headers and uncompressible stuff+    maxBlockSize = 65478+++fillBuffer :: BB -> BgzfTokens -> IO (BB, BgzfTokens)+fillBuffer bb0 tk = withForeignPtr (buffer bb0) (\p -> go_slowish p bb0 tk)+  where+    go_slowish p bb = go_fast p bb (used bb)++    go_fast p bb use tk1 = case tk1 of+        -- no space?  not our job.+        _ | size bb - use < 1024 -> return (bb { used = use },tk1)++        -- the actual end.+        TkEnd                    -> return (bb { used = use },tk1)++        -- I'm cheating.  This stuff works only if the platform allows+        -- unaligned accesses, is little-endian and uses IEEE floats.+        -- It's true on i386 and ix86_64.+        TkWord32   x tk' -> do pokeByteOff p use x+                               go_fast p bb (use + 4) tk'++        TkWord16   x tk' -> do pokeByteOff p use x+                               go_fast p bb (use + 2) tk'++        TkWord8    x tk' -> do pokeByteOff p use x+                               go_fast p bb (use + 1) tk'++        TkFloat    x tk' -> do pokeByteOff p use x+                               go_fast p bb (use + 4) tk'++        TkDouble   x tk' -> do pokeByteOff p use x+                               go_fast p bb (use + 8) tk'++        TkString   s tk'+            -- Too big, can't handle.  By returning with unfinished+            -- business, we will get progressively bigger buffers and+            -- eventually handle it.+            | B.length s > size bb - use -> return (bb { used = use },tk1)++            | otherwise  -> do let ln = B.length s+                               B.unsafeUseAsCString s $ \q ->+                                    copyBytes (p `plusPtr` use) q ln+                               go_fast p bb (use + ln) tk'++        TkDecimal  x tk' -> do ln <- int_loop (p `plusPtr` use) (fromIntegral x)+                               go_fast p bb (use + fromIntegral ln) tk'++        TkSetMark        tk' ->    go_slowish p bb { used = use + 4, mark = use } tk'++        TkEndRecord      tk' -> do let !l = use - mark bb - 4+                                   pokeByteOff p (mark bb) (fromIntegral l :: Word32)+                                   go_slowish p bb { used = use, mark = maxBound } tk'++        TkEndRecordPart1 tk' -> do let !l = use - mark bb - 4+                                   pokeByteOff p (mark bb - 4) (fromIntegral l :: Word32)+                                   go_slowish p bb { used = use, mark2 = use } tk'++        TkEndRecordPart2 tk' -> do let !l = use - mark2 bb+                                   pokeByteOff p (mark bb) (fromIntegral l :: Word32)+                                   go_slowish p bb { used = use, mark = maxBound } tk'+++        TkBclSpecial special_args tk' -> do+            l <- loop_bcl_special (p `plusPtr` use) special_args+            go_fast p bb (use + l) tk'++        TkLowLevel minsize proc tk'+            | size bb - use < minsize -> return (bb { used = use },tk1)+            | otherwise               -> do bb' <- proc bb { used = use }+                                            go_slowish p bb' tk'++-- | The EOF marker for BGZF files.+-- This is just an empty string compressed as BGZF.  Appended to BAM+-- files to indicate their end.+bgzfEofMarker :: Bytes+bgzfEofMarker = "\x1f\x8b\x8\x4\0\0\0\0\0\xff\x6\0\x42\x43\x2\0\x1b\0\x3\0\0\0\0\0\0\0\0\0"++loop_dec_int :: Ptr Word8 -> Int -> IO Int+loop_dec_int p i = fromIntegral <$> int_loop p (fromIntegral i)++loop_bcl_special :: Ptr Word8 -> BclArgs -> IO Int+loop_bcl_special p (BclArgs tp vec stride u v i) =++    V.unsafeWith vec $ \q -> case tp of+        BclNucsBin -> do+            nuc_loop p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ (v - u + 2) `div` 2++        BclNucsWide -> do+            nuc_loop_wide p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++        BclNucsAsc -> do+            nuc_loop_asc p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++        BclNucsAscRev -> do+            nuc_loop_asc_rev p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++        BclQualsBin -> do+            qual_loop p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++        BclQualsAsc -> do+            qual_loop_asc p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++        BclQualsAscRev -> do+            qual_loop_asc_rev p (fromIntegral stride) (plusPtr q i) (fromIntegral u) (fromIntegral v)+            return $ v - u + 1++foreign import ccall unsafe "nuc_loop"+    nuc_loop :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "nuc_loop_wide"+    nuc_loop_wide :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "nuc_loop_asc"+    nuc_loop_asc :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "nuc_loop_asc_rev"+    nuc_loop_asc_rev :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "qual_loop"+    qual_loop :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "qual_loop_asc"+    qual_loop_asc :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "qual_loop_asc_rev"+    qual_loop_asc_rev :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> CInt -> IO ()++foreign import ccall unsafe "int_loop"+    int_loop :: Ptr Word8 -> CInt -> IO CInt++foreign import ccall unsafe "compress_chunk"+    compress_chunk :: Ptr Word8 -> Ptr CInt -> Ptr Word8 -> CInt -> CInt -> IO CInt++foreign import ccall unsafe "decompress_chunk"+    decompress_chunk :: Ptr Word8 -> CInt -> Ptr Word8 -> CInt -> IO CInt+
+ Bio/Streaming/Bytes.hs view
@@ -0,0 +1,780 @@+{-# LANGUAGE Rank2Types, TypeFamilies #-}++-- This library emulates Data.ByteStream.Lazy but includes a monadic element+-- and thus at certain points uses a `Stream`/`FreeT` type in place of lists.++-- |+-- Module      : ByteStream+-- Copyright   : (c) Don Stewart 2006+--               (c) Duncan Coutts 2006-2011+--               (c) Michael Thompson 2015+--               (c) Udo Stenzel 2018+-- License     : BSD-style+--+-- Maintainer  : u.stenzel@web.de+-- Stability   : experimental+-- Portability : portable+--+-- See the simple examples of use <https://gist.github.com/michaelt/6c6843e6dd8030e95d58 here>.+-- We begin with a slight modification of the documentation to "Data.ByteStream.Lazy":+--+-- A time and space-efficient implementation of effectful byte streams+-- using a stream of packed 'Word8' arrays, suitable for high performance+-- use, both in terms of large data quantities, or high speed+-- requirements. ByteStreams are encoded as streams of strict chunks+-- of bytes.+--+-- A key feature of ByteStreams is the means to manipulate large or+-- unbounded streams of data without requiring the entire sequence to be+-- resident in memory. To take advantage of this you have to write your+-- functions in a streaming style, e.g. classic pipeline composition. The+-- default I\/O chunk size is 32k, which should be good in most circumstances.+--+-- Some operations, such as 'concat', 'append', 'reverse' and 'cons', have+-- better complexity than their "Data.ByteStream" equivalents, due to+-- optimisations resulting from the list spine structure. For other+-- operations streaming, like lazy, ByteStreams are usually within a few percent of+-- strict ones.+--+-- This module is intended to be imported @qualified@, to avoid name+-- clashes with "Prelude" functions.  eg.+--+-- > import qualified Bio.Streaming.Bytes as B+--+-- Original GHC implementation by Bryan O\'Sullivan.+-- Rewritten to use 'Data.Array.Unboxed.UArray' by Simon Marlow.+-- Rewritten to support slices and use 'Foreign.ForeignPtr.ForeignPtr'+-- by David Roundy.+-- Rewritten again and extended by Don Stewart and Duncan Coutts.+-- Lazy variant by Duncan Coutts and Don Stewart.+-- Streaming variant by Michael Thompson, following the ideas of Gabriel Gonzales' pipes-bytestring+-- Adapted for use in biohazard by Udo Stenzel.+--+module Bio.Streaming.Bytes (+    -- * The @ByteStream@ type+    ByteStream(..)++    -- * Introducing and eliminating 'ByteStream's+    , empty            -- empty :: ByteStream m ()+    , singleton        -- singleton :: Monad m => Word8 -> ByteStream m ()+    , fromLazy         -- fromLazy :: Monad m => ByteStream -> ByteStream m ()+    , fromChunks       -- fromChunks :: Monad m => Stream (Of Bytes) m r -> ByteStream m r+    , toLazy           -- toLazy :: Monad m => ByteStream m () -> m ByteStream+    , toStrict         -- toStrict :: Monad m => ByteStream m () -> m ByteStream+    , effects+    , mwrap++    -- * Basic interface+    , cons             -- cons :: Monad m => Word8 -> ByteStream m r -> ByteStream m r+    , nextByte         -- nextByte :: Monad m => ByteStream m r -> m (Either r (Word8, ByteStream m r))+    , nextByteOff      -- nextByteOff :: Monad m => ByteStream m r -> m (Either r (Word8, Int64, ByteStream m r))++    -- * Substrings++    -- ** Breaking strings+    , break            -- break :: Monad m => (Word8 -> Bool) -> ByteStream m r -> ByteStream m (ByteStream m r)+    , drop             -- drop :: Monad m => GHC.Int.Int64 -> ByteStream m r -> ByteStream m r+    , dropWhile+    , splitAt          -- splitAt :: Monad m => GHC.Int.Int64 -> ByteStream m r -> ByteStream m (ByteStream m r)+    , splitAt'         -- splitAt' :: Monad m => Int -> ByteStream m r -> m (Of Bytes (ByteStream m r))+    , trim++    -- ** Breaking into many substrings+    , lines+    , lines'++    -- ** Special folds+    , concat          -- concat :: Monad m => Stream (ByteStream m) m r -> ByteStream m r++    -- * Builders+    , toByteStream+    , toByteStreamWith+    , concatBuilders++    -- * I\/O with 'ByteStream's++    -- ** Files+    , withOutputFile+    , writeFile        -- writeFile :: FilePath -> ByteStream IO r -> IO r++    -- ** I\/O with Handles+    , hGetContents     -- hGetContents :: Handle -> ByteStream IO ()+    , hGetContentsN    -- hGetContentsN :: Int -> Handle -> ByteStream IO ()+    , hPut             -- hPut :: Handle -> ByteStream IO r -> IO r++    -- * Simple chunkwise operations+    , nextChunk+    , nextChunkOff+    , consChunk             -- :: Bytes -> ByteStream m r -> ByteStream m r+    , consChunkOff          -- :: Bytes -> Int64 -> ByteStream m r -> ByteStream m r+    , chunk+    , copy+    , mapChunksM_++    -- * compression support+    , gzip+    , gunzip+    , gunzipWith++  ) where++import Bio.Prelude                      hiding (break,concat,drop,dropWhile,lines,splitAt,writeFile,empty,loop)+import Data.ByteString.Builder.Internal+        (Builder,builder,runBuilder,runBuilderWith,bufferSize+        ,AllocationStrategy,ChunkIOStream(..),buildStepToCIOS+        ,byteStringFromBuffer,safeStrategy,defaultChunkSize)+import GHC.Exts                                (SpecConstrAnnotation(..))+import Streaming                               (Of(..),Identity(..),destroy)+import Streaming.Internal                      (Stream (..))+import System.Directory                        (renameFile)++import qualified Codec.Compression.Zlib.Internal as Z+import qualified Data.ByteString                 as B+import qualified Data.ByteString.Internal        as B+import qualified Data.ByteString.Lazy.Internal   as L (foldrChunks,ByteString(..),smallChunkSize,defaultChunkSize)+import qualified Data.ByteString.Unsafe          as B+import qualified Streaming.Prelude               as Q++-- | A space-efficient representation of a succession of 'Word8' vectors, supporting many+-- efficient operations.+--+-- An effectful 'ByteStream' contains 8-bit bytes, or by using certain+-- operations can be interpreted as containing 8-bit characters.  It+-- also contains an offset, which will be needed to track the virtual+-- offsets in the BGZF decode.++data ByteStream m r =+  Empty r+  | Chunk {-# UNPACK #-} !Bytes {-# UNPACK #-} !Int64 (ByteStream m r)+  | Go (m (ByteStream m r))++instance Monad m => Functor (ByteStream m) where+  fmap f x = case x of+    Empty a        -> Empty (f a)+    Chunk bs o bss -> Chunk bs o (fmap f bss)+    Go mbss        -> Go (liftM (fmap f) mbss)++instance Monad m => Applicative (ByteStream m) where+  pure = Empty+  {-# INLINE pure #-}+  bf <*> bx = do {f <- bf; x <- bx; Empty (f x)}+  {-# INLINE (<*>) #-}+  (*>) = (>>)+  {-# INLINE (*>) #-}++instance Monad m => Monad (ByteStream m) where+  return = Empty+  {-# INLINE return #-}+  x0 >> y = loop SPEC x0 where+    loop !_ x = case x of   -- this seems to be insanely effective+      Empty _     -> y+      Chunk a o b -> Chunk a o (loop SPEC b)+      Go m        -> Go (liftM (loop SPEC) m)+  {-# INLINEABLE (>>) #-}+  x >>= f =+    loop SPEC2 x where -- unlike >> this SPEC seems pointless+      loop !_ y = case y of+        Empty a        -> f a+        Chunk bs o bss -> Chunk bs o (loop SPEC bss)+        Go mbss        -> Go (liftM (loop SPEC) mbss)+  {-# INLINEABLE (>>=) #-}++instance MonadIO m => MonadIO (ByteStream m) where+  liftIO io = Go (liftM Empty (liftIO io))+  {-# INLINE liftIO #-}++instance MonadTrans ByteStream where+  lift ma = Go $ liftM Empty ma+  {-# INLINE lift #-}++instance (r ~ ()) => IsString (ByteStream m r) where+  fromString = chunk . fromString+  {-# INLINE fromString #-}++instance (m ~ Identity, Show r) => Show (ByteStream m r) where+  show bs0 = case bs0 of  -- the implementation this instance deserves ...+    Empty r           -> "Empty (" ++ show r ++ ")"+    Go (Identity bs') -> "Go (Identity (" ++ show bs' ++ "))"+    Chunk bs'' o bs   -> "Chunk " ++ show bs'' ++ " " ++ show o ++ " (" ++ show bs ++ ")"++instance (Semigroup r, Monad m) => Semigroup (ByteStream m r) where+  (<>) = liftM2 (<>)+  {-# INLINE (<>) #-}++instance (Semigroup r, Monoid r, Monad m) => Monoid (ByteStream m r) where+  mempty = Empty mempty+  {-# INLINE mempty #-}+  mappend = (<>)+  {-# INLINE mappend #-}+++data SPEC = SPEC | SPEC2+{-# ANN type SPEC ForceSpecConstr #-}++-- --------------------------------------------------------------------------++-- | Smart constructor for 'Chunk'.+consChunk :: Bytes -> ByteStream m r -> ByteStream m r+consChunk c = consChunkOff c 0+{-# INLINE consChunk #-}++consChunkOff :: Bytes -> Int64 -> ByteStream m r -> ByteStream m r+consChunkOff c@(B.PS _ _ len) off cs+  | len == 0  = cs+  | otherwise = Chunk c off cs+{-# INLINE consChunkOff #-}++-- | Yield-style smart constructor for 'Chunk'.+chunk :: Bytes -> ByteStream m ()+chunk bs = consChunk bs empty+{-# INLINE chunk #-}+++{- | Reconceive an effect that results in an effectful bytestring as an effectful bytestring.+    Compare Streaming.mwrap. The closes equivalent of++>>> Streaming.wrap :: f (Stream f m r) -> Stream f m r++    is here  @consChunk@. @mwrap@ is the smart constructor for the internal @Go@ constructor.+-}+mwrap :: m (ByteStream m r) -> ByteStream m r+mwrap = Go+{-# INLINE mwrap #-}++-- | Construct a succession of chunks from its Church encoding (compare @GHC.Exts.build@)+materialize :: (forall x . (r -> x) -> (Bytes -> Int64 -> x -> x) -> (m x -> x) -> x)+            -> ByteStream m r+materialize phi = phi Empty Chunk Go+{-# INLINE[0] materialize #-}++-- | Resolve a succession of chunks into its Church encoding; this is+-- not a safe operation; it is equivalent to exposing the constructors+dematerialize :: Monad m+              => ByteStream m r+              -> (forall x . (r -> x) -> (Bytes -> Int64 -> x -> x) -> (m x -> x) -> x)+dematerialize x0 nil con fin = loop SPEC x0+  where+  loop !_ x = case x of+     Empty r      -> nil r+     Chunk b o bs -> con b o (loop SPEC bs )+     Go ms        -> fin (liftM (loop SPEC) ms)+{-# INLINE [1] dematerialize #-}++{-# RULES+  "dematerialize/materialize" forall (phi :: forall b . (r -> b) -> (Bytes -> Int64 -> b -> b) -> (m b -> b) -> b) . dematerialize (materialize phi) = phi ;+  #-}+------------------------------------------------------------------------++copy :: Monad m => ByteStream m r -> ByteStream (ByteStream m) r+copy = loop where+  loop str = case str of+    Empty r         -> Empty r+    Go m            -> Go (liftM loop (lift m))+    Chunk bs o rest -> Chunk bs o (Go (Chunk bs o (Empty (loop rest))))+{-# INLINABLE copy #-}++-- | /O(n)/ Concatenate a stream of byte streams.+concat :: Monad m => Stream (ByteStream m) m r -> ByteStream m r+concat x = destroy x join Go Empty+{-# INLINE concat #-}++-- | Perform the effects contained in an effectful bytestring, ignoring the bytes.+effects :: Monad m => ByteStream m r -> m r+effects bs = case bs of+  Empty r        -> return r+  Go m           -> m >>= effects+  Chunk _ _ rest -> effects rest+{-# INLINABLE effects #-}+++-- -----------------------------------------------------------------------------+-- Introducing and eliminating 'ByteStream's++{-| /O(1)/ The empty 'ByteStream' -- i.e. @return ()@ Note that @ByteStream m w@ is+  generally a monoid for monoidal values of @w@, like @()@+-}+empty :: ByteStream m ()+empty = Empty ()+{-# INLINE empty #-}++{-| /O(1)/ Yield a 'Word8' as a minimal 'ByteStream'+-}+singleton :: Word8 -> ByteStream m ()+singleton w = Chunk (B.singleton w) 0 (Empty ())+{-# INLINE singleton #-}++-- | /O(c)/ Converts a byte stream into a stream of individual strict bytestrings.+--   This of course exposes the internal chunk structure.+toChunks :: Monad m => ByteStream m r -> Stream (Of Bytes) m r+toChunks bs = dematerialize bs return (\b _ mx -> Step (b :> mx)) Effect+{-# INLINE toChunks #-}++mapChunksM_ :: Monad m => (Bytes -> m ()) -> ByteStream m r -> m r+mapChunksM_ f bs = dematerialize bs return (\c _ k -> f c >> k) join+{-# INLINE mapChunksM_ #-}+++-- | /O(c)/ Converts a stream of strict bytestrings into a byte stream.+fromChunks :: Monad m => Stream (Of Bytes) m r -> ByteStream m r+fromChunks bs = destroy bs+      (\(b :> mx) !i -> Chunk b i (mx (i + fromIntegral (B.length b))))+      (\k !i -> Go (k >>= \f -> return (f i)))+      (\r !_ -> return r) 0+{-# INLINE fromChunks #-}++{-| /O(n)/ Convert a monadic byte stream into a single strict 'ByteStream',+   retaining the return value of the original pair. This operation is+   for use with 'mapped'.++> mapped R.toStrict :: Monad m => Stream (ByteStream m) m r -> Stream (Of ByteStream) m r++   It is subject to all the objections one makes to Data.ByteStream.Lazy 'toStrict';+   all of these are devastating.+-}+toStrict :: Monad m => ByteStream m r -> m (Of Bytes r)+toStrict bs = do+  (bss :> r) <- Q.toList (toChunks bs)+  return $ (B.concat bss :> r)+{-# INLINE toStrict #-}++{-| /O(c)/ Transmute a pseudo-pure lazy bytestring to its representation+    as a monadic stream of chunks.++>>> Q.putStrLn $ Q.fromLazy "hi"+hi+>>>  Q.fromLazy "hi"+Chunk "hi" (Empty (()))  -- note: a 'show' instance works in the identity monad+>>>  Q.fromLazy $ BL.fromChunks ["here", "are", "some", "chunks"]+Chunk "here" (Chunk "are" (Chunk "some" (Chunk "chunks" (Empty (())))))++-}+fromLazy :: LazyBytes -> ByteStream m ()+fromLazy = L.foldrChunks consChunk empty+{-# INLINE fromLazy #-}++{-| /O(n)/ Convert an effectful byte stream into a single lazy 'ByteStream'+    with the same internal chunk structure, retaining the original+    return value.++    This is the canonical way of breaking streaming (@toStrict@ and the+    like are far more demonic). Essentially one is dividing the interleaved+    layers of effects and bytes into one immense layer of effects,+    followed by the memory of the succession of bytes.++    Because one preserves the return value, @toLazy@ is a suitable argument+    for 'Streaming.mapped'++>   B.mapped Q.toLazy :: Stream (ByteStream m) m r -> Stream (Of LazyBytes) m r++>>> Q.toLazy "hello"+"hello" :> ()+>>> B.toListM $ traverses Q.toLazy $ Q.lines "one\ntwo\nthree\nfour\nfive\n"+["one","two","three","four","five",""]  -- [LazyBytes]++-}+toLazy :: Monad m => ByteStream m r -> m (Of LazyBytes r)+toLazy bs0 = dematerialize bs0+                (\r -> return (L.Empty :> r))+                (\b _ mx -> do+                      (bs :> x) <- mx+                      return $ L.Chunk b bs :> x+                      )+                join+{-# INLINE toLazy #-}++-- | /O(1)/ 'cons' is analogous to '(:)' for lists.+cons :: Word8 -> ByteStream m r -> ByteStream m r+cons c cs = Chunk (B.singleton c) 0 cs+{-# INLINE cons #-}++-- | /O(1)/ Extract the head and tail of a 'ByteStream', or its return value+-- if it is empty. This is the \'natural\' uncons for an effectful byte stream.+nextByte :: Monad m => ByteStream m r -> m (Either r (Word8, ByteStream m r))+nextByte = liftM (either Left (\(a,_,b) -> Right (a,b))) . nextByteOff+{-# INLINE nextByte #-}++nextByteOff :: Monad m => ByteStream m r -> m (Either r (Word8, Int64, ByteStream m r))+nextByteOff (Empty r) = return (Left r)+nextByteOff (Chunk c o cs)+    = if B.null c+        then nextByteOff cs+        else return $ Right (B.unsafeHead c, o+                     , if B.length c == 1+                         then cs+                         else Chunk (B.unsafeTail c) (o+1) cs)+nextByteOff (Go m) = m >>= nextByteOff+{-# INLINABLE nextByteOff #-}++nextChunk :: Monad m => ByteStream m r -> m (Either r (Bytes, ByteStream m r))+nextChunk = liftM (either Left (\(a,_,b) -> Right (a,b))) . nextChunkOff+{-# INLINE nextChunk #-}++nextChunkOff :: Monad m => ByteStream m r -> m (Either r (Bytes, Int64, ByteStream m r))+nextChunkOff (Empty r)      = return (Left r)+nextChunkOff (Go m)         = m >>= nextChunkOff+nextChunkOff (Chunk c o cs)+    | B.null c              = nextChunkOff cs+    | otherwise             = return (Right (c,o,cs))+{-# INLINABLE nextChunkOff #-}++{-| /O(n\/c)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@+    elements, or @[]@ if @n > 'length' xs@.++>>> Q.putStrLn $ Q.drop 6 "Wisconsin"+sin+>>> Q.putStrLn $ Q.drop 16 "Wisconsin"++>>>+-}+drop  :: Monad m => Int64 -> ByteStream m r -> ByteStream m r+drop i p | i <= 0 = p+drop i cs0 = drop' i cs0+  where drop' 0 cs           = cs+        drop' _ (Empty r)    = Empty r+        drop' n (Chunk c o cs) =+          if n < fromIntegral (B.length c)+            then Chunk (B.drop (fromIntegral n) c) (o+n) cs+            else drop' (n - fromIntegral (B.length c)) cs+        drop' n (Go m) = Go (liftM (drop' n) m)+{-# INLINABLE drop #-}+++{-| /O(n\/c)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.++>>> rest <- Q.putStrLn $ Q.splitAt 3 "therapist is a danger to good hyphenation, as Knuth notes"+the+>>> Q.putStrLn $ Q.splitAt 19 rest+rapist is a danger++-}+splitAt :: Monad m => Int64 -> ByteStream m r -> ByteStream m (ByteStream m r)+splitAt i cs0 | i <= 0 = Empty cs0+splitAt i cs0 = go i cs0+  where go 0 cs             = Empty cs+        go _ (Empty r)      = Empty (Empty r)+        go n (Chunk c o cs) =+          if n < fromIntegral (B.length c)+            then Chunk (B.take (fromIntegral n) c) o $+                     Empty (Chunk (B.drop (fromIntegral n) c) (o+n) cs)+            else Chunk c o (go (n - fromIntegral (B.length c)) cs)+        go n (Go m) = Go (liftM (go n) m)+{-# INLINABLE splitAt #-}++-- | Strictly splits off a piece.  This breaks streaming, so reserve its+-- use for small strings or when conversion to strict 'Bytes' is needed+-- anyway.+splitAt' :: Monad m => Int -> ByteStream m r -> m (Of Bytes (ByteStream m r))+splitAt' i cs0 | i <= 0 = return $! B.empty :> cs0+splitAt' i cs0 = go i [] cs0+  where go 0 acc cs             = return $! B.concat (reverse acc) :> cs+        go _ acc (Empty r)      = return $! B.concat (reverse acc) :> Empty r+        go n acc (Chunk c o cs) =+          if n < B.length c+            then return $! B.concat (reverse (B.take n c : acc))+                        :> Chunk (B.drop n c) (o + fromIntegral n) cs+            else go (n - B.length c) (c:acc) cs+        go n acc (Go m) = m >>= go n acc+{-# INLINABLE splitAt' #-}++trim :: Monad m => Int64 -> ByteStream m () -> ByteStream m ()+trim eoff = go+  where+    go (Empty     _)             = Empty ()+    go (Go        m)             = lift m >>= go+    go (Chunk c o s) | o <  eoff = Chunk c o (go s)+                     | otherwise = Empty ()+{-# INLINABLE trim #-}++-- | 'dropWhile' @p xs@ returns the suffix remaining after 'takeWhile' @p xs@.+dropWhile :: Monad m => (Word8 -> Bool) -> ByteStream m r -> ByteStream m r+dropWhile pr = drop' where+  drop' bs = case bs of+    Empty r      -> Empty r+    Go m         -> Go (liftM drop' m)+    Chunk c o cs -> case findIndexOrEnd (not.pr) c of+        0                  -> Chunk c o cs+        n | n < B.length c -> Chunk (B.drop n c) (o + fromIntegral n) cs+          | otherwise      -> drop' cs+{-# INLINABLE dropWhile #-}++-- | 'break' @p@ is equivalent to @'span' ('not' . p)@.+break :: Monad m => (Word8 -> Bool) -> ByteStream m r -> ByteStream m (ByteStream m r)+break f cs0 = break' cs0+  where break' (Empty r)        = Empty (Empty r)+        break' (Chunk c o cs) =+          case findIndexOrEnd f c of+            0                  -> Empty (Chunk c o cs)+            n | n < B.length c -> Chunk (B.take n c) o $+                                      Empty (Chunk (B.drop n c) (o + fromIntegral n) cs)+              | otherwise      -> Chunk c o (break' cs)+        break' (Go m) = Go (liftM break' m)+{-# INLINABLE break #-}++{- | Read entire handle contents /lazily/ into a 'ByteStream'. Chunks+    are read on demand, in at most @k@-sized chunks. It does not block+    waiting for a whole @k@-sized chunk, so if less than @k@ bytes are+    available then they will be returned immediately as a smaller chunk.++    The handle is closed on EOF.++    Note: the 'Handle' should be placed in binary mode with+    'System.IO.hSetBinaryMode' for 'hGetContentsN' to+    work correctly.+-}+hGetContentsN :: MonadIO m => Int -> Handle -> ByteStream m ()+hGetContentsN k h = loop 0+  where+    loop !o = do+        c <- liftIO (B.hGetSome h k)+        -- only blocks if there is no data available+        if B.null c+          then Empty ()+          else Chunk c o (loop (o + fromIntegral (B.length c)))+{-# INLINABLE hGetContentsN #-} -- very effective inline pragma++{-| Read entire handle contents /lazily/ into a 'ByteStream'. Chunks+    are read on demand, using the default chunk size.++    Note: the 'Handle' should be placed in binary mode with+    'System.IO.hSetBinaryMode' for 'hGetContents' to+    work correctly.+-}+hGetContents :: MonadIO m => Handle -> ByteStream m ()+hGetContents = hGetContentsN defaultChunkSize+{-# INLINE hGetContents #-}+++withOutputFile :: (MonadIO m, MonadMask m) => FilePath -> (Handle -> m a) -> m a+withOutputFile "-" k = k stdout+withOutputFile  f  k = bracket (liftIO $ openBinaryFile (f++".#~#") WriteMode) (liftIO . hClose) $ \hdl ->+                       k hdl >>= \r -> liftIO (renameFile (f++".#~#") f) >> return r+{-# INLINE withOutputFile #-}++{-| Writes a 'ByteStream' to a file.  Actually writes to a temporary+    file and renames it on successful completion.  The filename \"-\"+    causes it to write to stdout instead.+ -}+writeFile :: (MonadIO m, MonadMask m) => FilePath -> ByteStream m r -> m r+writeFile f str = withOutputFile f $ \hdl -> hPut hdl str+{-# INLINE writeFile #-}++-- | Outputs a 'ByteStream' to the specified 'Handle'.+hPut ::  MonadIO m => Handle -> ByteStream m r -> m r+hPut h cs = dematerialize cs return (\x _ y -> liftIO (B.hPut h x) >> y) (>>= id)+{-# INLINE hPut #-}++-- -- ---------------------------------------------------------------------+-- -- Internal utilities++-- | 'findIndexOrEnd' is a variant of findIndex, that returns the length+-- of the string if no element is found, rather than Nothing.+findIndexOrEnd :: (Word8 -> Bool) -> Bytes -> Int+findIndexOrEnd k (B.PS x s l) =+    unsafeDupablePerformIO $+    withForeignPtr x $ \f -> go (f `plusPtr` s) 0+  where+    go !ptr !n | n >= l    = return l+               | otherwise = do w <- peek ptr+                                if k w+                                  then return n+                                  else go (ptr `plusPtr` 1) (n+1)+{-# INLINABLE findIndexOrEnd #-}++{- Take a builder constructed otherwise and convert it to a genuine+   streaming bytestring.++>>>  Q.putStrLn $ Q.toByteStream $ stringUtf8 "哈斯克尔" <> stringUtf8 " " <> integerDec 98+哈斯克尔 98++    <https://gist.github.com/michaelt/6ea89ca95a77b0ef91f3 This benchmark> shows its+    indistinguishable performance is indistinguishable from @toLazyByteStream@+-}++toByteStream :: MonadIO m => Builder -> ByteStream m ()+toByteStream = toByteStreamWith (safeStrategy L.smallChunkSize L.defaultChunkSize)+{-# INLINE toByteStream #-}++{-| Take a builder and convert it to a genuine+   streaming bytestring, using a specific allocation strategy.+-}+toByteStreamWith :: MonadIO m => AllocationStrategy -> Builder -> ByteStream m ()+toByteStreamWith strategy builder0 = do+       cios <- liftIO (buildStepToCIOS strategy (runBuilder builder0))+       let loop !o cios0 = case cios0 of+              Yield1 bs io   -> Chunk bs o $ do+                    cios1 <- liftIO io+                    loop (o + fromIntegral (B.length bs)) cios1+              Finished buf r -> trimmedChunkFromBuffer o buf (Empty r)+           trimmedChunkFromBuffer o buffer k+              | B.null bs                            = k+              |  2 * B.length bs < bufferSize buffer = Chunk (B.copy bs) o k+              | otherwise                            = Chunk bs          o k+              where+                bs = byteStringFromBuffer buffer+       loop 0 cios+{-# INLINABLE toByteStreamWith #-}+{-# SPECIALIZE toByteStreamWith :: AllocationStrategy -> Builder -> ByteStream IO () #-}+++{- Concatenate a stream of builders (not a streaming bytestring!) into a single builder.++>>> let aa = yield (integerDec 10000) >> yield (string8 " is a number.") >> yield (char8 '\n')+>>>  hPutBuilder  IO.stdout $ concatBuilders aa+10000 is a number.++-}+concatBuilders :: Stream (Of Builder) IO () -> Builder+concatBuilders p = builder $ \bstep r -> do+  case p of+    Return _          -> runBuilderWith mempty bstep r+    Step (b :> rest)  -> runBuilderWith (b `mappend` concatBuilders rest) bstep r+    Effect m            -> m >>= \p' -> runBuilderWith (concatBuilders p') bstep r+{-# INLINABLE concatBuilders #-}++{- | Turns a ByteStream into a connected stream of ByteStreams that+     divide at newline characters. The resulting strings do not contain+     newlines.  This is the genuinely streaming 'lines' which only+     breaks chunks, and thus never increases the use of memory.++     Because 'ByteStream's are usually read in binary mode, with no line+     ending conversion, this function recognizes both @\\n@ and @\\r\\n@+     endings (regardless of the current platform). -}++lines :: Monad m => ByteStream m r -> Stream (ByteStream m) m r+lines text0 = loop1 text0+  where+    loop1 :: Monad m => ByteStream m r -> Stream (ByteStream m) m r+    loop1 text =+      case text of+        Empty r -> Return r+        Go m -> Effect $ liftM loop1 m+        Chunk c _ cs+          | B.null c -> loop1 cs+          | otherwise -> Step (loop2 Nothing text)+    loop2 :: Monad m => Maybe Int64 -> ByteStream m r -> ByteStream m (Stream (ByteStream m) m r)+    loop2 prevCr text =+      case text of+        Empty r -> case prevCr of+          Just  o -> Chunk (B.singleton 13) o (Empty (Return r))+          Nothing -> Empty (Return r)+        Go m -> Go $ liftM (loop2 prevCr) m+        Chunk c o cs ->+          case B.elemIndex 10 c of+            Nothing -> case B.length c of+              0 -> loop2 prevCr cs+              l -> if B.unsafeLast c == 13+                     then Chunk (B.unsafeInit c) o (loop2 (Just (o-1 + fromIntegral l)) cs)+                     else Chunk c o (loop2 Nothing cs)+            Just i -> do+              let prefixLength =+                    if i >= 1 && B.unsafeIndex c (i-1) == 13 -- \r\n (dos)+                      then i-1+                      else i+                  rest =+                    if B.length c > i+1+                      then Chunk (B.drop (i+1) c) (o+1 + fromIntegral i) cs+                      else cs+                  result = Chunk (B.unsafeTake prefixLength c) o (Empty (loop1 rest))+              case prevCr of+                Just oo | i > 0 -> Chunk (B.singleton 13) oo result+                _               -> result+{-# INLINABLE lines #-}++{- | Turns a 'ByteStream' into a stream of strict 'Bytes' that divide at+     newline characters. The resulting strings do not contain newlines.+     This will cost memory if the lines are very long, and it does not+     recognize DOS line endings. -}++lines' :: Monad m => ByteStream m r -> Stream (Of Bytes) m r+lines' = loop1 []+  where+    loop1 :: Monad m => [Bytes] -> ByteStream m r -> Stream (Of Bytes) m r+    loop1 acc text =+      case text of+        Empty r -> Return r+        Go m    -> Effect $ liftM (loop1 acc) m+        Chunk c o cs+          | B.null c  -> loop1 acc cs+          | otherwise ->+              case B.elemIndex 10 c of+                Just  i -> Q.cons (if null acc then B.take i c else B.concat (reverse (B.take i c : acc)))+                                  (loop1 [] (Chunk (B.drop (i+1) c) (o+1 + fromIntegral i) cs))+                Nothing -> loop1 (c:acc) cs+{-# INLINABLE lines' #-}++-- --------------------------------------------------------------------------++{-| Decompresses GZip if present.  If any GZip stream is found, all+    such streams are decompressed and any remaining data is discarded.+    Else, the input is returned unchanged.  If the input is BGZF, the+    result will contain meaningful virtual offsets.  If the input+    contains exactly one GZip stream, the result will have meaningfull+    offsets into the uncompressed data.  Else, the offsets will be+    bogus. -}++gunzip :: MonadIO m => ByteStream m r -> ByteStream m r+gunzip = gunzipWith id+{-# INLINABLE gunzip #-}++{-| Checks if the input is GZip at all, and runs gunzip if it is.  If+    it isn't, it runs 'k' on the input. -}++gunzipWith :: MonadIO m => (ByteStream m r -> ByteStream m r)+                        -> ByteStream m r -> ByteStream m r+gunzipWith k s0 = lift (nextByteOff s0) >>= \case+    Right (31, o, s') -> lift (nextByte s') >>= \case+        Right (139,s'') -> gunzipLoop o $ Chunk (B.pack [31,139]) o s''+        Right ( c, s'') -> k $ Chunk (B.pack [31,c]) o s''+        Left     r      -> k $ Chunk (B.singleton 31) o (pure r)+    Right ( c, o, s')   -> k $ Chunk (B.singleton c) o s'+    Left       r        -> k $ pure r+{-# INLINABLE gunzipWith #-}++{-| Decompresses a gzip stream.  If the leftovers look like another+    gzip stream, it recurses (some files, notably those produced by+    bgzip, contain multiple streams).  Otherwise, the leftovers are+    discarded (some compressed HETFA files appear to have junk at the+    end). -}++gunzipLoop :: MonadIO m => Int64 -> ByteStream m r -> ByteStream m r+gunzipLoop o = go o (shiftL o 16) $ Z.decompressIO Z.gzipOrZlibFormat Z.defaultDecompressParams+  where+    -- get next chunk, make sure it is empty iff the input ended+    go inoff outoff (Z.DecompressInputRequired next) inp =+        lift (nextChunk inp) >>= \case+            Left r          -> do z <- liftIO (next B.empty)+                                  go inoff outoff z (pure r)+            Right (ck,inp')+                | B.null ck ->    go inoff outoff (Z.DecompressInputRequired next) inp'+                | otherwise -> do z <- liftIO (next ck)+                                  go (inoff + fromIntegral (B.length ck)) outoff z inp'++    go inoff outoff (Z.DecompressOutputAvailable outchunk next) inp = do+        z <- Chunk outchunk outoff (liftIO next)+        go inoff (outoff + fromIntegral (B.length outchunk)) z inp++    go inoff _outoff (Z.DecompressStreamEnd inchunk) inp =+        -- decompress leftovers if possible, else return+        gunzipWith (lift . effects) (Chunk inchunk (inoff - fromIntegral (B.length inchunk)) inp)++    go _inoff _outoff (Z.DecompressStreamError derr) _inp =+        liftIO $ throwIO derr++-- | Compresses a byte stream using GZip with default parameters.+gzip :: MonadIO m => ByteStream m r -> ByteStream m r+gzip = go $ Z.compressIO Z.gzipFormat Z.defaultCompressParams+  where+    -- get next chunk, make sure it is empty iff the input ended+    go (Z.CompressInputRequired next) inp =+        lift (nextChunk inp) >>= \case+            Left r          -> liftIO (next B.empty) >>= flip go (pure r)+            Right (ck,inp')+                | B.null ck -> go (Z.CompressInputRequired next) inp'+                | otherwise -> liftIO (next ck) >>= flip go inp'++    go (Z.CompressOutputAvailable outchunk next) inp =+        chunk outchunk >> liftIO next >>= flip go inp++    go Z.CompressStreamEnd inp = lift (effects inp)++
+ Bio/Streaming/Furrow.hs view
@@ -0,0 +1,45 @@+module Bio.Streaming.Furrow+    ( Furrow(..)+    , evertStream+    , afford+    , drain+    ) where++import Bio.Prelude+import Bio.Streaming++{- | A tiny stream that can be afforded to incrementally.++The streaming abstraction works fine if multiple sources feed into a+small constant number of functions, but fails if there is an+unpredictable number of such consumers.  In that case, 'evertStream'+should be used to turn each consumer into a 'Furrow'.  It's then+possible to incrementally 'afford' stuff to each 'Furrow' in a+collection in a simple loop.  To get the final value, 'drain' each+'Furrow'.+-}+newtype Furrow a m r = Furrow (Stream ((->) (Maybe a)) m r) deriving+  (Functor, Applicative, Monad, MonadTrans, MonadIO, MFunctor, MMonad)++instance MonadThrow m => MonadThrow (Furrow a m) where+    throwM = Furrow . lift . throwM++afford :: Monad m => Furrow a m b -> a -> m (Furrow a m b)+afford (Furrow s) a = inspect s >>= \case+    Left  b -> return (Furrow (pure b))+    Right f -> return (Furrow (f (Just a)))++drain :: Monad m => Furrow a m b -> m b+drain (Furrow s) = inspect s >>= \case+    Left  b -> return b+    Right f -> inspect (f Nothing) >>= \case+        Left  b -> return b+        Right _ -> error "continuedAfterEOF"++-- | Turns a function that consumes a stream into a furrow.  Idea and+-- some code stolen from \"streaming-eversion\".+evertStream :: Monad m => (Stream (Of a) (Furrow a m) () -> Furrow a m b) -> Furrow a m b+evertStream consumer = consumer cat+  where+    cat = lift (Furrow (yields id)) >>= maybe (pure ()) (\a -> wrap (a :> cat))+
+ Bio/Streaming/Parse.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE Rank2Types #-}+module Bio.Streaming.Parse+    ( Parser+    , ParseError(..)+    , EofException(..)+    , parse+    , parseIO+    , parseM+    , abortParse+    , isFinished+    , drop+    , dropLine+    , getByte+    , getString+    , getWord32+    , getWord64+    , isolate+    , atto+    ) where++-- ^ Parsers for use with 'ByteStream's.++import Bio.Prelude                       hiding ( drop )+import Bio.Streaming.Bytes                      ( ByteStream )++import qualified Bio.Streaming.Bytes            as S+import qualified Data.Attoparsec.ByteString     as A+import qualified Data.ByteString                as B+import qualified Streaming.Prelude              as Q++newtype Parser r m a = P {+    runP :: forall x .+            (a -> ByteStream m r -> m x)+         -> (r -> m x)+         -> (SomeException -> m x)+         -> ByteStream m r -> m x }++instance Functor (Parser r m) where+    fmap f p = P $ \sk -> runP p (sk . f)++instance Applicative (Parser r m) where+    pure a = P $ \sk _rk _ek -> sk a+    a <*> b = P $ \sk rk ek -> runP a (\f -> runP b (\x -> sk (f x)) rk ek) rk ek++instance Monad (Parser r m) where+    return = pure+    m >>= k = P $ \sk rk ek -> runP m (\a -> runP (k a) sk rk ek) rk ek++instance MonadIO m => MonadIO (Parser r m) where+    liftIO m = P $ \sk _rk _ek s -> liftIO m >>= \a -> sk a s++instance MonadTrans (Parser r) where+    lift m = P $ \sk _rk _ek s -> m >>= \a -> sk a s++instance MonadThrow (Parser r m) where+    throwM e = P $ \_sk _rk ek _s -> ek (toException e)++modify :: (ByteStream m r -> ByteStream m r) -> Parser r m ()+modify f = P $ \sk _rk _ek -> sk () . f++parse :: Monad m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either SomeException (Either r (a, ByteStream m r)))+parse p = go+  where+    go    (S.Empty     r)             = return $ Right $ Left r+    go    (S.Go        k)             = k >>= go+    go ck@(S.Chunk c o s) | B.null  c = go s+                          | otherwise = runP (p o) (\a t -> return . Right $ Right (a,t))+                                                   (return . Right . Left)+                                                   (return . Left)+                                                   ck++parseIO :: MonadIO m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))+parseIO p = parse p >=> either (liftIO . throwM) return++parseM :: MonadThrow m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))+parseM p = parse p >=> either throwM return++abortParse :: Monad m => Parser r m a+abortParse = P $ \_sk rk _ek -> S.effects >=> rk++liftFun :: Monad m => (ByteStream m r -> m (a, ByteStream m r)) -> Parser r m a+liftFun f = P $ \sk _rk _ek -> f >=> uncurry sk++isFinished :: Monad m => Parser r m Bool+isFinished = liftFun go+  where+    go    (S.Empty     r)             = return (True, S.Empty r)+    go    (S.Go        k)             = k >>= go+    go ck@(S.Chunk c _ s) | B.null  c = go s+                          | otherwise = return (False, ck)++drop :: Monad m => Int -> Parser r m ()+drop l = modify $ S.drop (fromIntegral l)++dropLine :: Monad m => Parser r m ()+dropLine = modify $ S.drop 1 . S.dropWhile (/= 10)++getByte :: Monad m => Parser r m Word8+getByte = P $ \sk _rk ek -> S.nextByte >=> either (const $ ek (toException EofException)) (uncurry sk)++getString :: Monad m => Int -> Parser r m B.ByteString+getString l = liftFun $ liftM Q.lazily . S.splitAt' l++getWord32 :: Monad m => Parser r m Word32+getWord32 = liftM (fst . B.foldl (\(a,i) w -> (a + shiftL (fromIntegral w) i, i + 8)) (0,0)) (getString 4)++getWord64 :: Monad m => Parser r m Word64+getWord64 = liftM (fst . B.foldl (\(a,i) w -> (a + shiftL (fromIntegral w) i, i + 8)) (0,0)) (getString 8)++isolate :: Monad m => Int -> Parser (ByteStream m r) m a -> Parser r m a+isolate l p = P $ \sk rk ek -> runP p (\a -> S.effects >=> sk a)+                                      (S.effects >=> rk)+                                      ek . S.splitAt (fromIntegral l)++data ParseError = ParseError {errorContexts :: [String], errorMessage :: String}+    deriving (Show, Typeable)++data EofException = EofException+    deriving (Show, Typeable)++instance Exception ParseError+instance Exception EofException++atto :: Monad m => A.Parser a -> Parser r m a+atto = go . A.parse+  where+    go k = P $ \sk rk ek ->+        S.nextChunk >=> \case+            Left r -> case k B.empty of+                      A.Fail _ err dsc -> ek $ toException (ParseError err dsc)+                      A.Partial _      -> ek $ toException EofException+                      A.Done rest v    -> sk v (S.consChunk rest (pure r))+            Right (c,s')+                | B.null c -> runP (go k) sk rk ek s'+                | otherwise -> case k c of+                      A.Fail _ err dsc -> ek $ toException (ParseError err dsc)+                      A.Partial k'     -> runP (go k') sk rk ek s'+                      A.Done rest v    -> sk v (S.consChunk rest s')++
+ Bio/Streaming/Vector.hs view
@@ -0,0 +1,35 @@+module Bio.Streaming.Vector ( stream2vector, stream2vectorN ) where++import Bio.Prelude+import Streaming++import qualified Data.Vector.Generic            as VG+import qualified Data.Vector.Generic.Mutable    as VM++-- | Equivalent to @stream2vector . Streaming.Prelude.take n@, but+-- terminates early and is thereby more efficient.+stream2vectorN :: (MonadIO m, VG.Vector v a) => Int -> Stream (Of a) m () -> m (v a)+stream2vectorN n s0 = do+    mv <- liftIO $ VM.new n+    go mv 0 s0+  where+    go !mv !i s+        | i == n    = liftIO $ VG.unsafeFreeze mv+        | otherwise =+            inspect s >>= \case+                Left        ()  -> liftIO $ VG.unsafeFreeze $ VM.take i mv+                Right (a :> s') -> liftIO (VM.write mv i a) >> go mv (i+1) s'++-- | Reads the whole stream into a 'VG.Vector'.+stream2vector :: (MonadIO m, VG.Vector v a) => Stream (Of a) m r -> m (Of (v a) r)+stream2vector s0 = do+    mv <- liftIO $ VM.new 1024+    go mv 0 s0+  where+    go !mv !i =+        inspect >=> \case+            Left        r  -> liftM (:> r) $ liftIO $ VG.unsafeFreeze $ VM.take i mv+            Right (a :> s) -> do mv' <- if VM.length mv == i then liftIO (VM.grow mv (VM.length mv)) else return mv+                                 liftIO $ VM.write mv' i a+                                 go mv' (i+1) s+
+ Bio/TwoBit.hs view
@@ -0,0 +1,296 @@+-- | Would you believe it?  The 2bit format stores blocks of Ns in a table at+-- the beginning of a sequence, then packs four bases into a byte.  So it+-- is neither possible nor necessary to store Ns in the main sequence, and+-- you would think they aren't stored there, right?  And they aren't.+-- Instead Ts are stored which the reader has to replace with Ns.+--+-- The sensible way to treat these is probably to just say there are two+-- kinds of implied annotation (repeats and large gaps for a typical+-- genome), which can be interpreted in whatever way fits.  And that's why+-- we have 'Mask' and 'getSubseqWith'.++module Bio.TwoBit (+        TwoBitFile(..),+        TwoBitSequence(..),+        openTwoBit,++        getFwdSubseqWith,+        getSubseq,+        getSubseqWith,+        getSubseqAscii,+        getSubseqMasked,+        getLazySubseq,+        getFragment,+        getFwdSubseqV,+        getSeqnames,+        lookupSequence,+        getSeqLength,+        clampPosition,+        getRandomSeq,++        takeOverlap,+        mergeBlocks,+        Mask(..)+    ) where++import           Bio.Prelude hiding ( left, right, chr )+import           Bio.Util.MMap+import           Bio.Util.Storable+import           Control.Monad.Trans.State ( StateT(..), get, evalStateT )+import qualified Data.ByteString                as B+import qualified Data.ByteString.Unsafe         as B+import qualified Data.IntMap.Strict             as I+import qualified Data.HashMap.Lazy              as M+import qualified Data.Vector.Unboxed            as U+import           Foreign.C.Types ( CChar )++data TwoBitFile = TBF {+    tbf_raw :: B.ByteString,+    -- This map is intentionally lazy.  May or may not be important.+    tbf_seqs :: !(M.HashMap Bytes TwoBitSequence)+}++data TwoBitSequence = TBS { tbs_n_blocks   :: !(I.IntMap Int)+                          , tbs_m_blocks   :: !(I.IntMap Int)+                          , tbs_dna_offset :: {-# UNPACK #-} !Int+                          , tbs_dna_size   :: {-# UNPACK #-} !Int }++-- | Brings a 2bit file into memory.  The file is mmap'ed, so it will+-- not work on streams that are not actual files.  It's also unsafe if+-- the file is modified in any way.+openTwoBit :: FilePath -> IO TwoBitFile+openTwoBit fp = do+        raw <- unsafeMMapFile fp+        B.unsafeUseAsCString raw $ \praw ->+        -- return $ flip runGet (L.fromChunks [raw]) $ do+            flip evalStateT praw $ do+                    sig <- getWord32be+                    getWord32 <- case sig :: Word32 of+                            0x1A412743 -> return getWord32be+                            0x4327411A -> return getWord32le+                            _          -> fail $ "invalid .2bit signature " ++ showHex sig []++                    version <- getWord32+                    unless (version == 0) $ fail $ "wrong .2bit version " ++ show version++                    nseqs <- getWord32+                    _reserved <- getWord32++                    TBF raw <$> foldM (\ix _ -> do !key <- getWord8 >>= getByteString+                                                   !off <- getWord32+                                                   return $! M.insert key (mkBlockIndex raw getWord32 off) ix+                                      ) M.empty [1..nseqs]++type Get = StateT (Ptr CChar) IO++getWord8, getWord32be, getWord32le :: Num a => Get a+getWord8    = StateT $ \p -> peekUnalnWord32BE  p >>= \w -> return (fromIntegral w, plusPtr p 1)+getWord32be = StateT $ \p -> peekUnalnWord32BE  p >>= \w -> return (fromIntegral w, plusPtr p 4)+getWord32le = StateT $ \p -> peekUnalnWord32LE  p >>= \w -> return (fromIntegral w, plusPtr p 4)++getByteString :: Int -> Get Bytes+getByteString l = StateT $ \p -> B.packCStringLen (p,l) >>= \s -> return (s, plusPtr p l)++mkBlockIndex :: B.ByteString -> Get Int -> Int -> TwoBitSequence+mkBlockIndex raw getWord32 ofs =+    unsafePerformIO $+    B.unsafeUseAsCString raw $ \praw ->+    evalStateT getBlock (plusPtr praw ofs)+  where+    getBlock = do p0 <- get+                  ds <- getWord32+                  nb <- readBlockList+                  mb <- readBlockList+                  _  <- getWord32+                  p1 <- get+                  return $! TBS (I.fromList nb) (I.fromList mb) (ofs + minusPtr p1 p0) ds++    readBlockList = getWord32 >>= \n -> liftM2 zip (repM n getWord32) (repM n getWord32)++-- | Repeat monadic action @n@ times.  Returns result in reverse(!)+-- order, but doesn't build a huge list of thunks in memory.+repM :: Monad m => Int -> m a -> m [a]+repM n0 m = go [] n0+  where+    go acc 0 = return acc+    go acc n = m >>= \x -> x `seq` go (x:acc) (n-1)++takeOverlap :: Int -> I.IntMap Int -> [(Int,Int)]+takeOverlap k m = dropWhile far_left $+                  maybe id (\(kv,_) -> (:) kv) (I.maxViewWithKey left) $+                  maybe id (\v -> (:) (k,v)) middle $+                  I.toAscList right+  where+    (left, middle, right) = I.splitLookup k m+    far_left (s,l) = s+l <= k++data Mask = None | Soft | Hard | Both deriving (Eq, Ord, Enum, Show)++getFwdSubseqWith :: TwoBitFile -> TwoBitSequence                -- raw data, sequence+                 -> (Word8 -> Mask -> a)                        -- mask function+                 -> Int -> [a]                                  -- start, lazy result+getFwdSubseqWith TBF{..} TBS{..} nt start =+    do_mask (takeOverlap start tbs_n_blocks `mergeBlocks` takeOverlap start tbs_m_blocks) start .+    drop (start .&. 3) .+    B.foldr toDNA [] .+    B.drop (tbs_dna_offset + (start `shiftR` 2)) $ tbf_raw+  where+    toDNA b = (++) [ 3 .&. (b `shiftR` x) | x <- [6,4,2,0] ]++    do_mask            _ _ [] = []+    do_mask [          ] _ ws = map (`nt` None) ws+    do_mask ((s,l,m):is) p ws+        | p < s     = map (`nt` None) (take  (s-p)  ws) ++ do_mask ((s,l,m):is)  s   (drop  (s-p)  ws)+        | otherwise = map (`nt`    m) (take (s+l-p) ws) ++ do_mask          is (s+l) (drop (s+l-p) ws)++-- | Merge blocks of Ns and blocks of Ms into single list of blocks with+-- masking annotation.  Gaps remain.  Used internally only.+mergeBlocks :: [(Int,Int)] -> [(Int,Int)] -> [(Int,Int,Mask)]+mergeBlocks ((_,0):nbs) mbs = mergeBlocks nbs mbs+mergeBlocks nbs ((_,0):mbs) = mergeBlocks nbs mbs++mergeBlocks ((ns,nl):nbs) ((ms,ml):mbs)+    | ns < ms   = let l = min (ms-ns) nl in (ns,l, Hard) : mergeBlocks ((ns+l,nl-l):nbs) ((ms,ml):mbs)+    | ms < ns   = let l = min (ns-ms) ml in (ms,l, Soft) : mergeBlocks ((ns,nl):nbs) ((ms+l,ml-l):mbs)+    | otherwise = let l = min nl ml in (ns,l, Both) : mergeBlocks ((ns+l,nl-l):nbs) ((ms+l,ml-l):mbs)++mergeBlocks ((ns,nl):nbs) [] = (ns,nl, Hard) : mergeBlocks nbs []+mergeBlocks [] ((ms,ml):mbs) = (ms,ml, Soft) : mergeBlocks [] mbs++mergeBlocks [     ] [     ] = []+++-- | Extract a subsequence and apply masking.  TwoBit file can represent+-- two kinds of masking (hard and soft), where hard masking is usually+-- realized by replacing everything by Ns and soft masking is done by+-- lowercasing.  Here, we take a user supplied function to apply+-- masking.+getSubseqWith :: (Nucleotide -> Mask -> a) -> TwoBitFile -> Range -> [a]+getSubseqWith maskf tbf Range{ r_pos = Pos { p_seq = chr, p_start = start }, r_length = len } = do+    let sq1 = fromMaybe (error $ unpack chr ++ " doesn't exist") $ M.lookup chr (tbf_seqs tbf)+    let go = getFwdSubseqWith tbf sq1+    if start < 0+        then reverse $ take len $ go (maskf . cmp_nt) (-start-len)+        else           take len $ go (maskf . fwd_nt)   start+  where+    fwd_nt = (!!) [nucT, nucC, nucA, nucG] . fromIntegral+    cmp_nt = (!!) [nucA, nucG, nucT, nucC] . fromIntegral++-- | Works only in forward direction.+getLazySubseq :: TwoBitFile -> Position -> [Nucleotide]+getLazySubseq tbf Pos{ p_seq = chr, p_start = start } = do+    let sq1 = fromMaybe (error $ unpack chr ++ " doesn't exist") $ M.lookup chr (tbf_seqs tbf)+    let go  = getFwdSubseqWith tbf sq1+    if start < 0+        then error "sorry, can't go backwards"+        -- then reverse $ take len $ go (maskf . cmp_nt) (-start-len)+        else go fwd_nt start+  where+    fwd_nt n _ = [nucT, nucC, nucA, nucG] !! fromIntegral n+++-- | Extract a subsequence without masking.+getSubseq :: TwoBitFile -> Range -> [Nucleotide]+getSubseq = getSubseqWith const++-- | Extract a subsequence with typical masking:  soft masking is+-- ignored, hard masked regions are replaced with Ns.+getSubseqMasked :: TwoBitFile -> Range -> [Nucleotides]+getSubseqMasked = getSubseqWith mymask+  where+    mymask n None = nucToNucs n+    mymask n Soft = nucToNucs n+    mymask _ Hard = nucsN+    mymask _ Both = nucsN++-- | Extract a subsequence with masking for biologists:  soft masking is+-- done by lowercasing, hard masking by printing an N.+getSubseqAscii :: TwoBitFile -> Range -> String+getSubseqAscii = getSubseqWith mymask+  where+    mymask n None = showNucleotide n+    mymask n Soft = toLower (showNucleotide n)+    mymask _ Hard = 'N'+    mymask _ Both = 'N'+++getSeqnames :: TwoBitFile -> [Bytes]+getSeqnames = M.keys . tbf_seqs++lookupSequence :: TwoBitFile -> Bytes -> Maybe TwoBitSequence+lookupSequence tbf sq = M.lookup sq . tbf_seqs $ tbf++getSeqLength :: TwoBitFile -> Bytes -> Int+getSeqLength tbf chr =+    maybe (error $ shows chr " doesn't exist") tbs_dna_size $+    M.lookup chr (tbf_seqs tbf)++-- | limits a range to a position within the actual sequence+clampPosition :: TwoBitFile -> Range -> Range+clampPosition tbf (Range (Pos n start) len) = Range (Pos n start') (end' - start')+  where+    size   = getSeqLength tbf n+    start' = if start < 0 then max start (-size) else start+    end'   = min (start + len) $ if start < 0 then 0 else size+++-- | Sample a piece of random sequence uniformly from the genome.+-- Only pieces that are not hard masked are sampled, soft masking is+-- allowed, but not reported.+-- On a 32bit platform, this will fail for genomes larger than 1G bases.+-- However, if you're running this code on a 32bit platform, you have+-- bigger problems to worry about.+getRandomSeq :: TwoBitFile                   -- ^ 2bit file+             -> Int                          -- ^ desired length+             -> (Int -> g -> (Int, g))       -- ^ draw random int below limit+             -> g                            -- ^ RNG+             -> ((Range, [Nucleotide]), g)   -- ^ position, sequence, new RNG+getRandomSeq tbf len rndInt = draw+  where+    names = getSeqnames tbf+    lengths = map (getSeqLength tbf) names+    total = sum lengths+    frags = I.fromList $ zip (scanl (+) 0 lengths) names++    draw g0 | good      = ((r', sq), gn)+            | otherwise = draw gn+      where+        (p0, gn) = rndInt (2*total) g0+        p = p0 `shiftR` 1+        (o,s) = fst $ fromJust $ I.maxViewWithKey $ fst $ I.split (p+1) frags+        r' = (if odd p0 then id else reverseRange) $ clampPosition tbf $ Range (Pos s (p-o)) len+        sq = catMaybes $ getSubseqWith mask2maybe tbf r'+        good = r_length r' == len && length sq == len++        mask2maybe n None = Just n+        mask2maybe n Soft = Just n+        mask2maybe _ Hard = Nothing+        mask2maybe _ Both = Nothing++-- | Gets a fragment from a 2bit file.  The result always has the+-- desired length; if necessary, it is padded with Ns.  Be careful about+-- the unconventional encoding: 0..4 == TCAGN+getFragment :: TwoBitFile -> Bytes -> Int -> Int -> U.Vector Word8+getFragment tbf chr p l =+    case lookupSequence tbf chr of+        Nothing  -> U.replicate l 4+        Just tbs -> getFwdSubseqV tbf tbs p l++-- Careful about weird encoding: 0..4 == TCAGN+getFwdSubseqV :: TwoBitFile -> TwoBitSequence -> Int -> Int -> U.Vector Word8+getFwdSubseqV TBF{..} TBS{..} start len = U.unfoldrN len step ini+  where+    ini = (start, takeOverlap start tbs_n_blocks)++    step (off, nbs)+        | off < 0                   = Just (4, (succ off, nbs))+        | off >= tbs_dna_size       = Just (4, (succ off, nbs))+        | otherwise = case nbs of+            [        ]             -> Just (y, (succ off, [ ]))+            (s,l):nbs' | off < s   -> Just (y, (succ off, nbs))+                       | off < s+l -> Just (4, (succ off, nbs))+                       | otherwise -> Just (y, (succ off, nbs'))+      where+        x = B.index tbf_raw (tbs_dna_offset + off `shiftR` 2)+        y = x `shiftR` (6 - 2 * (off .&. 3)) .&. 3     -- T,C,A,G+
+ Bio/Util/MMap.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Bio.Util.MMap ( unsafeMMapFile ) where++import BasePrelude+import Data.ByteString.Internal ( fromForeignPtr, ByteString )+import Foreign.C.Types+import System.Posix.Files+import System.Posix.IO++unsafeMMapFile :: FilePath -> IO ByteString+unsafeMMapFile fp =+    bracket (openFd fp ReadOnly Nothing defaultFileFlags) closeFd $ \fd -> do+        stat <- getFdStatus fd+        let size = fromIntegral (fileSize stat)+        if size <= 0+            then return mempty+            else do+                ptr <- c_mmap size (fromIntegral fd)+                if ptr == nullPtr+                    then error "unable to mmap file"+                    else do+                          fptr <- newForeignPtrEnv c_munmap (intPtrToPtr $ fromIntegral size) ptr+                          return $ fromForeignPtr fptr 0 (fromIntegral size)++foreign import ccall unsafe  "my_mmap"   c_mmap   :: CSize -> CInt -> IO (Ptr Word8)+foreign import ccall unsafe "&my_munmap" c_munmap :: FunPtr (Ptr () -> Ptr Word8 -> IO ())++
+ Bio/Util/Nub.hs view
@@ -0,0 +1,16 @@+module Bio.Util.Nub ( nubHash, nubHashBy )where++import BasePrelude+import Data.Hashable ( Hashable )+import qualified Data.HashSet as H++nubHash :: (Hashable a, Eq a) => [a] -> [a]+nubHash = nubHashBy id++nubHashBy :: (Hashable b, Eq b) => (a -> b) -> [a] -> [a]+nubHashBy f = go H.empty+  where+    go _ [] = []+    go h (x:xs) | f x `H.member` h = go h xs+                | otherwise        = x : go (H.insert (f x) h) xs+
+ Bio/Util/Numeric.hs view
@@ -0,0 +1,213 @@+-- | Random useful stuff I didn't know where to put.++module Bio.Util.Numeric (+    wilson, invnormcdf, choose,+    estimateComplexity, showNum, showOOM,+    log1p, expm1, (<#>),+    log1mexp, log1pexp,+    lsum, llerp+                ) where++import Prelude+import Data.Char ( intToDigit )+import Data.List ( foldl1' )++-- | Calculates the Wilson Score interval.+-- If @(l,m,h) = wilson c x n@, then @m@ is the binary proportion and+-- @(l,h)@ it's @c@-confidence interval for @x@ positive examples out of+-- @n@ observations.  @c@ is typically something like 0.05.++wilson :: Double -> Int -> Int -> (Double, Double, Double)+wilson c x n = ( (m - h) / d, p, (m + h) / d )+  where+    nn = fromIntegral n+    p  = fromIntegral x / nn++    z = invnormcdf (1-c*0.5)+    h = z * sqrt (( p * (1-p) + 0.25*z*z / nn ) / nn)+    m = p + 0.5 * z * z / nn+    d = 1 + z * z / nn++showNum :: Show a => a -> String+showNum = triplets [] . reverse . show+  where+    triplets acc [] = acc+    triplets acc [a] = a:acc+    triplets acc [a,b] = b:a:acc+    triplets acc [a,b,c] = c:b:a:acc+    triplets acc (a:b:c:s) = triplets (',':c:b:a:acc) s++showOOM :: Double -> String+showOOM x | x < 0 = '-' : showOOM (negate x)+          | otherwise = findSuffix (x*10) ".kMGTPEZY"+  where+    findSuffix _ [] = "many"+    findSuffix y (s:ss) | y < 100  = intToDigit (round y `div` 10) : case (round y `mod` 10, s) of+                                            (0,'.') -> [] ; (0,_) -> [s] ; (d,_) -> [s, intToDigit d]+                        | y < 1000 = intToDigit (round y `div` 100) : intToDigit ((round y `mod` 100) `div` 10) :+                                            if s == '.' then [] else [s]+                        | y < 10000 = intToDigit (round y `div` 1000) : intToDigit ((round y `mod` 1000) `div` 100) :+                                            '0' : if s == '.' then [] else [s]+                        | otherwise = findSuffix (y*0.001) ss++-- Stolen from Lennart Augustsson's erf package, who in turn took it from+-- <http://home.online.no/~pjacklam/notes/invnorm/> Accurate to about 1e-9.+invnormcdf :: (Ord a, Floating a) => a -> a+invnormcdf p =+    let a1 = -3.969683028665376e+01+        a2 =  2.209460984245205e+02+        a3 = -2.759285104469687e+02+        a4 =  1.383577518672690e+02+        a5 = -3.066479806614716e+01+        a6 =  2.506628277459239e+00++        b1 = -5.447609879822406e+01+        b2 =  1.615858368580409e+02+        b3 = -1.556989798598866e+02+        b4 =  6.680131188771972e+01+        b5 = -1.328068155288572e+01++        c1 = -7.784894002430293e-03+        c2 = -3.223964580411365e-01+        c3 = -2.400758277161838e+00+        c4 = -2.549732539343734e+00+        c5 =  4.374664141464968e+00+        c6 =  2.938163982698783e+00++        d1 =  7.784695709041462e-03+        d2 =  3.224671290700398e-01+        d3 =  2.445134137142996e+00+        d4 =  3.754408661907416e+00++        pLow = 0.02425++        nan = 0/0++    in  if p < 0 then+            nan+        else if p == 0 then+            -1/0+        else if p < pLow then+            let q = sqrt(-2 * log p)+            in  (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) /+                 ((((d1*q+d2)*q+d3)*q+d4)*q+1)+        else if p < 1 - pLow then+            let q = p - 0.5+                r = q*q+            in  (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q /+                (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1)+        else if p <= 1 then+            - invnormcdf (1 - p)+        else+            nan+++-- | Try to estimate complexity of a whole from a sample.  Suppose we+-- sampled @total@ things and among those @singles@ occured only once.+-- How many different things are there?+--+-- Let the total number be @m@.  The copy number follows a Poisson+-- distribution with paramter @\lambda@.  Let \( z := e^{\lambda} \), then+-- we have:+--+-- \[+--   P( 0 ) = e^{-\lambda} = \frac{1}{z}                    \\+--   P( 1 ) = \lambda e^{-\lambda} = \frac{\ln z}{z}                   \\+--   P(\ge 1) = 1 - e^{-\lambda} = 1 - \frac{1}{z}                    \\+-- \]+-- \[+--   \mbox{singles} = m \frac{\ln z}{z}                   \\+--   \mbox{total}   = m \left( 1 - \frac{1}{z} \right)                  \\+-- \]+-- \[+--   D := \frac{\mbox{total}}{\mbox{singles}} = (1 - \frac{1}{z}) * \frac{z}{\ln z}                  \\+--   f := z - 1 - D \ln z = 0+-- \]+--+-- To get @z@, we solve using Newton iteration and then substitute to+-- get @m@:+--+-- \[+--   df/dz = 1 - D/z                                    \\+--   z' = z - \frac{ z (z - 1 - D \ln z) }{ z - D }     \\+--   m = \mbox{singles} * \frac{z}{\ln z}+-- \]+--+-- It converges as long as the initial @z@ is large enough, and @10D@+-- (in the line for @zz@ below) appears to work well.++estimateComplexity :: (Integral a, Floating b, Ord b) => a -> a -> Maybe b+estimateComplexity total singles | total   <= singles = Nothing+                                 | singles <= 0       = Nothing+                                 | otherwise          = Just m+  where+    d = fromIntegral total / fromIntegral singles+    step z = z * (z - 1 - d * log z) / (z - d)+    iter z = case step z of zd | abs zd < 1e-12 -> z+                               | otherwise -> iter $! z-zd+    zz = iter $! 10*d+    m = fromIntegral singles * zz / log zz+++-- | Computes \( \ln \left( e^x + e^y \right) \) without leaving the log domain and+-- hence without losing precision.+infixl 5 <#>+{-# INLINE (<#>) #-}+(<#>) :: (Floating a, Ord a) => a -> a -> a+x <#> y = if x >= y then x + log1pexp (y-x) else y + log1pexp (x-y)++-- | Computes @log (1+x)@ to a relative precision of @10^-8@ even for+-- very small @x@.  Stolen from <http://www.johndcook.com/cpp_log_one_plus_x.html>+{-# INLINE log1p #-}+log1p :: (Floating a, Ord a) => a -> a+log1p x | x < -1 = error "log1p: argument must be greater than -1"+        -- x is large enough that the obvious evaluation is OK:+        | x > 0.0001 || x < -0.0001 = log $ 1 + x+        -- Use Taylor approx. log(1 + x) = x - x^2/2 with error roughly x^3/3+        -- Since |x| < 10^-4, |x|^3 < 10^-12, relative error less than 10^-8:+        | otherwise = (1 - 0.5*x) * x+++-- | Computes \( e^x - 1 \) to a relative precision of @10^-10@ even for+-- very small @x@.  Stolen from <http://www.johndcook.com/cpp_expm1.html>+{-# INLINE expm1 #-}+expm1 :: (Floating a, Ord a) => a -> a+expm1 x | x > -0.00001 && x < 0.00001 = (1 + 0.5 * x) * x       -- Taylor approx+        | otherwise                   = exp x - 1               -- direct eval++-- | Computes \( \ln (1 - e^x) \), following Martin Mächler.+{-# INLINE log1mexp #-}+log1mexp :: (Floating a, Ord a) => a -> a+log1mexp x | x > - log 2 = log (- expm1 x)+           | otherwise   = log1p (- exp x)++-- | Computes \( \ln (1 + e^x) \), following Martin Mächler.+{-# INLINE log1pexp #-}+log1pexp :: (Floating a, Ord a) => a -> a+log1pexp x | x <=  -37 = exp x+           | x <=   18 = log1p $ exp x+           | x <= 33.3 = x + exp (-x)+           | otherwise = x+++-- | Computes \( \ln ( \sum_i e^{x_i} ) \) sensibly.  The list must be+-- sorted in descending(!) order.+{-# INLINE lsum #-}+lsum :: (Floating a, Ord a) => [a] -> a+lsum = foldl1' (\x y -> if x >= y then x + log1pexp (y-x) else err)+    where err = error "lsum: argument list must be in descending order"++-- | Computes \( \ln \left( c e^x + (1-c) e^y \right) \).+{-# INLINE llerp #-}+llerp :: (Floating a, Ord a) => a -> a -> a -> a+llerp c x y | c <= 0.0  = y+            | c >= 1.0  = x+            | x >= y    = log     c  + x + log1p ( (1-c)/c * exp (y-x) )        -- Hmm.+            | otherwise = log1p (-c) + y + log1p ( c/(1-c) * exp (x-y) )        -- Hmm.++-- | Binomial coefficient: \( \mbox{choose n k} = \frac{n!}{(n-k)! k!} \)+{-# INLINE choose #-}+choose :: Integral a => a -> a -> a+choose n k = product [n-k+1 .. n] `div` product [2..k]++
+ Bio/Util/Storable.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE CPP #-}+-- | Utilities to read multibyte quantities from arbitrary positions.+module Bio.Util.Storable+    ( peekWord8+    , peekUnalnWord16LE+    , peekUnalnWord16BE+    , peekUnalnWord32LE+    , peekUnalnWord32BE+    , pokeUnalnWord32LE+    ) where++#if __GLASGOW_HASKELL__ >= 710+#define HAVE_BYTESWAP_PRIMOPS+#endif++#if i386_HOST_ARCH || x86_64_HOST_ARCH+#define MEM_UNALIGNED_OPS+#endif++import BasePrelude++peekWord8 :: Ptr a -> IO Word8+peekWord8 = peek . castPtr++#if defined(MEM_UNALIGNED_OPS) && defined(WORDS_BIGENDIAN) && defined(HAVE_BYTESWAP_PRIMOPS)+peekUnalnWord16LE :: Ptr a -> IO Word16+peekUnalnWord16LE = fmap byteSwap16 . peek . castPtr++peekUnalnWord32LE :: Ptr a -> IO Word32+peekUnalnWord32LE = fmap byteSwap32 . peek . castPtr++pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()+pokeUnalnWord32LE p w = poke (castPtr p) (byteSwap32 w)++#elif defined(MEM_UNALIGNED_OPS) && !defined(WORDS_BIGENDIAN)+peekUnalnWord16LE :: Ptr a -> IO Word16+peekUnalnWord16LE = peek . castPtr++peekUnalnWord32LE :: Ptr a -> IO Word32+peekUnalnWord32LE = peek . castPtr++pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()+pokeUnalnWord32LE p w = poke (castPtr p) w++#else+peekUnalnWord16LE :: Ptr a -> IO Word16+peekUnalnWord16LE p = do+    x <- fromIntegral <$> peekWord8 (plusPtr p 0)+    y <- fromIntegral <$> peekWord8 (plusPtr p 1)+    return $! x .|. unsafeShiftL y 8++peekUnalnWord32LE :: Ptr a -> IO Word32+peekUnalnWord32LE p = do+    x <- fromIntegral <$> peekWord8 (plusPtr p 0)+    y <- fromIntegral <$> peekWord8 (plusPtr p 1)+    z <- fromIntegral <$> peekWord8 (plusPtr p 2)+    w <- fromIntegral <$> peekWord8 (plusPtr p 3)+    return $! x .|. unsafeShiftL y 8 .|. unsafeShiftL z 16 .|. unsafeShiftL w 24++pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()+pokeUnalnWord32LE p w = do pokeByteOff p 0 (fromIntegral $ shiftR w  0 :: Word8)+                           pokeByteOff p 1 (fromIntegral $ shiftR w  8 :: Word8)+                           pokeByteOff p 2 (fromIntegral $ shiftR w 16 :: Word8)+                           pokeByteOff p 3 (fromIntegral $ shiftR w 24 :: Word8)+#endif+++#if defined(MEM_UNALIGNED_OPS) && !defined(WORDS_BIGENDIAN) && defined(HAVE_BYTESWAP_PRIMOPS)+peekUnalnWord16BE :: Ptr a -> IO Word16+peekUnalnWord16BE = fmap byteSwap16 . peek . castPtr++peekUnalnWord32BE :: Ptr a -> IO Word32+peekUnalnWord32BE = fmap byteSwap32 . peek . castPtr++#elif defined(MEM_UNALIGNED_OPS) && defined(WORDS_BIGENDIAN)+peekUnalnWord16BE :: Ptr a -> IO Word16+peekUnalnWord16BE = peek . castPtr++peekUnalnWord32BE :: Ptr a -> IO Word32+peekUnalnWord32BE = peek . castPtr++#else+peekUnalnWord16BE :: Ptr a -> IO Word16+peekUnalnWord16BE p = do+    x <- fromIntegral <$> peekWord8 (plusPtr p 0)+    y <- fromIntegral <$> peekWord8 (plusPtr p 1)+    return $! y .|. unsafeShiftL x 8++peekUnalnWord32BE :: Ptr a -> IO Word32+peekUnalnWord32BE p = do+    x <- fromIntegral <$> peekWord8 (plusPtr p 0)+    y <- fromIntegral <$> peekWord8 (plusPtr p 1)+    z <- fromIntegral <$> peekWord8 (plusPtr p 2)+    w <- fromIntegral <$> peekWord8 (plusPtr p 3)+    return $! w .|. unsafeShiftL z 8 .|. unsafeShiftL y 16 .|. unsafeShiftL x 24+#endif+
+ Bio/Util/Text.hs view
@@ -0,0 +1,51 @@+module Bio.Util.Text+    ( Unpack(..)+    , w2c, c2w+    , decodeBytes+    , encodeBytes+    , decompressGzip+    ) where++import BasePrelude+import Data.ByteString.Internal     ( c2w, w2c )+import Data.Text.Encoding           ( encodeUtf8, decodeUtf8With )++import qualified Codec.Compression.Zlib.Internal as Z+import qualified Data.ByteString.Char8           as S+import qualified Data.ByteString.Lazy            as L+import qualified Data.ByteString.Lazy.Internal   as L ( ByteString(..) )+import qualified Data.Text                       as T++-- | Class of things that can be unpacked into 'String's.  Kind of the+-- opposite of 'IsString'.+class Unpack s where unpack :: s -> String++instance Unpack S.ByteString where unpack = S.unpack+instance Unpack T.Text       where unpack = T.unpack+instance Unpack String       where unpack = id+++-- | Converts 'Bytes' into 'Text'.  This uses UTF8, but if there is an+-- error, it pretends it was Latin1.  Evil as this is, it tends to Just+-- Work on files where nobody ever wasted a thought on encodings.+decodeBytes :: S.ByteString -> T.Text+decodeBytes = decodeUtf8With (const $ fmap w2c)++-- | Converts 'Text' into 'Bytes'.  This uses UTF8.+encodeBytes :: T.Text -> S.ByteString+encodeBytes = encodeUtf8+++-- | Decompresses Gzip or Bgzf and passes everything else on.  In+-- reality, it simply decompresses Gzip, and when done, looks for+-- another Gzip stream.  Since there is a small chance to attempt+-- decompression of an uncompressed stream, the original data is+-- returned in case of an error.+decompressGzip :: L.ByteString -> L.ByteString+decompressGzip s = case L.uncons s of+    Just (31, s') -> case L.uncons s' of+        Just (139,_) -> Z.foldDecompressStreamWithInput L.Chunk decompressGzip (const s)+                        (Z.decompressST Z.gzipOrZlibFormat Z.defaultDecompressParams) s+        _            -> s+    _                -> s+
CHANGELOG.md view
@@ -1,3 +1,10 @@+# 2.0.0++ * Switched from "iteratee" to "streaming".  The code looks much cleaner+   and is easier to understand.++ * Repaired mistreatment of bam headers when merging files.+ # 1.1.0 (2018-10-15)   * Fix for previous workaround.  Repairs writeBamFile.
biohazard.cabal view
@@ -1,5 +1,5 @@ Name:                biohazard-Version:             1.1.1+Version:             2.0   Synopsis:            bioinformatics support library Description:         This is a collection of modules I separated from                      various bioinformatics tools.@@ -16,7 +16,7 @@  Cabal-version:       >= 1.10 Build-type:          Simple-Tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.1, GHC == 8.4.3, GHC == 8.6.1+Tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1  source-repository head   type:     git@@ -39,53 +39,59 @@                        Bio.Bam.Trim,                        Bio.Bam.Writer,                        Bio.Base,-                       Bio.Iteratee,-                       Bio.Iteratee.Base,-                       Bio.Iteratee.Bgzf,-                       Bio.Iteratee.Builder,-                       Bio.Iteratee.Bytes,-                       Bio.Iteratee.Exception,-                       Bio.Iteratee.IO,-                       Bio.Iteratee.Iteratee,-                       Bio.Iteratee.List,-                       Bio.Iteratee.ZLib,                        Bio.Prelude,+                       Bio.Streaming,+                       Bio.Streaming.Bgzf,+                       Bio.Streaming.Bytes,+                       Bio.Streaming.Furrow,+                       Bio.Streaming.Parse,+                       Bio.Streaming.Vector,                        Bio.TwoBit,                        Bio.Util.MMap,+                       Bio.Util.Nub,                        Bio.Util.Numeric,                        Bio.Util.Storable,-                       Bio.Util.Zlib+                       Bio.Util.Text -  Build-depends:       async                    >= 2.0 && < 2.3,-                       attoparsec               >= 0.10 && < 0.14,+  Build-depends:       attoparsec               >= 0.10 && < 0.14,                        base                     >= 4.7 && < 4.13,                        base-prelude             == 1.2.0.*,                        bytestring               >= 0.10.2 && < 0.11,                        containers               >= 0.5 && < 0.7,+                       directory                >= 1.2.1 && < 1.4,                        exceptions               >= 0.6 && < 0.11,                        hashable                 >= 1.0 && < 1.3,                        primitive                >= 0.5 && < 0.7,                        stm                      >= 2.4 && < 2.6,+                       streaming                >= 0.1.4.2 && < 0.3,                        text                     >= 1.0 && < 1.3,                        transformers             >= 0.4.1 && < 0.6,                        unix                     >= 2.5 && < 2.8,                        unordered-containers     >= 0.2.3 && < 0.3,                        vector                   >= 0.11 && < 0.13,                        vector-algorithms        >= 0.3 && < 0.8,-                       vector-th-unbox          == 0.2.*,                        zlib                     == 0.6.* +  if !impl(ghc >= 7.10)+    build-depends: bifunctors == 5.*   if !impl(ghc >= 8.0)     build-depends: semigroups == 0.18.*    Ghc-options:         -Wall+  if impl(ghc >= 8.0)+    Ghc-options:       -Wincomplete-uni-patterns -Wredundant-constraints+                       -Wcompat -Wincomplete-record-updates -Widentities+  if impl(ghc >= 8.4)+    Ghc-options:       -Wmissing-export-lists -Wpartial-fields    Default-Language:    Haskell2010    Default-Extensions:  BangPatterns,                        DeriveDataTypeable,+                       DeriveGeneric,                        FlexibleContexts,                        FlexibleInstances,+                       GeneralizedNewtypeDeriving,                        LambdaCase,                        MultiParamTypeClasses,                        NoImplicitPrelude,@@ -94,21 +100,19 @@                        TypeSynonymInstances    Other-Extensions:    CPP,-                       DeriveGeneric,                        ExistentialQuantification,                        ForeignFunctionInterface,-                       GeneralizedNewtypeDeriving,                        Rank2Types,-                       TemplateHaskell,                        TypeFamilies -  Hs-source-dirs:      src-  Include-dirs:        src/cbits+  Hs-source-dirs:      .+  Include-dirs:        cbits   Install-Includes:    myers_align.h-  C-sources:           src/cbits/loops.c,-                       src/cbits/mmap.c,-                       src/cbits/myers_align.c,-                       src/cbits/trim.c+  C-sources:           cbits/loops.c,+                       cbits/mmap.c,+                       cbits/myers_align.c,+                       cbits/trim.c+                       cbits/zlib.c   CC-options:          -fPIC  -- :vim:tw=132:
+ cbits/loops.c view
@@ -0,0 +1,113 @@+void nuc_loop( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;++    while( u < v ) {+        char a = q[ u ] ;+        char b = q[ u + stride ] ;+        char a1 = a ? 0x10 << (a&3) : 0xf0 ;+        char b1 = b ? 0x1  << (b&3) : 0xf  ;+        *p++ = a1 | b1 ;+        u += stride+stride ;+    }+    if( u == v ) {+        char a = q[ u ] ;+        char a1 = a ? 0x10 << (a&3) : 0xf0 ;+        *p = a1 ;+    }+}++void nuc_loop_wide( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;++    while( u < v ) {+        char a = q[ u ] ;+        *p++ = a ? 0x1 << (a&3) : 0xf  ;+        u += stride ;+    }+}++void nuc_loop_asc( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;++    while( u <= v ) {+        char a = q[ u ] ;+        *p++ = a == 0 ? 'N' : (a&3) == 0 ? 'A' : (a&3) == 1 ? 'C' : (a&3) == 2 ? 'G' : 'T' ;+        u += stride ;+    }+    *p = 0 ;+}++void nuc_loop_asc_rev( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;++    while( u <= v ) {+        char a = q[ v ] ;+        *p++ = a == 0 ? 'N' : (a&3) == 0 ? 'T' : (a&3) == 1 ? 'G' : (a&3) == 2 ? 'C' : 'A' ;+        v -= stride ;+    }+    *p = 0 ;+}++void qual_loop( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;+    while( u <= v ) {+        *p++ = (q[u] >> 2) & 0x3f ;+        u += stride ;+    }+}++void qual_loop_asc( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;+    while( u <= v ) {+        *p++ = 33 + ((q[u] >> 2) & 0x3f) ;+        u += stride ;+    }+    *p = 0 ;+}++void qual_loop_asc_rev( char* p, int stride, char* q, int u, int v )+{+    u *= stride ;+    v *= stride ;+    while( u <= v ) {+        *p++ = 33 + ((q[v] >> 2) & 0x3f) ;+        v -= stride ;+    }+    *p = 0 ;+}++int int_loop( char* p, int x )+{+    *p++ = ':' ;+    if( x == 0 ) {+        *p = '0' ;+        return 2 ;+    }+    char *q = p ;+    while( x > 0 ) {+        *q++ = '0' + x % 10 ;+        x /= 10 ;+    }+    int r = q-p ;+    --q ;+    while( p < q ) {+        char c = *p ;+        *p++ = *q ;+        *q-- = c ;+    }+    return r+1 ;+}++
+ cbits/mmap.c view
@@ -0,0 +1,10 @@+#include <sys/mman.h>++unsigned char *my_mmap(size_t len, int fd) {+        void *result = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);+        return (unsigned char*)( result == MAP_FAILED ? 0 : result );+}++void my_munmap(void *len, unsigned char *p) {+        munmap( p, (size_t)len ) ; +}
+ cbits/myers_align.c view
@@ -0,0 +1,102 @@+#include "myers_align.h"++#include <limits.h>+#include <stdlib.h>+#include <string.h>++// [*blech*, this looks and feels like FORTRAN.]+unsigned myers_diff(+        const char *seq_a, int len_a, enum myers_align_mode mode, +        const char* seq_b, int len_b, int maxd,+        char *bt_a, char *bt_b ) +{+	// int len_a = strlen( seq_a ), len_b = strlen( seq_b ) ;+	if( maxd > len_a + len_b ) maxd = len_a + len_b ;++	// in vee[d][k], d runs from 0 to maxd; k runs from -d to +d+	int **vee = calloc( maxd, sizeof(int*) ) ;++	int d, dd, k, x, y, r = UINT_MAX ;+	int *v_d_1 = 0, *v_d = 0 ; 															// "array slice" vee[.][d-1]+	for( d = 0 ; d != maxd ; ++d, v_d_1 = v_d )									// D-paths in order of increasing D+	{+		v_d = d + (vee[d] = malloc( (2 * d + 1) * sizeof( int ) )) ; 		// "array slice" vee[.][d]++		for( k = max(-d,-len_a) ; k <= min(d,len_b) ; ++k ) 					// diagonals+		{+			if( d == 0 )         x = 0 ;+			else if(d==1&&k==0)  x =                       v_d_1[ k ]+1 ;+			else if( k == -d   ) x =                                     v_d_1[ k+1 ] ;+			else if( k ==  d   ) x =       v_d_1[ k-1 ]+1 ;									// argh, need to check for d first, b/c -d+2 could be equal to d+			else if( k == -d+1 ) x = max(                  v_d_1[ k ]+1, v_d_1[ k+1 ] ) ;+			else if( k ==  d-1 ) x = max(  v_d_1[ k-1 ]+1, v_d_1[ k ]+1 ) ;+			else                 x = max3( v_d_1[ k-1 ]+1, v_d_1[ k ]+1, v_d_1[ k+1 ] ) ;++			y = x-k ;+			while( x < len_b && y < len_a && match( seq_b[x], seq_a[y] ) ) ++x, ++y ;+			v_d[ k ] = x ;++			if(+                    bt_a && bt_b &&+					(mode == myers_align_is_prefix || y == len_a) &&+					(mode == myers_align_has_prefix || x == len_b) )+			{+				char *out_a = bt_a + len_a + d +2 ;+				char *out_b = bt_b + len_b + d +2 ;+				*--out_a = 0 ;+				*--out_b = 0 ;+				for( dd = d ; dd != 0 ; )+				{+					if( k != -dd && k != dd && x == vee[ dd-1 ][ k + dd-1 ]+1 )+					{+						--dd ;+						--x ;+						--y ;+						*--out_b = seq_b[x] ;+						*--out_a = seq_a[y] ;+					}+					else if( k > -dd+1 && x == vee[ dd-1 ][ k-1 + dd-1 ]+1 )+					{+						--x ;+						--k ;+						--dd ;+						*--out_b = seq_b[x] ;+						*--out_a = '-' ;+					}+					else if( k < dd-1 && x == vee[ dd-1 ][ k+1 + dd-1 ] )+					{+						++k ;+						--y ;+						--dd ;+						*--out_b = '-' ;+						*--out_a = seq_a[y] ;+					}+					else // this better had been a match...+					{+						--x ;+						--y ;+						*--out_b = seq_b[x] ;+						*--out_a = seq_a[y] ;+					}+				}+				while( x > 0 )+				{+					--x ;+					*--out_b = seq_b[x] ;+					*--out_a = seq_a[x] ;+				}+				memmove( bt_a, out_a, bt_a + len_a + d + 2 - out_a ) ;+				memmove( bt_b, out_b, bt_b + len_b + d + 2 - out_b ) ;+				r = d ;+				goto cleanup ;+			}+		}+	}++cleanup:+	for( dd = maxd ; dd != 0 ; --dd )+		free( vee[dd-1] ) ;+	free( vee ) ;+	return r ;+}+
+ cbits/myers_align.h view
@@ -0,0 +1,76 @@+#ifndef INCLUDED_MYERS_ALIGN+#define INCLUDED_MYERS_ALIGN++enum myers_align_mode {+    myers_align_globally = 0,+    myers_align_is_prefix = 1,+    myers_align_has_prefix = 2 } ;++//! \brief aligns two sequences in O(nd) time+//! This alignment algorithm following Eugene W. Myers: "An O(ND)+//! Difference Algorithm and Its Variations".+//! Both input sequences are ASCIIZ-encoded with IUPAC-IUB ambiguity+//! codes.  By definition, if ambiguity codes overlap, that's a match,+//! else a mismatch.  Mismatches and gaps count a unit penalty.  If mode+//! is myers_align_globally, both sequences must align completely.  If+//! mode is myers_align_is_prefix, seq_a must align completely as prefix+//! of seq_b.  If mode is myers_align_has_prefix, seq_b must align+//! completely as prefix of seq_a.  +//!+//! Note that the calculation time is O(nd) where n is the length of the+//! best alignment and d the number of differences in it, memory+//! consumption is O(maxd^2).+//!+//! \param seq_a First input sequence.+//! \param mode How to align (i.e. what gaps to count).+//! \param seq_b Second input sequence.+//! \param maxd Maximum penalty to consider.+//! \param bt_a Space to backtrace seq_a into, must have room for+//!             (strlen(seq_a)+maxd+1) characters.+//! \param bt_b Space to backtrace seq_b into, must have room for+//!             (strlen(seq_b)+maxd+1) characters.+//! \return The actual edit distance or UINT_MAX if the edit distance+//!         would be greater than maxd.+//!+unsigned myers_diff(+        const char *seq_a, int len_a, enum myers_align_mode mode,+        const char* seq_b, int len_b, int maxd,+        char *bt_a, char *bt_b ) ;++//! \brief converts an IUPAC-IUB ambiguity code to a bitmap Each base is+//! represented by a bit, makes checking for matches easier.+static inline int char_to_bitmap( char x ) +{+    switch( x & ~32 )+    {+        case 'A': return 1 ;+        case 'C': return 2 ;+        case 'G': return 4 ;+        case 'T': return 8 ;+        case 'U': return 8 ;++        case 'S': return 6 ;+        case 'W': return 9 ;+        case 'R': return 5 ;+        case 'Y': return 10 ;+        case 'K': return 12 ;+        case 'M': return 3 ;++        case 'B': return 14 ;+        case 'D': return 13 ;+        case 'H': return 11 ;+        case 'V': return 7 ;++        case 'N': return 15 ;+        default: return 0 ;+    }+}++static inline int compatible( char x, char y ) { return (char_to_bitmap(x) & char_to_bitmap(y)) != 0 ; }+static inline int match( char a, char b ) { return (char_to_bitmap(a) & char_to_bitmap(b)) != 0 ; }++static inline int min( int a, int b ) { return a < b ? a : b ; }+static inline int max( int a, int b ) { return a < b ? b : a ; }+static inline int max3( int a, int b, int c ) { return a < b ? max( b, c ) : max( a, c ) ; }++#endif
+ cbits/trim.c view
@@ -0,0 +1,46 @@+#include <stdint.h>++static const uint8_t compls[] =+    { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 } ;++int prim_match_reads( int i1+                    , int i2+                    , int r+                    , const uint8_t *rd1+                    , const uint8_t *qs1+                    , const uint8_t *rd2+                    , const uint8_t *qs2 )+{+    int acc = 0 ;+    while( r != 0 )+    {+        --i2 ;+        uint8_t n1 = rd1[ i1 ] ;+        uint8_t n2 = rd2[ i2 ] ;+        uint8_t q1 = qs1[ i1 ] ;+        uint8_t q2 = qs2[ i2 ] ;++        acc += (n1 & 0xF) == compls[ n2 & 0xF ] ? 0 : 5 + (q1 < q2 ? q1 : q2) ;++        ++i1 ;+        --r ;+    }+    return acc ;+}++int prim_match_ad( int off+                 , int i+                 , const uint8_t *rd+                 , const uint8_t *qs+                 , const uint8_t *ad )+{+    int acc = 0 ;+    while( i > 0 )+    {+        --i;+        acc += rd[ i+off ] == ad[ i ] ? 0 : 5 ++               (qs[ i+off ] < 25 ? qs[ i+off ] : 25) ;+    }+    return acc ;+}+
+ cbits/zlib.c view
@@ -0,0 +1,64 @@+#include <stdint.h>+#include <string.h>+#include <zlib.h>++// header stolen from the EOF marker+static const char header[18] = "\x1f\x8b\x8\x4\0\0\0\0\0\xff\x6\0\x42\x43\x2\0" ;++// Compresses one bgzf chunk.  We assemble the header ourselves and call+// deflate in raw mode, because some tools seems to be extremely picky+// about irrelevant details.+int compress_chunk ( char *dest, int *dest_len, char *source, int source_len, int level )+{+    z_stream stream = {0} ;+    stream.next_in = source ;+    stream.next_out = dest + 18 ;+    stream.avail_in = source_len ;+    stream.avail_out = *dest_len ;++    memmove( dest, header, 16 ) ;++    int rc = deflateInit2( &stream, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY ) ;+    if( rc != Z_OK ) return rc ;++    rc = deflate( &stream, Z_FINISH ) ;+    int rc2 = deflateEnd( &stream ) ;+    if( rc != Z_STREAM_END ) return rc ;+    if( rc2 != Z_OK ) return rc2 ;++    long crc  = crc32( crc32( 0, 0, 0 ), source, source_len ) ;++    int compressed_length = 18 + 8 + stream.total_out ;+    if (compressed_length > 65536) return Z_BUF_ERROR ;++    dest[16] = (compressed_length-1) & 0xff ;+    dest[17] = (compressed_length-1) >> 8 ;++    *(uint32_t*)(dest+compressed_length-8) = crc ;+    *(uint32_t*)(dest+compressed_length-4) = source_len ;++    *dest_len = compressed_length ;+    return Z_OK ;+}++// Decompresses one bgzf chunk.  We receive no header, so we call+// inflate in raw mode and check the crc32 ourselves.+int decompress_chunk ( char *dest, int dest_len, char *source, int source_len )+{+    z_stream stream = {0} ;+    stream.next_in = source ;+    stream.next_out = dest ;+    stream.avail_in = source_len - 8 ;+    stream.avail_out = dest_len ;++    int rc = inflateInit2( &stream, -15 ) ;+    if( rc != Z_OK ) return rc ;++    rc = inflate( &stream, Z_FINISH ) ;+    int rc2 = inflateEnd( &stream ) ;+    if( rc != Z_STREAM_END ) return rc ;+    if( rc2 != Z_OK ) return rc2 ;++    long crc  = crc32( crc32( 0, 0, 0 ), dest, dest_len ) ;+    return *(uint32_t*)(dest+dest_len-8) == crc && stream.avail_out == 0 ? Z_OK : Z_STREAM_ERROR ;+}
− src/Bio/Adna.hs
@@ -1,661 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}---- | Things specific to ancient DNA, e.g. damage models.------ For aDNA, we need a substitution probability.  We have three options:--- use an empirically determined PSSM, use an arithmetically defined--- PSSM based on the /Johnson/ model, use a context sensitive PSSM based--- on the /Johnson/ model and an alignment.  Using /Dindel/, actual--- substitutions relative to a called haplotype would be taken into--- account.  Since we're not going to do that, taking alignments into--- account is difficult, somewhat approximate, and therefore not worth--- the hassle.--module Bio.Adna (-    DmgStats(..),-    CompositionStats,-    SubstitutionStats,-    addFragType,-    damagePatternsIter,-    damagePatternsIterMD,-    damagePatternsIter2Bit,-    alnFromMd,--    DamageParameters(..),-    NewDamageParameters(..),-    GenDamageParameters(..),-    DamageModel,-    bang, nudge,-    Alignment(..),-    FragType(..),-    Subst(..),--    NPair,-    npair,-    fst_np,-    snd_np,--    noDamage,-    univDamage,-    empDamage,-    Mat44D(..),-    MMat44D(..),-    scalarMat,-    complMat,-    freezeMats,--    bwa_cal_maxdiff-  ) where--import Bio.Bam-import Bio.Prelude-import Bio.TwoBit--import qualified Data.Vector                    as V-import qualified Data.Vector.Generic            as G-import qualified Data.Vector.Storable           as VS-import qualified Data.Vector.Unboxed            as U-import qualified Data.Vector.Unboxed.Mutable    as UM---- | We represent substitution matrices by the type 'Mat44D'.  Internally,--- this is a vector of packed vectors.  Conveniently, each of the packed--- vectors represents all transitions /into/ the given nucleotide.--newtype Mat44D = Mat44D (U.Vector Double) deriving (Show, Generic)-newtype MMat44D = MMat44D (UM.IOVector Double)---- | A 'DamageModel' is a function that gives substitution matrices for--- each position in a read.  The 'DamageModel' can depend on whether the--- alignment is reversed, the length of the read and the position.  (In--- practice, we should probably memoize precomputed damage models--- somehow.)--type DamageModel = Bool -> Int -> Int -> Mat44D-data Subst = Nucleotide :-> Nucleotide deriving (Eq, Ord, Ix, Show)--infix 9 :->-infix 8 `bang`---- | Convenience function to access a substitution matrix that has a--- mnemonic reading.-{-# INLINE bang #-}-bang :: Mat44D -> Subst -> Double-bang (Mat44D v) (N x :-> N y)-    | U.length v == 16 = v U.! (fromIntegral x + 4 * fromIntegral y)-    | otherwise = error $ "Huh? " ++ show (U.length v)--{-# INLINE nudge #-}-nudge :: MMat44D -> Subst -> Double -> IO ()-nudge (MMat44D v) (N x :-> N y) a = UM.read v i >>= UM.write v i . (+) a-  where i = fromIntegral x + 4 * fromIntegral y--scalarMat :: Double -> Mat44D-scalarMat s = Mat44D $ U.fromListN 16 [ s, 0, 0, 0-                                      , 0, s, 0, 0-                                      , 0, 0, s, 0-                                      , 0, 0, 0, s ]--complMat :: Mat44D -> Mat44D-complMat v = Mat44D $ U.fromListN 16 [ v `bang` compl x :-> compl y-                                     | y <- range (nucA, nucT)-                                     , x <- range (nucA, nucT) ]---- | Adds the two matrices of a mutable substitution model (one for each--- strand) appropriately, normalizes the result (to make probabilities--- from pseudo-counts), and freezes that into one immutable matrix.  We--- add a single count everywhere to avoid getting NaNs from bizarre--- data.-freezeMats :: MMat44D -> MMat44D -> IO Mat44D-freezeMats (MMat44D vv) (MMat44D ww) = do-    v <-            Mat44D <$> U.freeze vv-    w <- complMat . Mat44D <$> U.freeze ww--    let sums = U.generate 4 $ \x0 ->-                    let x = N $ fromIntegral x0-                    in sum [ v `bang` x :-> z + w `bang` x :-> z-                           | z <- range (nucA, nucT) ] + 4--    return . Mat44D $ U.fromListN 16-            [ (v `bang` x :-> y + w `bang` x :-> y + 1) / s-            | y <- range (nucA, nucT)-            , x <- range (nucA, nucT)-            , let s = sums U.! fromIntegral (unN x) ]----- | 'DamageModel' for undamaged DNA.  The likelihoods follow directly--- from the quality score.  This needs elaboration to see what to do--- with amibiguity codes (even though those haven't actually been--- observed in the wild).--noDamage :: DamageModel-noDamage _ _ _ = one-  where !one = scalarMat 1----- | Parameters for the universal damage model.------ We assume the correct model is either no damage, or single strand--- damage, or double strand damage.  Each of them comes with a--- probability.  It turns out that blending them into one is simply--- accomplished by multiplying these probabilities onto the deamination--- probabilities.------ For single stranded library prep, only one kind of damage occurs (C--- frequency ('ssd_sigma') in single stranded parts, and the overhang--- length is distributed exponentially with parameter 'ssd_lambda' at--- the 5' end and 'ssd_kappa' at the 3' end.  (Without UDG treatment,--- those will be equal.  With UDG, those are much smaller and in fact--- don't literally represent overhangs.)------ For double stranded library prep, we get C->T damage at the 5' end--- and G->A at the 3' end with rate 'dsd_sigma' and both in the interior--- with rate 'dsd_delta'.  Everything is symmetric, and therefore the--- orientation of the aligned read doesn't matter either.  Both--- overhangs follow a distribution with parameter 'dsd_lambda'.--data DamageParameters float = DP { ssd_sigma  :: !float         -- deamination rate in ss DNA, SS model-                                 , ssd_delta  :: !float         -- deamination rate in ds DNA, SS model-                                 , ssd_lambda :: !float         -- param for geom. distribution, 5' end, SS model-                                 , ssd_kappa  :: !float         -- param for geom. distribution, 3' end, SS model-                                 , dsd_sigma  :: !float         -- deamination rate in ss DNA, DS model-                                 , dsd_delta  :: !float         -- deamination rate in ds DNA, DS model-                                 , dsd_lambda :: !float }       -- param for geom. distribution, DS model-  deriving (Read, Show, Generic)--data NewDamageParameters vec float = NDP { dp_gc_frac :: !float-                                         , dp_mu      :: !float-                                         , dp_nu      :: !float-                                         , dp_alpha5  :: !(vec float)-                                         , dp_beta5   :: !(vec float)-                                         , dp_alpha   :: !float-                                         , dp_beta    :: !float-                                         , dp_alpha3  :: !(vec float)-                                         , dp_beta3   :: !(vec float) }-  deriving (Read, Show, Generic)--data GenDamageParameters vec float-    = UnknownDamage-    | OldDamage (DamageParameters float)-    | NewDamage (NewDamageParameters vec float)-  deriving (Show, Generic, Read)------ | Generic substitution matrix, has C->T and G->A deamination as--- parameters.  Setting 'p' or 'q' to 0 as appropriate makes this apply--- to the single stranded or undamaged case.--{-# INLINE genSubstMat #-}-genSubstMat :: Double -> Double -> Mat44D-genSubstMat p q = Mat44D $ U.fromListN 16 [ 1,  0,   q,  0-                                          , 0, 1-p,  0,  0-                                          , 0,  0,  1-q, 0-                                          , 0,  p,   0,  1 ]--univDamage :: DamageParameters Double -> DamageModel-univDamage DP{..} r l i = genSubstMat (p1+p2) (q1+q2)-    where-        (p1, q1) = if r then let lam5 = ssd_lambda ^ (l-i)-                                 lam3 = ssd_kappa ^ (1+i)-                                 lam  = lam3 + lam5 - lam3 * lam5-                                 p    = ssd_sigma * lam + ssd_delta * (1-lam)-                             in (0,p)-                        else let lam5 = ssd_lambda ^ (1+i)-                                 lam3 = ssd_kappa ^ (l-i)-                                 lam  = lam3 + lam5 - lam3 * lam5-                                 p    = ssd_sigma * lam + ssd_delta * (1-lam)-                             in (p,0)--        p2      = dsd_sigma * lam5_ds + dsd_delta * (1-lam5_ds)-        q2      = dsd_sigma * lam3_ds + dsd_delta * (1-lam3_ds)-        lam5_ds = dsd_lambda ^ (1+i)-        lam3_ds = dsd_lambda ^ (l-i)--empDamage :: NewDamageParameters U.Vector Double -> DamageModel-empDamage NDP{..} =-    \r l i -> if i+i < l then-                if r then fromMaybe middleRev (rev5 V.!? i)-                     else fromMaybe middle    (fwd5 V.!? i)-              else-                if r then fromMaybe middleRev (rev3 V.!? (l-i-1))-                     else fromMaybe middle    (fwd3 V.!? (l-i-1))-  where-    !middle    = genSubstMat' dp_alpha dp_beta-    !middleRev = genSubstMat' dp_beta dp_alpha--    !fwd5 = V.zipWith genSubstMat' (G.convert dp_alpha5) (G.convert dp_beta5)-    !fwd3 = V.zipWith genSubstMat' (G.convert dp_alpha3) (G.convert dp_beta3)--    !rev5 = V.zipWith genSubstMat' (G.convert dp_beta5) (G.convert dp_alpha5)-    !rev3 = V.zipWith genSubstMat' (G.convert dp_beta3) (G.convert dp_alpha3)--    genSubstMat' a b = genSubstMat (recip $ 1 + exp (-a)) (recip $ 1 + exp (-b))----- | Collected \"traditional\" statistics:------ * Base composition near 5' end and near 3' end.  Each consists of---   five vectors of counts of A,C,G,T, and everything else.---   'basecompo5' begins with 'context' bases to the left of the 5' end,---   'basecompo3' ends with 'context' bases to the right of the 3' end.------ * Substitutions.  Counted from the reconstructed alignment, once---   around the 5' end and once around the 3' end.  For a total of 2*4*4---   different substitutions.  Positions where the query has a gap are---   skipped.------ * Substitutions at CpG motifs.  Also counted from the reconstructed---   alignment, and a CpG site is simply the sequence CG in the---   reference.  Gaps may confuse that definition, so that CpHpG still---   counts as CpG, because the H is gapped.  That might actually---   be desirable.------ * Conditional substitutions.  The 5' and 3' ends count as damaged if---   the very last position has a C-to-T substitution.  With that in---   mind, 'substs5d5', 'substs5d3', 'substs5dd' are like 'substs5', but---   counting only reads where the 5' end is damaged, where the 3' end---   is damaged, and where both ends are damaged, respectively.--data DmgStats a = DmgStats {-    basecompo5 :: CompositionStats,-    basecompo3 :: CompositionStats,-    substs5    :: SubstitutionStats,-    substs3    :: SubstitutionStats,-    substs5d5  :: SubstitutionStats,-    substs3d5  :: SubstitutionStats,-    substs5d3  :: SubstitutionStats,-    substs3d3  :: SubstitutionStats,-    substs5dd  :: SubstitutionStats,-    substs3dd  :: SubstitutionStats,-    substs5cpg :: SubstitutionStats,-    substs3cpg :: SubstitutionStats,-    stats_more :: a }-  deriving Show--type CompositionStats  = [( Maybe Nucleotide, U.Vector Int )]-type SubstitutionStats = [( Subst, U.Vector Int )]---data FragType = Complete | Leading | Trailing deriving (Show, Eq)---- | Compact storage of a pair of ambiguous 'Nucleotides'.  Used to--- represent alignments in a way that is accessible even to assembly--- code.  The first and sencond field are stored in the low and high--- nybble, respectively.  See 'fst_np', 'snd_np', 'npair'.-newtype NPair = NPair Word8 deriving (Eq, Ord)--npair :: Nucleotides -> Nucleotides -> NPair-npair (Ns r) (Ns q) = NPair $ shiftL q 4 .|. r .&. 0xF--fst_np, snd_np :: NPair -> Nucleotides-fst_np (NPair w) = Ns (w .&. 0xF)-snd_np (NPair w) = Ns (shiftR w 4)--instance Storable NPair where-    sizeOf    _ = 1-    alignment _ = 1-    peek p = NPair <$> peek (castPtr p :: Ptr Word8)-    poke p (NPair v) = poke (castPtr p :: Ptr Word8) v--instance Show NPair where-    showsPrec _ p = shows (fst_np p) . (:) '/' . shows (snd_np p)---- | Alignment record.  The reference sequence is filled with Ns if--- missing.-data Alignment = ALN-    { a_sequence :: !(VS.Vector NPair)      -- the alignment proper-    , a_fragment_type :: !FragType }        -- was the adapter trimmed?--addFragType :: BamMeta -> Enumeratee [BamRaw] [(BamRaw,FragType)] m b-addFragType meta = mapStream $ \br -> (br, case unpackBam br of-    b | isFirstMate  b && isPaired     b -> Leading-      | isSecondMate b && isPaired     b -> Trailing-      | not sane                         -> Complete     -- leeHom fscked it up-      | isFirstMate  b || isSecondMate b -> Complete     -- old style flagging-      | isTrimmed    b || isMerged     b -> Complete     -- new style flagging-      | otherwise                        -> Leading)-  where-    sane = null [ () | ("PG",line) <- meta_other_shit meta-                     , ("PN","mergeTrimReadsBAM") <- line ]---- | Enumeratee (almost) that computes some statistics from plain BAM--- (no MD field needed) and a 2bit file.  The 'Alignment' is also--- reconstructed and passed downstream.  The result of any downstream--- processing is available in the 'stats_more' field of the result.------ * Get the reference sequence including both contexts once.  If this---   includes invalid sequence (negative coordinate), pad suitably.--- * Accumulate counts for the valid parts around 5' and 3' ends as---   appropriate from flags and config.--- * Combine the part that was aligned to (so no context) with the read---   to reconstruct the alignment.------ Arguments are the table of reference names, the 2bit file with the--- reference, the amount of context outside the alignment desired, and--- the amount of context inside desired.------ For 'Complete' fragments, we cut the read in the middle, so the 5'--- and 3' plots stay clean from each other's influence.  'Leading' and--- 'Trailing' fragments count completely towards the appropriate end.--damagePatternsIter2Bit :: MonadIO m-                       => Refs -> TwoBitFile -> Int -> Int-                       -> Iteratee [Alignment] m b-                       -> Iteratee [(BamRaw,FragType)] m (DmgStats b)-damagePatternsIter2Bit refs tbf ctx rng it =-    mapMaybeStream (\(br,ft) -> do-        let b@BamRec{..} = unpackBam br-        guard (not $ isUnmapped b)-        let ref_nm = sq_name $ getRef refs b_rname-            ref    = getFragment tbf ref_nm (b_pos - ctx) (alignedLength b_cigar + 2*ctx)-            pps    = aln_from_ref (U.drop ctx ref) b_seq b_cigar-        return (b, ft, ref, pps)) =$-    damagePatternsIter ctx rng it---- | Enumeratee (almost) that computes some statistics from plain BAM--- with a valid MD field.  The 'Alignment' is also reconstructed and--- passed downstream.  The result of any downstream processing is--- available in the 'stats_more' field of the result.------ * Reconstruct the alignment from CIGAR, SEQ, and MD.--- * Filter the alignment to get the reference sequence, accumulate it.--- * Accumulate everything over the alignment.------ The argument is the amount of context inside desired.------ For 'Complete' fragments, we cut the read in the middle, so the 5'--- and 3' plots stay clean from each other's influence.  'Leading' and--- 'Trailing' fragments count completely towards the appropriate end.--damagePatternsIterMD :: MonadIO m-                     => Int -> Iteratee [Alignment] m b-                     -> Iteratee [(BamRaw,FragType)] m (DmgStats b)-damagePatternsIterMD rng it =-    mapMaybeStream (\(br,ft) -> do-        let b@BamRec{..} = unpackBam br-        guard (not $ isUnmapped b)-        md <- getMd b-        let pps = alnFromMd b_seq b_cigar md-            ref = U.convert $ VS.map fromN $ VS.filter ((/=) gap) $ VS.map fst_np pps-        return (b, ft, ref, pps)) =$-    damagePatternsIter 0 rng it-  where-    fromN ns | ns == nucsA = 2-             | ns == nucsC = 1-             | ns == nucsG = 3-             | ns == nucsT = 0-             | otherwise   = 4---- | Common logic for statistics. The function 'get_ref_and_aln'--- reconstructs reference sequence and alignment from a Bam record.  It--- is expected to construct the alignment with respect to the forwards--- strand of the reference; we reverse-complement it if necessary.-damagePatternsIter :: MonadIO m-                   => Int -> Int-                   -> Iteratee [Alignment] m b-                   -> Iteratee [(BamRec, FragType, U.Vector Word8, VS.Vector NPair)] m (DmgStats b)-damagePatternsIter ctx rng it = mapStream revcom_both =$ do-    let maxwidth = ctx + rng-    acc_bc <- liftIO $ UM.replicate (2 * 5 *    maxwidth) (0::Int)-    acc_st <- liftIO $ UM.replicate (2 * 4 * 4 * 4 * rng) (0::Int)-    acc_cg <- liftIO $ UM.replicate (2 * 2 * 4 *     rng) (0::Int)--    it' <- flip mapStreamM it $ \(BamRec{..}, a_fragment_type, ref, a_sequence) -> liftIO $ do-              -- basecompositon near 5' end, near 3' end-              let (width5, width3) = case a_fragment_type of-                                            Leading -> (full_width, 0)-                                            Trailing -> (0, full_width)-                                            Complete -> (half_width, half_width)-                        where full_width = min (U.length ref) $ ctx + min rng (alignedLength b_cigar)-                              half_width = min (U.length ref) $ ctx + min rng (alignedLength b_cigar `div` 2)-              mapM_ (\i -> bump (fromIntegral (ref U.!  i                   ) * maxwidth + i) acc_bc) [0 .. width5-1]-              mapM_ (\i -> bump (fromIntegral (ref U.! (i + U.length ref) +6) * maxwidth + i) acc_bc) [-width3 .. -1]--              -- For substitutions, decide what damage class we're in:-              -- 0 - no damage, 1 - damaged 5' end, 2 - damaged 3' end, 3 - both-              let dmgbase = 2*4*4*rng * ( (if VS.null a_sequence || VS.head a_sequence /= npair nucsC nucsT then 1 else 0)-                                        + (if VS.null a_sequence || VS.last a_sequence /= npair nucsC nucsT then 2 else 0) )--              -- substitutions near 5' end-              let len_at_5 = case a_fragment_type of Leading  -> min rng (G.length b_seq)-                                                     Complete -> min rng (G.length b_seq `div` 2)-                                                     Trailing -> 0-              flip G.imapM_ (VS.take len_at_5 a_sequence) $-                    \i uv -> withPair uv $ \j -> bump (j * rng + i + dmgbase) acc_st--              -- substitutions at CpG sites near 5' end-              G.izipWithM_-                  (\i uv wz ->-                      when (fst_np uv == nucsC && fst_np wz == nucsG) $ do-                          withNs (snd_np uv) $ \y -> bump (  y   * rng +  i ) acc_cg-                          withNs (snd_np wz) $ \y -> bump ((y+4) * rng + i+1) acc_cg)-                  (VS.take len_at_5 a_sequence) (VS.drop 1 a_sequence)--              -- substitutions near 3' end-              let len_at_3 = case a_fragment_type of Leading  -> 0-                                                     Complete -> min rng (G.length b_seq `div` 2)-                                                     Trailing -> min rng (G.length b_seq)-              flip G.imapM_ (VS.take len_at_3 (VS.reverse a_sequence)) $-                    \i uv -> withPair uv $ \j -> bump ((17+j) * rng -i -1 + dmgbase) acc_st--              -- substitutions at CpG sites near 3' end-              G.izipWithM_-                  (\i wz uv ->-                      when (fst_np uv == nucsC && fst_np wz == nucsG) $ do-                          withNs (snd_np uv) $ \y -> bump ((y+ 9) * rng - i-2) acc_cg-                          withNs (snd_np wz) $ \y -> bump ((y+13) * rng - i-1) acc_cg)-                  (VS.take len_at_3 (VS.reverse a_sequence))-                  (VS.drop 1 (VS.reverse a_sequence))--              return ALN{..}---    let nsubsts = 4*4*rng-        mk_substs off = sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((4*i+j)*rng + off*nsubsts) rng acc_st)-                                 | (i,n1) <- zip [0..] [nucA..nucT]-                                 , (j,n2) <- zip [0..] [nucA..nucT] ]--    accs <- liftIO $ DmgStats <$> sequence [ (,) nuc <$> U.unsafeFreeze (UM.slice (i*maxwidth) maxwidth acc_bc)-                                           | (i,nuc) <- zip [2,1,3,0,4] [Just nucA,Just nucC,Just nucG,Just nucT,Nothing] ]-                              <*> sequence [ (,) nuc <$> U.unsafeFreeze (UM.slice (i*maxwidth) maxwidth acc_bc)-                                           | (i,nuc) <- zip [7,6,8,5,9] [Just nucA,Just nucC,Just nucG,Just nucT,Nothing] ]--                              <*> mk_substs 0-                              <*> mk_substs 1-                              <*> mk_substs 2-                              <*> mk_substs 3-                              <*> mk_substs 4-                              <*> mk_substs 5-                              <*> mk_substs 6-                              <*> mk_substs 7--                              <*> sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((i+j)*rng) rng acc_cg)-                                           | (i,n1) <- [(0,nucC), (4,nucG)]-                                           , (j,n2) <- zip [0..] [nucA..nucT] ]--                              <*> sequence [ (,) (n1 :-> n2) <$> U.unsafeFreeze (UM.slice ((i+j)*rng) rng acc_cg)-                                           | (i,n2) <- [(8,nucC), (12,nucG)]-                                           , (j,n1) <- zip [0..] [nucA..nucT] ]--    accs' <- accs `liftM` lift (run it')-    return $ accs' { substs5   = mconcat [ substs5 accs', substs5d5 accs', substs5d3 accs', substs5dd accs' ]-                   , substs3   = mconcat [ substs3 accs', substs3d5 accs', substs3d3 accs', substs3dd accs' ]-                   , substs5d5 = mconcat [ substs5d5 accs', substs5dd accs']-                   , substs3d5 = mconcat [ substs3d5 accs', substs3dd accs']-                   , substs5d3 = mconcat [ substs5d3 accs', substs5dd accs']-                   , substs3d3 = mconcat [ substs3d3 accs', substs3dd accs'] }-  where-    {-# INLINE withPair #-}-    withPair (NPair i) k =-        case pairTab `U.unsafeIndex` fromIntegral i of-            j -> when (j >= 0) (k j)--    !pairTab = U.replicate 256 (-1) U.//-            [ (fromIntegral i, x*4+y) | (u,x) <- zip [nucsA, nucsC, nucsG, nucsT] [0,1,2,3]-                                      , (v,y) <- zip [nucsA, nucsC, nucsG, nucsT] [0,1,2,3]-                                      , let NPair i = npair u v ]-    {-# INLINE bump #-}-    bump i v = UM.unsafeRead v i >>= UM.unsafeWrite v i . succ--    {-# INLINE withNs #-}-    withNs ns k | ns == nucsA = k 0-                | ns == nucsC = k 1-                | ns == nucsG = k 2-                | ns == nucsT = k 3-                | otherwise   = return ()---instance Monoid a => Monoid (DmgStats a) where-    mappend = merge_dmg_stats mappend-    mempty = DmgStats { basecompo5 = empty_compo-                      , basecompo3 = empty_compo-                      , substs5    = empty_subst-                      , substs3    = empty_subst-                      , substs5d5  = empty_subst-                      , substs3d5  = empty_subst-                      , substs5d3  = empty_subst-                      , substs3d3  = empty_subst-                      , substs5dd  = empty_subst-                      , substs3dd  = empty_subst-                      , substs5cpg = empty_subst-                      , substs3cpg = empty_subst-                      , stats_more = mempty }-      where-        empty_compo = [ (nuc, U.empty) | nuc <- [Just nucA, Just nucC, Just nucG, Just nucT, Nothing] ]-        empty_subst = [ (n1 :-> n2, U.empty) | n1 <- [nucA..nucT], n2 <- [nucA..nucT] ]--instance Semigroup a => Semigroup (DmgStats a) where-    (<>) = merge_dmg_stats (<>)--merge_dmg_stats :: (a -> a -> a) -> DmgStats a -> DmgStats a -> DmgStats a-merge_dmg_stats plus a b =-    DmgStats { basecompo5 = zipWith s1 (basecompo5 a) (basecompo5 b)-             , basecompo3 = zipWith s1 (basecompo3 a) (basecompo3 b)-             , substs5    = zipWith s2 (substs5    a) (substs5    b)-             , substs3    = zipWith s2 (substs3    a) (substs3    b)-             , substs5d5  = zipWith s2 (substs5d5  a) (substs5d5  b)-             , substs3d5  = zipWith s2 (substs3d5  a) (substs3d5  b)-             , substs5d3  = zipWith s2 (substs5d3  a) (substs5d3  b)-             , substs3d3  = zipWith s2 (substs3d3  a) (substs3d3  b)-             , substs5dd  = zipWith s2 (substs5dd  a) (substs5dd  b)-             , substs3dd  = zipWith s2 (substs3dd  a) (substs3dd  b)-             , substs5cpg = zipWith s2 (substs5cpg a) (substs5cpg b)-             , substs3cpg = zipWith s2 (substs3cpg a) (substs3cpg b)-             , stats_more = plus       (stats_more a) (stats_more b) }-      where-        s1 (x, u) (z, v) | x /= z    = error "Mismatch in zip.  This is a bug."-                         | U.null u  = (x, v)-                         | U.null v  = (x, u)-                         | otherwise = (x, U.zipWith (+) u v)--        s2 (x :-> y, u) (z :-> w, v) | x /= z || y /= w = error "Mismatch in zip.  This is a bug."-                                     | U.null u         = (x :-> y, v)-                                     | U.null v         = (x :-> y, u)-                                     | otherwise        = (x :-> y, U.zipWith (+) u v)---revcom_both :: ( BamRec, FragType, U.Vector Word8, VS.Vector NPair )-            -> ( BamRec, FragType, U.Vector Word8, VS.Vector NPair )-revcom_both (b, ft, ref, pps)-    | isReversed b = ( b, ft, revcom_ref ref, revcom_pairs pps )-    | otherwise    = ( b, ft,            ref,              pps )-  where-    revcom_ref   =  U.reverse .  U.map (\c -> if c > 3 then c else xor c 2)-    revcom_pairs = VS.reverse . VS.map (\p -> npair (compls $ fst_np p) (compls $ snd_np p))----- | Reconstructs the alignment from reference, query, and cigar.  Only--- positions where the query is not gapped are produced.-aln_from_ref :: U.Vector Word8 -> Vector_Nucs_half Nucleotides -> VS.Vector Cigar -> VS.Vector NPair-aln_from_ref ref0 qry0 cig0 = VS.fromList $ step ref0 qry0 cig0-  where-    step ref qry cig1-        | U.null ref || G.null qry || G.null cig1 = []-        | otherwise = case G.unsafeHead cig1 of { op :* n ->-                      case G.unsafeTail cig1 of { cig ->-                      case op of {--        Mat -> zipWith (npair . nn) (G.toList (G.take n ref))-                                    (G.toList (G.take n qry)) ++ step (G.drop n ref) (G.drop n qry) cig ;-        Del ->                                                   step (G.drop n ref)           qry  cig ;-        Ins -> map (npair gap) (G.toList (G.take n qry))      ++ step           ref  (G.drop n qry) cig ;-        SMa -> map (npair gap) (G.toList (G.take n qry))      ++ step           ref  (G.drop n qry) cig ;-        HMa -> replicate n (npair gap nucsN)                  ++ step           ref            qry  cig ;-        Nop ->                                                   step           ref            qry  cig ;-        Pad ->                                                   step           ref            qry  cig }}}--    nn 0 = nucsT-    nn 1 = nucsC-    nn 2 = nucsA-    nn 3 = nucsG-    nn _ = nucsN----- | Reconstructs the alignment from query, cigar, and md.  Only--- positions where the query is not gapped are produced.-alnFromMd :: Vector_Nucs_half Nucleotides -> VS.Vector Cigar -> [MdOp] -> VS.Vector NPair-alnFromMd qry0 cig0 md0 = VS.fromList $ step qry0 cig0 md0-  where-    step qry cig1 md-        | G.null qry || G.null cig1 || null md = []-        | otherwise = case G.unsafeHead cig1 of op :* n -> step' qry op n (G.unsafeTail cig1) md--    step' qry  _ 0 cig             md  = step  qry      cig md-    step' qry op n cig (MdNum  0 : md) = step' qry op n cig md-    step' qry op n cig (MdDel [] : md) = step' qry op n cig md--    step' qry Mat n cig (MdNum m : md)-            | n <  m =    map twin (G.toList (G.take n qry)) ++ step  (G.drop n qry)           cig (MdNum (m-n) : md)-            | n >  m =    map twin (G.toList (G.take m qry)) ++ step' (G.drop m qry) Mat (n-m) cig                md-            | n == m =    map twin (G.toList (G.take n qry)) ++ step  (G.drop n qry)           cig                md-    step' qry Mat n cig (MdRep c : md) = npair c (G.head qry) : step' (G.tail   qry) Mat (n-1) cig                md-    step'   _ Mat _   _          _     = []--    step' qry Del n cig (MdDel (_:ss) : md) = step' qry Del (n-1) cig (MdDel ss : md)-    step'   _ Del _   _               _     = []--    step' qry Ins n cig                 md  = map (npair gap) (G.toList (G.take n qry)) ++ step (G.drop n qry) cig md-    step' qry SMa n cig                 md  = map (npair gap) (G.toList (G.take n qry)) ++ step (G.drop n qry) cig md-    step' qry HMa n cig                 md  =             replicate n (npair gap nucsN) ++ step           qry  cig md-    step' qry Nop _ cig                 md  =                                              step           qry  cig md-    step' qry Pad _ cig                 md  =                                              step           qry  cig md--    twin q = npair q q---- | Number of mismatches allowed by BWA.--- @bwa_cal_maxdiff thresh len@ returns the number of mismatches--- @bwa aln -n $tresh@ would allow in a read of length @len@.  For--- reference, here is the code from BWA that computes it (we assume @err--- = 0.02@, just like BWA):------ @--- int bwa_cal_maxdiff(int l, double err, double thres)---   {---      double elambda = exp(-l * err);---      double sum, y = 1.0;---      int k, x = 1;---      for (k = 1, sum = elambda; k < 1000; ++k) {---          y *= l * err;---          x *= k;---          sum += elambda * y / x;---          if (1.0 - sum < thres) return k;---      }---      return 2;---   }--- @--bwa_cal_maxdiff :: Double -> Int -> Int-bwa_cal_maxdiff thresh len = k_fin-1-  where-    (k_fin, _, _, _) : _ = dropWhile bad $ iterate step (1,elambda,1,1)--    err = 0.02-    elambda = exp . negate $ fromIntegral len * err--    step (k, s, x, y) = (k+1, s', x', y')-      where y' = y * fromIntegral len * err-            x' = x * fromIntegral k-            s' = s + elambda * y' / x'--    bad (_, s, _, _) = 1-s >= thresh-
− src/Bio/Align.hs
@@ -1,85 +0,0 @@-module Bio.Align (-    Mode(..),-    myersAlign,-    showAligned-                 ) where--import Bio.Prelude       hiding ( lefts, rights )-import Foreign.C.String         ( CString )-import Foreign.C.Types          ( CInt(..) )-import Foreign.Marshal.Alloc    ( allocaBytes )--import qualified Data.ByteString.Char8      as S-import qualified Data.ByteString.Unsafe     as S-import qualified Data.ByteString.Lazy.Char8 as L--foreign import ccall unsafe "myers_align.h myers_diff" myers_diff ::-        CString -> CInt ->              -- sequence A and length A-        CInt ->                         -- mode (an enum)-        CString -> CInt ->              -- sequence B and length B-        CInt ->                         -- max distance-        CString ->                      -- backtracing space A-        CString ->                      -- backtracing space B-        IO CInt                         -- returns distance---- | Mode argument for 'myersAlign', determines where free gaps are--- allowed.-data Mode = Globally  -- ^ align globally, without gaps at either end-          | HasPrefix -- ^ align so that the second sequence is a prefix of the first-          | IsPrefix  -- ^ align so that the first sequence is a prefix of the second-    deriving Enum---- | Align two strings.  @myersAlign maxd seqA mode seqB@ tries to align--- @seqA@ to @seqB@, which will work as long as no more than @maxd@ gaps--- or mismatches are incurred.  The @mode@ argument determines if either--- of the sequences is allowed to have an overhanging tail.------ The result is the triple of the actual distance (gaps + mismatches)--- and the two padded sequences.  These sequences are the original--- sequences with dashes inserted for gaps.------ The algorithm is the O(nd) algorithm by Myers, implemented in C.  A--- gap and a mismatch score the same.  The strings are supposed to code--- for DNA, the code understands IUPAC-IUB ambiguity codes.  Two--- characters match iff there is at least one nucleotide both can code--- for.  Note that N is a wildcard, while X matches nothing.--myersAlign :: Int -> Bytes -> Mode -> Bytes -> (Int, Bytes, Bytes)-myersAlign maxd seqA mode seqB =-    unsafePerformIO                                 $-    S.unsafeUseAsCStringLen seqA                    $ \(seq_a, len_a) ->-    S.unsafeUseAsCStringLen seqB                    $ \(seq_b, len_b) ->--    -- size of output buffers derives from this:-    -- char *out_a = bt_a + len_a + maxd +2 ;-    -- char *out_b = bt_b + len_b + maxd +2 ;-    allocaBytes (len_a + maxd + 2)                  $ \bt_a ->-    allocaBytes (len_b + maxd + 2)                  $ \bt_b ->--    myers_diff seq_a (fromIntegral len_a)-               (fromIntegral $ fromEnum mode)-               seq_b (fromIntegral len_b)-               (fromIntegral maxd) bt_a bt_b      >>= \dist ->-    if dist < 0-      then return (maxBound, S.empty, S.empty)-      else (,,) (fromIntegral dist) <$>-           S.packCString bt_a <*>-           S.packCString bt_b----- | Nicely print an alignment.  An alignment is simply a list of--- strings with inserted gaps to make them align.  We split them into--- manageable chunks, stack them vertically and add a line showing--- asterisks in every column where all aligned strings agree.  The--- result is /almost/ the Clustal format.-showAligned :: Int -> [Bytes] -> [L.ByteString]-showAligned w ss | all S.null ss = []-                 | otherwise = map (L.fromChunks . (:[])) lefts ++-                               L.pack agreement :-                               L.empty :-                               showAligned w rights-  where-    (lefts, rights) = unzip $ map (S.splitAt w) ss-    agreement = map star $ S.transpose lefts-    star str = if S.null str || S.all (== S.head str) str then '*' else ' '-
− src/Bio/Bam.hs
@@ -1,24 +0,0 @@--- | Umbrella module for most of what's under 'Bio.Bam'.--module Bio.Bam (-    module Bio.Bam.Fastq,-    module Bio.Bam.Filter,-    module Bio.Bam.Header,-    module Bio.Bam.Index,-    module Bio.Bam.Reader,-    module Bio.Bam.Rec,-    module Bio.Bam.Trim,-    module Bio.Bam.Writer,-    module Bio.Iteratee-               ) where--import Bio.Bam.Fastq-import Bio.Bam.Filter-import Bio.Bam.Header-import Bio.Bam.Index-import Bio.Bam.Reader-import Bio.Bam.Rec-import Bio.Bam.Trim-import Bio.Bam.Writer-import Bio.Iteratee-
− src/Bio/Bam/Evan.hs
@@ -1,97 +0,0 @@--- | This module contains stuff relating to conventions local to MPI--- EVAN.  The code is needed regularly, but it can be harmful when--- applied to BAM files that follow different conventions.  Most--- importantly, no program should call these functions by default.--module Bio.Bam.Evan where--import Bio.Bam.Header-import Bio.Bam.Rec-import Data.Bits-import Prelude--import qualified Data.ByteString.Char8 as S---- | Fixes abuse of flags valued 0x800 and 0x1000.  We used them for--- low quality and low complexity, but they have since been redefined.--- If set, we clear them and store them into the ZQ field.  Also fixes--- abuse of the combination of the paired, 1st mate and 2nd mate flags--- used to indicate merging or trimming.  These are canonicalized and--- stored into the FF field.  This function is unsafe on BAM files of--- unclear origin!-fixupFlagAbuse :: BamRec -> BamRec-fixupFlagAbuse b =-    (if b_flag b .&. flag_low_quality /= 0 then setQualFlag 'Q' else id) $          -- low qual, new convention-    (if b_flag b .&. flag_low_complexity /= 0 then setQualFlag 'C' else id) $       -- low complexity, new convention-    b { b_flag = cleaned_flags, b_exts = cleaned_exts }-  where-        -- removes old flag abuse-        flags' = b_flag b .&. complement (flag_low_quality .|. flag_low_complexity)-        cleaned_flags | flags' .&. flagPaired == 0 = flags' .&. complement (flagFirstMate .|. flagSecondMate)-                      | otherwise                  = flags'--        flag_low_quality    =  0x800-        flag_low_complexity = 0x1000--        -- merged & trimmed from old flag abuse-        is_merged  = flags' .&. (flagPaired .|. flagFirstMate .|. flagSecondMate) == flagFirstMate .|. flagSecondMate-        is_trimmed = flags' .&. (flagPaired .|. flagFirstMate .|. flagSecondMate) == flagSecondMate-        newflags = (if is_merged then eflagMerged else 0) .|. (if is_trimmed then eflagTrimmed else 0)--        -- Extended flags, renamed to avoid collision with BWA.  Goes like this:  if FF is there, use-        -- it.  Else check if XF is there __and is numeric__.  If so, use it, remove it, and set FF-        -- instead.  Else use 0 and leave it alone.  Note that this resolves the collision with BWA,-        -- since BWA puts a character there, not an int.-        cleaned_exts = case (lookup "FF" (b_exts b), lookup "XF" (b_exts b)) of-                ( Just (Int i), _ ) -> updateE "FF" (Int (i .|. newflags))                (b_exts b)-                ( _, Just (Int i) ) -> updateE "FF" (Int (i .|. newflags)) $ deleteE "XF" (b_exts b)-                _ | newflags /= 0   -> updateE "FF" (Int        newflags )                (b_exts b)-                  | otherwise       ->                                                     b_exts b----- | Fixes typical inconsistencies produced by Bwa: sometimes, 'mate unmapped' should be set, and we--- can see it, because we match the mate's coordinates.  Sometimes 'properly paired' should not be--- set, because one mate is unmapped.  This function is generally safe, but needs to be called only--- on the output of affected (older?) versions of Bwa.-fixupBwaFlags :: BamRec -> BamRec-fixupBwaFlags b = b { b_flag = fixPP $ b_flag b .|. if mu then flagMateUnmapped else 0 }-  where-        -- Set "mate unmapped" if self coordinates and mate coordinates are equal, but self is-        -- paired and mapped.  (BWA forgets this flag for invalid mate alignments)-        mu = and [ isPaired b, not (isUnmapped b)-                 , isReversed b == isMateReversed b-                 , b_rname b == b_mrnm b, b_pos b == b_mpos b ]--        -- If either mate is unmapped, remove "properly paired".-        fixPP f | f .&. (flagUnmapped .|. flagMateUnmapped) == 0 = f-                | otherwise = f .&. complement flagProperlyPaired----- | Removes syntactic warts from old read names or the read names used--- in FastQ files.-removeWarts :: BamRec -> BamRec-removeWarts br = br { b_qname = name, b_flag = flags, b_exts = tags }-  where-    (name, flags, tags) = checkFR $ checkC $ checkSharp (b_qname br, b_flag br, b_exts br)--    checkFR (n,f,t) | "F_" `S.isPrefixOf` n = checkC (S.drop 2 n, f .|. flagFirstMate  .|. flagPaired, t)-                    | "R_" `S.isPrefixOf` n = checkC (S.drop 2 n, f .|. flagSecondMate .|. flagPaired, t)-                    | "M_" `S.isPrefixOf` n = checkC (S.drop 2 n, f,   insertE "FF" (Int  eflagMerged) t)-                    | "T_" `S.isPrefixOf` n = checkC (S.drop 2 n, f,   insertE "FF" (Int eflagTrimmed) t)-                    | "/1" `S.isSuffixOf` n =        ( rdrop 2 n, f .|. flagFirstMate  .|. flagPaired, t)-                    | "/2" `S.isSuffixOf` n =        ( rdrop 2 n, f .|. flagSecondMate .|. flagPaired, t)-                    | otherwise             =        (         n, f,                                   t)--    checkC (n,f,t) | "C_" `S.isPrefixOf` n  = (S.drop 2 n, f, insertE "XP" (Int (-1)) t)-                   | otherwise              = (         n, f,                         t)--    rdrop n s = S.take (S.length s - n) s--    checkSharp (n,f,t) = case S.split '#' n of [n',ts] -> (n', f, insertTags ts t)-                                               _       -> ( n, f,               t)--    insertTags ts t | S.null y  = insertE "XI" (Text ts) t-                    | otherwise = insertE "XI" (Text  x) $ insertE "XJ" (Text $ S.tail y) t-        where (x,y) = S.break (== ',') ts--
− src/Bio/Bam/Fastq.hs
@@ -1,113 +0,0 @@--- | Parser for @FastA/FastQ@, 'Iteratee' style, based on--- "Data.Attoparsec", and written such that it is compatible with module--- 'Bio.Bam'.  This gives import of @FastA/FastQ@ while respecting some--- local (to MPI EVAN) conventions.--module Bio.Bam.Fastq ( parseFastq, parseFastq', parseFastqCassava ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Prelude hiding ( isSpace )-import Bio.Iteratee-import Data.Attoparsec.ByteString.Char8-        ( char, skipSpace, satisfy, inClass, skipWhile, takeTill-        , scan, isSpace, isSpace_w8, (<?>) )--import qualified Data.Attoparsec.ByteString.Char8   as P-import qualified Data.ByteString                    as B-import qualified Data.ByteString.Char8              as S-import qualified Data.Vector.Generic                as V---- | Reader for DNA (not protein) sequences in FastA and FastQ.  We read--- everything vaguely looking like FastA or FastQ, then shoehorn it into--- a BAM record.  We strive to extract information following more or--- less established conventions from the header, but don't aim for--- completeness.  The recognized syntactical warts are converted into--- appropriate flags and removed.  Only the canonical variant of FastQ--- is supported (qualities stored as raw bytes with offset 33).------ Supported additional conventions:------ * A name suffix of @/1@ or @/2@ is turned into the first mate or second---   mate flag and the read is flagged as paired.------ * Same for name prefixes of @F_@ or @R_@, respectively.------ * A name prefix of @M_@ flags the sequence as unpaired and merged------ * A name prefix of @T_@ flags the sequence as unpaired and trimmed------ * A name prefix of @C_@, optionally before or after any of the other---   prefixes, is turned into the extra flag @XP:i:-1@ (result of---   duplicate removal with unknown duplicate count).------ * A collection of tags separated from the name by an octothorpe is---   removed and put into the fields @XI@ and @XJ@ as text.------ Everything before the first sequence header is ignored.  Headers can--- start with @\>@ or @\@@, we treat both equally.  The first word of--- the header becomes the read name, the remainder of the header is--- ignored.  The sequence can be split across multiple lines;--- whitespace, dashes and dots are ignored, IUPAC-IUB ambiguity codes--- are accepted as bases, anything else causes an error.  The sequence--- ends at a line that is either a header or starts with @\+@, in the--- latter case, that line is ignored and must be followed by quality--- scores.  There must be exactly as many Q-scores as there are bases,--- followed immediately by a header or end-of-file.  Whitespace is--- ignored.--parseFastq :: Monad m => Enumeratee Bytes [ BamRec ] m a-parseFastq = parseFastq' (const id)---- | Like 'parseFastq', but also------ * If the first word of the description has at least four colon---   separated subfields, the first is used to flag first/second mate,---   the second is the \"QC failed\" flag, and the fourth is the index---   sequence.--parseFastqCassava :: Monad m => Enumeratee Bytes [ BamRec ] m a-parseFastqCassava = parseFastq' (pdesc . S.split ':' . S.takeWhile (' ' /=))-  where-    pdesc (num:flg:_:idx:_) br = br { b_flag = sum [ if num == "1" then flagFirstMate .|. flagPaired else 0-                                                   , if num == "2" then flagSecondMate .|. flagPaired else 0-                                                   , if flg == "Y" then flagFailsQC else 0-                                                   , b_flag br .&. complement (flagFailsQC .|. flagSecondMate .|. flagPaired) ]-                                    , b_exts = if S.all (`S.elem` "ACGTN") idx then insertE "XI" (Text idx) (b_exts br) else b_exts br }-    pdesc _ br = br---- | Same as 'parseFastq', but a custom function can be applied to the--- description string (the part of the header after the sequence name),--- which can modify the parsed record.  Note that the quality field can--- end up empty.--parseFastq' :: Monad m => ( Bytes -> BamRec -> BamRec ) -> Enumeratee Bytes [ BamRec ] m a-parseFastq' descr it = do skipJunk ; convStream (parserToIteratee $ (:[]) <$> pRec) it-  where-    isCBase   = inClass "ACGTUBDHVSWMKRYNacgtubdhvswmkryn"-    canSkip c = isSpace c || c == '.' || c == '-'-    isHdr   c = c == '@' || c == '>'--    pRec   = (satisfy isHdr <?> "start marker") *> (makeRecord <$> pName <*> (descr <$> P.takeWhile ('\n' /=)) <*> (pSeq >>= pQual))-    pName  = takeTill isSpace <* skipWhile (\c -> c /= '\n' && isSpace c)  <?> "read name"-    pSeq   =     (:) <$> satisfy isCBase <*> pSeq-             <|> satisfy canSkip *> pSeq-             <|> pure []                                                   <?> "sequence"--    pQual sq = (,) sq <$> (char '+' *> skipWhile ('\n' /=) *> pQual' (length sq) <* skipSpace <|> return S.empty)  <?> "qualities"-    pQual' n = B.filter (not . isSpace_w8) <$> scan n step-    step 0 _ = Nothing-    step i c | isSpace c = Just i-             | otherwise = Just (i-1)--skipJunk :: Monad m => Iteratee Bytes m ()-skipJunk = peekStreamBS >>= check-  where-    check (Just c) | bad c = dropWhileStreamBS (c2w '\n' /=) >> dropStreamBS 1 >> skipJunk-    check _                = return ()-    bad c = c /= c2w '>' && c /= c2w '@'--makeRecord :: Seqid -> (BamRec->BamRec) -> (String, Bytes) -> BamRec-makeRecord name extra (sq,qual) = extra $ nullBamRec-        { b_qname = name, b_seq = V.fromList $ read sq, b_qual = V.fromList $ map (Q . subtract 33) $ B.unpack qual }-
− src/Bio/Bam/Filter.hs
@@ -1,127 +0,0 @@--- | Quality filters adapted from prehistoric pipeline.--module Bio.Bam.Filter (-    filterPairs, QualFilter,-    complexSimple, complexEntropy,-    qualityAverage, qualityMinimum,-    qualityFromOldIllumina, qualityFromNewIllumina-                      ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Base-import Bio.Iteratee-import Data.Bits-import Prelude--import qualified Data.Vector.Generic as V---- | A filter/transformation applied to pairs of reads.  We supply a--- predicate to be applied to single reads and one to be applied to--- pairs, tha latter can get incomplete pairs, too, if mates have been--- separated or filtered asymmetrically.--filterPairs :: Monad m => (BamRec -> [BamRec])-                       -> (Maybe BamRec -> Maybe BamRec -> [BamRec])-                       -> Enumeratee [BamRec] [BamRec] m a-filterPairs ps pp = eneeCheckIfDone step-  where-    step k = tryHead >>= step' k-    step' k Nothing = return $ liftI k-    step' k (Just b)-        | isPaired b = tryHead >>= step'' k b-        | otherwise  = case ps b of [] -> step k ; b' -> eneeCheckIfDone step . k $ Chunk b'--    step'' k b Nothing = case pp (Just b) Nothing of-                            [] -> return $ liftI k-                            b' -> return $ k $ Chunk b'--    step'' k b (Just c)-        | b_rname b /= b_rname c || not (isPaired c) =-                let b' = if isSecondMate b then pp Nothing (Just b) else pp (Just b) Nothing-                in case b' of [] -> step' k (Just c)-                              _  -> eneeCheckIfDone (\k' -> step' k' (Just c)) . k $ Chunk b'--        | isFirstMate c && isSecondMate b = step''' k c b-        | otherwise                       = step''' k b c--    step''' k b c = case pp (Just b) (Just c) of [] -> step k-                                                 b' -> eneeCheckIfDone step . k $ Chunk b'----- | A quality filter is simply a transformation on @BamRec@s.  By--- convention, quality filters should set @flagFailsQC@, a further step--- can then remove the failed reads.  Filtering of individual reads--- tends to result in mate pairs with inconsistent flags, which in turn--- will result in lone mates and all sort of troubles with programs that--- expect non-broken BAM files.  It is therefore recommended to use--- @pairFilter@ with suitable predicates to do the post processing.--type QualFilter = BamRec -> BamRec--{-# INLINE count #-}-count :: (V.Vector v a, Eq a) => a -> v a -> Int-count x = V.foldl' (\acc y -> if x == y then acc+1 else acc) 0---- | Simple complexity filter aka "Nancy Filter".  A read is considered--- not-sufficiently-complex if the most common base accounts for greater--- than the @cutoff@ fraction of all non-N bases.-{-# INLINE complexSimple #-}-complexSimple :: Double -> QualFilter-complexSimple r b = if p then b else b'-  where-    b' = setQualFlag 'C' $ b { b_flag = b_flag b .|. flagFailsQC }-    p  = let counts = [ count x $ b_seq b | x <- properBases ]-             lim = floor $ r * fromIntegral (sum counts)-         in all (<= lim) counts---- | Filter on order zero empirical entropy.  Entropy per base must be--- greater than cutoff.-{-# INLINE complexEntropy #-}-complexEntropy :: Double -> QualFilter-complexEntropy r b = if p then b else b'-  where-    b' = setQualFlag 'C' $ b { b_flag = b_flag b .|. flagFailsQC }-    p = ent >= r * total--    counts = [ count x $ b_seq b | x <- properBases ]-    total = fromIntegral $ V.length $ b_seq b-    ent   = sum [ fromIntegral c * log (total / fromIntegral c) | c <- counts, c /= 0 ] / log 2---- | Filter on average quality.  Reads without quality string pass.-{-# INLINE qualityAverage #-}-qualityAverage :: Int -> QualFilter-qualityAverage q b = if p then b else b'-  where-    b' = setQualFlag 'Q' $ b { b_flag = b_flag b .|. flagFailsQC }-    p  = let total = V.foldl' (\a x -> a + fromIntegral (unQ x)) 0 $ b_qual b-         in total >= q * V.length (b_qual b)---- | Filter on minimum quality.  In @qualityMinimum n q@, a read passes--- if it has no more than @n@ bases with quality less than @q@.  Reads--- without quality string pass.-{-# INLINE qualityMinimum #-}-qualityMinimum :: Int -> Qual -> QualFilter-qualityMinimum n (Q q) b = if p then b else b'-  where-    b' = setQualFlag 'Q' $ b { b_flag = b_flag b .|. flagFailsQC }-    p  = V.length (V.filter (< Q q) (b_qual b)) <= n----- | Convert quality scores from old Illumina scale (different formula--- and offset 64 in FastQ).-qualityFromOldIllumina :: BamRec -> BamRec-qualityFromOldIllumina b = b { b_qual = V.map conv $ b_qual b }-  where-    conv (Q s) = let s' :: Double-                     s' = exp $ log 10 * (fromIntegral s - 31) / (-10)-                     p  = s' / (1+s')-                     q  = - 10 * log p / log 10-                 in Q (round q)---- | Convert quality scores from new Illumina scale (standard formula--- but offset 64 in FastQ).-qualityFromNewIllumina :: BamRec -> BamRec-qualityFromNewIllumina b = b { b_qual = V.map (Q . subtract 31 . unQ) $ b_qual b }--
− src/Bio/Bam/Header.hs
@@ -1,397 +0,0 @@-module Bio.Bam.Header (-        BamMeta(..),-        parseBamMeta,-        parseBamMetaLine,-        showBamMeta,-        addPG,--        BamKey(..),-        BamHeader(..),-        BamSQ(..),-        BamSorting(..),-        BamOtherShit,--        Refseq(..),-        invalidRefseq,-        isValidRefseq,-        invalidPos,-        isValidPos,-        unknownMapq,-        isKnownMapq,--        Refs,-        noRefs,-        getRef,--        compareNames,--        flagPaired,-        flagProperlyPaired,-        flagUnmapped,-        flagMateUnmapped,-        flagReversed,-        flagMateReversed,-        flagFirstMate,-        flagSecondMate,-        flagAuxillary,-        flagSecondary,-        flagFailsQC,-        flagDuplicate,-        flagSupplementary,-        eflagTrimmed,-        eflagMerged,-        eflagAlternative,-        eflagExactIndex,--        distinctBin,--        MdOp(..),-        readMd,-        showMd-    ) where--import Bio.Prelude           hiding ( uncons )-import Data.ByteString              ( uncons )-import Data.ByteString.Builder      ( Builder, byteString, char7, intDec, word16LE )-import Data.Sequence                ( (><), (|>) )--import qualified Data.Attoparsec.ByteString.Char8   as P-import qualified Data.ByteString.Char8              as S-import qualified Data.Sequence                      as Z--data BamMeta = BamMeta {-        meta_hdr :: !BamHeader,-        meta_refs :: !Refs,-        meta_other_shit :: [(BamKey, BamOtherShit)],-        meta_comment :: [Bytes]-    } deriving Show---- | Exactly two characters, for the \"named\" fields in bam.-newtype BamKey = BamKey Word16-    deriving ( Eq, Ord )--instance IsString BamKey where-    {-# INLINE fromString #-}-    fromString [a,b]-        | ord a < 256 && ord b < 256-            = BamKey . fromIntegral $ ord a .|. shiftL (ord b) 8--    fromString s-            = error $ "Not a legal BAM key: " ++ show s--instance Show BamKey where-    show (BamKey a) = [ chr (fromIntegral a .&. 0xff), chr (shiftR (fromIntegral a) 8 .&. 0xff) ]--addPG :: Maybe Version -> IO (BamMeta -> BamMeta)-addPG vn = do-    args <- getArgs-    pn   <- getProgName-    return $ go args pn-  where-    go args pn bm = bm { meta_other_shit = ("PG",pg_line) : meta_other_shit bm }-      where-        pg_line = concat [ [ ("ID", pg_id) ]-                         , [ ("PN", S.pack pn) ]-                         , [ ("CL", S.pack $ unwords args) ]-                         , maybe [] (\v -> [("VN",S.pack (showVersion v))]) vn-                         , map (\p -> ("PP",p)) (take 1 pg_pp)-                         , map (\p -> ("pp",p)) (drop 1 pg_pp) ]--        pg_id : _ = filter (not . flip elem pg_ids) . map S.pack $-                      pn : [ pn ++ '-' : show i | i <- [(1::Int)..] ]--        pg_ids = [ pgid | ("PG",fs) <- meta_other_shit bm, ("ID",pgid) <- fs ]-        pg_pps = [ pgid | ("PG",fs) <- meta_other_shit bm, ("PP",pgid) <- fs ]--        pg_pp  = pg_ids \\ pg_pps---instance Monoid BamMeta where-    mempty = BamMeta mempty noRefs [] []-    mappend = (<>)--instance Semigroup BamMeta where-    a <> b = BamMeta { meta_hdr = meta_hdr a `mappend` meta_hdr b-                     , meta_refs = meta_refs a >< meta_refs b-                     , meta_other_shit = nub $ meta_other_shit a ++ meta_other_shit b-                     , meta_comment = nub $ meta_comment a ++ meta_comment b }--data BamHeader = BamHeader {-        hdr_version :: (Int, Int),-        hdr_sorting :: !BamSorting,-        hdr_other_shit :: BamOtherShit-    } deriving (Show, Eq)--instance Monoid BamHeader where-    mempty = BamHeader (1,0) Unknown []-    mappend = (<>)--instance Semigroup BamHeader where-    a <> b = BamHeader { hdr_version = hdr_version a `min` hdr_version b-                       , hdr_sorting = let u = hdr_sorting a ; v = hdr_sorting b in if u == v then u else Unknown-                       , hdr_other_shit = hdr_other_shit a ++ hdr_other_shit b }--data BamSQ = BamSQ {-        sq_name :: Seqid,-        sq_length :: Int,-        sq_other_shit :: BamOtherShit-    } deriving (Show, Eq)--bad_seq :: BamSQ-bad_seq = BamSQ (error "no SN field") (error "no LN field") []---- | Possible sorting orders from bam header.  Thanks to samtools, which--- doesn't declare sorted files properly, we have to have the stupid--- 'Unknown' state, too.-data BamSorting = Unknown | Unsorted | Grouped | Queryname | Coordinate | GroupSorted-    deriving (Show, Eq)--type BamOtherShit = [(BamKey, Bytes)]--parseBamMeta :: P.Parser BamMeta-parseBamMeta = fixup . foldl' (flip ($)) mempty <$> P.sepBy parseBamMetaLine (P.skipWhile (=='\t') >> P.char '\n')-  where-    fixup meta = meta { meta_other_shit = reverse (meta_other_shit meta)-                      , meta_comment    = reverse (meta_comment    meta) }--parseBamMetaLine :: P.Parser (BamMeta -> BamMeta)-parseBamMetaLine = P.char '@' >> P.choice [hdLine, sqLine, coLine, otherLine]-  where-    hdLine = P.string "HD\t" >>-             (\fns meta -> let fixup hdr = hdr { hdr_other_shit = reverse (hdr_other_shit hdr) }-                           in meta { meta_hdr = fixup $! foldl' (flip ($)) (meta_hdr meta) fns })-               <$> P.sepBy1 (P.choice [hdvn, hdso, hdother]) tabs--    sqLine = P.string "SQ\t" >>-             (\fns meta -> let fixup sq = sq { sq_other_shit = reverse (sq_other_shit sq) }-                               !s = fixup $ foldl' (flip ($)) bad_seq fns-                           in meta { meta_refs = meta_refs meta |> s })-               <$> P.sepBy1 (P.choice [sqnm, sqln, sqother]) tabs--    hdvn = P.string "VN:" >>-           (\a b hdr -> hdr { hdr_version = (a,b) })-             <$> P.decimal <*> ((P.char '.' <|> P.char ':') >> P.decimal)--    hdso = P.string "SO:" >>-           (\s hdr -> hdr { hdr_sorting = s })-             <$> P.choice [ Grouped     <$ P.string "grouped"-                          , Queryname   <$ P.string "queryname"-                          , Coordinate  <$ P.string "coordinate"-                          , GroupSorted <$ P.string "groupsort"-                          , Unsorted    <$ P.string "unsorted"-                          , Unknown     <$ P.skipWhile (\c -> c/='\t' && c/='\n') ]--    sqnm = P.string "SN:" >> (\s sq -> sq { sq_name = s }) <$> pall-    sqln = P.string "LN:" >> (\i sq -> sq { sq_length = i }) <$> P.decimal--    hdother = (\t hdr -> t `seq` hdr { hdr_other_shit = t : hdr_other_shit hdr }) <$> tagother-    sqother = (\t sq  -> t `seq` sq  { sq_other_shit = t : sq_other_shit sq }) <$> tagother--    coLine = P.string "CO\t" >>-             (\s meta -> s `seq` meta { meta_comment = s : meta_comment meta })-               <$> P.takeWhile (/= 'n')--    otherLine = (\k ts meta -> meta { meta_other_shit = (k,ts) : meta_other_shit meta })-                  <$> bamkey <*> (tabs >> P.sepBy1 tagother tabs)--    tagother :: P.Parser (BamKey,Bytes)-    tagother = (,) <$> bamkey <*> (P.char ':' >> pall)--    tabs = P.char '\t' >> P.skipWhile (== '\t')--    pall :: P.Parser Bytes-    pall = P.takeWhile (\c -> c/='\t' && c/='\n')--    bamkey :: P.Parser BamKey-    bamkey = (\a b -> fromString [a,b]) <$> P.anyChar <*> P.anyChar--showBamMeta :: BamMeta -> Builder-showBamMeta (BamMeta h ss os cs) =-    show_bam_meta_hdr h <>-    foldMap show_bam_meta_seq ss <>-    foldMap show_bam_meta_other os <>-    foldMap show_bam_meta_comment cs-  where-    show_bam_meta_hdr (BamHeader (major,minor) so os') =-        byteString "@HD\tVN:" <>-        intDec major <> char7 '.' <> intDec minor <>-        byteString (case so of Unknown     -> mempty-                               Unsorted    -> "\tSO:unsorted"-                               Grouped     -> "\tSO:grouped"-                               Queryname   -> "\tSO:queryname"-                               Coordinate  -> "\tSO:coordinate"-                               GroupSorted -> "\tSO:groupsort") <>-        show_bam_others os'--    show_bam_meta_seq (BamSQ  _  _ []) = mempty-    show_bam_meta_seq (BamSQ nm ln ts) =-        byteString "@SQ\tSN:" <> byteString nm <>-        byteString "\tLN:" <> intDec ln <> show_bam_others ts--    show_bam_meta_comment cm = byteString "@CO\t" <> byteString cm <> char7 '\n'--    show_bam_meta_other (BamKey k,ts) =-        char7 '@' <> word16LE k <> show_bam_others ts--    show_bam_others ts =-        foldMap show_bam_other ts <> char7 '\n'--    show_bam_other (BamKey k,v) =-        char7 '\t' <> word16LE k <> char7 ':' <> byteString v----- | Reference sequence in Bam--- Bam enumerates the reference sequences and then sorts by index.  We--- need to track that index if we want to reproduce the sorting order.-newtype Refseq = Refseq { unRefseq :: Word32 } deriving (Eq, Ord, Ix)--instance Show Refseq where-    showsPrec p (Refseq r) = showsPrec p r--instance Enum Refseq where-    succ = Refseq . succ . unRefseq-    pred = Refseq . pred . unRefseq-    toEnum = Refseq . fromIntegral-    fromEnum = fromIntegral . unRefseq-    enumFrom = map Refseq . enumFrom . unRefseq-    enumFromThen (Refseq a) (Refseq b) = map Refseq $ enumFromThen a b-    enumFromTo (Refseq a) (Refseq b) = map Refseq $ enumFromTo a b-    enumFromThenTo (Refseq a) (Refseq b) (Refseq c) = map Refseq $ enumFromThenTo a b c----- | Tests whether a reference sequence is valid.--- Returns true unless the the argument equals @invalidRefseq@.-isValidRefseq :: Refseq -> Bool-isValidRefseq = (/=) invalidRefseq---- | The invalid Refseq.--- Bam uses this value to encode a missing reference sequence.-invalidRefseq :: Refseq-invalidRefseq = Refseq 0xffffffff---- | The invalid position.--- Bam uses this value to encode a missing position.-{-# INLINE invalidPos #-}-invalidPos :: Int-invalidPos = -1---- | Tests whether a position is valid.--- Returns true unless the the argument equals @invalidPos@.-{-# INLINE isValidPos #-}-isValidPos :: Int -> Bool-isValidPos = (/=) invalidPos--{-# INLINE unknownMapq #-}-unknownMapq :: Int-unknownMapq = 255--isKnownMapq :: Int -> Bool-isKnownMapq = (/=) unknownMapq---- | A list of reference sequences.-type Refs = Z.Seq BamSQ---- | The empty list of references.  Needed for BAM files that don't really store alignments.-noRefs :: Refs-noRefs = Z.empty--getRef :: Refs -> Refseq -> BamSQ-getRef refs (Refseq i)-    | i < fromIntegral (Z.length refs) = Z.index refs (fromIntegral i)-    | otherwise                        = BamSQ "*" 0 []---flagPaired, flagProperlyPaired, flagUnmapped, flagMateUnmapped,- flagReversed, flagMateReversed, flagFirstMate, flagSecondMate,- flagAuxillary, flagSecondary, flagFailsQC, flagDuplicate,- flagSupplementary :: Int--flagPaired         =   0x1-flagProperlyPaired =   0x2-flagUnmapped       =   0x4-flagMateUnmapped   =   0x8-flagReversed       =  0x10-flagMateReversed   =  0x20-flagFirstMate      =  0x40-flagSecondMate     =  0x80-flagAuxillary      = 0x100-flagSecondary      = 0x100-flagFailsQC        = 0x200-flagDuplicate      = 0x400-flagSupplementary  = 0x800--eflagTrimmed, eflagMerged, eflagAlternative, eflagExactIndex :: Int-eflagTrimmed     = 0x1-eflagMerged      = 0x2-eflagAlternative = 0x4-eflagExactIndex  = 0x8----- | Compares two sequence names the way samtools does.--- samtools sorts by \"strnum_cmp\":------ * if both strings start with a digit, parse the initial---   sequence of digits and compare numerically, if equal,---   continue behind the numbers--- * else compare the first characters (possibly NUL), if equal---   continue behind them--- * else both strings ended and the shorter one counts as---   smaller (and that part is stupid)--compareNames :: Seqid -> Seqid -> Ordering-compareNames n m = case (uncons n, uncons m) of-        ( Nothing, Nothing ) -> EQ-        ( Just  _, Nothing ) -> GT-        ( Nothing, Just  _ ) -> LT-        ( Just (c,n'), Just (d,m') )-            | is_digit c && is_digit d ->-                let Just (u,n'') = S.readInt n-                    Just (v,m'') = S.readInt m-                in case u `compare` v of-                    LT -> LT-                    GT -> GT-                    EQ -> n'' `compareNames` m''-            | otherwise -> case c `compare` d of-                    LT -> LT-                    GT -> GT-                    EQ -> n' `compareNames` m'-  where-    is_digit c = 48 <= c && c < 58---data MdOp = MdNum Int | MdRep Nucleotides | MdDel [Nucleotides] deriving Show--readMd :: Bytes -> Maybe [MdOp]-readMd s | S.null s           = return []-         | isDigit (S.head s) = do (n,t) <- S.readInt s-                                   (MdNum n :) <$> readMd t-         | S.head s == '^'    = let (a,b) = S.break isDigit (S.tail s)-                                in (MdDel (map toNucleotides $ S.unpack a) :) <$> readMd b-         | otherwise          = (MdRep (toNucleotides $ S.head s) :) <$> readMd (S.tail s)---- | Normalizes a series of 'MdOp's and encodes them in the way BAM and--- SAM expect it.-showMd :: [MdOp] -> Bytes-showMd = S.pack . flip s1 []-  where-    s1 (MdNum  i : MdNum  j : ms) = s1 (MdNum (i+j) : ms)-    s1 (MdNum  0            : ms) = s1 ms-    s1 (MdNum  i            : ms) = shows i . s1 ms--    s1 (MdRep  r            : ms) = shows r . s1 ms--    s1 (MdDel d1 : MdDel d2 : ms) = s1 (MdDel (d1++d2) : ms)-    s1 (MdDel []            : ms) = s1 ms-    s1 (MdDel ns : MdRep  r : ms) = (:) '^' . shows ns . (:) '0' . shows r . s1 ms-    s1 (MdDel ns            : ms) = (:) '^' . shows ns . s1 ms-    s1 [                        ] = id----- | Computes the "distinct bin" according to the BAM binning scheme.  If--- an alignment starts at @pos@ and its CIGAR implies a length of @len@--- on the reference, then it goes into bin @distinctBin pos len@.-distinctBin :: Int -> Int -> Int-distinctBin beg len = mkbin 14 $ mkbin 17 $ mkbin 20 $ mkbin 23 $ mkbin 26 0-  where end = beg + len - 1-        mkbin n x = if beg `shiftR` n /= end `shiftR` n then x-                    else ((1 `shiftL` (29-n))-1) `div` 7 + (beg `shiftR` n)
− src/Bio/Bam/Index.hs
@@ -1,334 +0,0 @@-module Bio.Bam.Index (-    BamIndex(..),-    readBamIndex,-    readBaiIndex,-    readTabix,--    Region(..),-    Subsequence(..),-    eneeBamRefseq,-    eneeBamSubseq,-    eneeBamRegions,-    eneeBamUnaligned-) where--import Bio.Bam.Header-import Bio.Bam.Reader-import Bio.Bam.Rec-import Bio.Bam.Regions              ( Region(..), Subsequence(..) )-import Bio.Iteratee-import Bio.Prelude--import qualified Bio.Bam.Regions                as R-import qualified Data.IntMap.Strict             as M-import qualified Data.ByteString                as B-import qualified Data.Vector                    as V-import qualified Data.Vector.Mutable            as W-import qualified Data.Vector.Unboxed            as U-import qualified Data.Vector.Unboxed.Mutable    as N-import qualified Data.Vector.Algorithms.Intro   as N---- | Full index, unifying BAI and CSI style.  In both cases, we have the--- binning scheme, parameters are fixed in BAI, but variable in CSI.--- Checkpoints are created from the linear index in BAI or from the--- `loffset' field in CSI.--data BamIndex a = BamIndex {-    -- | Minshift parameter from CSI-    minshift :: {-# UNPACK #-} !Int,--    -- | Depth parameter from CSI-    depth :: {-# UNPACK #-} !Int,--    -- | Best guess at where the unaligned records start-    unaln_off :: {-# UNPACK #-} !Int64,--    -- | Room for stuff (needed for tabix)-    extensions :: a,--    -- | Records for the binning index, where each bin has a list of-    -- segments belonging to it.-    refseq_bins :: {-# UNPACK #-} !(V.Vector Bins),--    -- | Known checkpoints of the form (pos,off) where off is the-    -- virtual offset of the first record crossing pos.-    refseq_ckpoints :: {-# UNPACK #-} !(V.Vector Ckpoints) }--  deriving Show---- | Mapping from bin number to vector of clusters.-type Bins = IntMap Segments-type Segments = U.Vector (Int64,Int64)----- | Checkpoints.  Each checkpoint is a position with the virtual offset--- where the first alignment crossing the position is found.  In BAI, we--- get this from the 'ioffset' vector, in CSI we get it from the--- 'loffset' field:  "Given a region [beg,end), we only need to visit--- chunks whose end file offset is larger than 'ioffset' of the 16kB--- window containing 'beg'."  (Sounds like a marginal gain, though.)--type Ckpoints = IntMap Int64----- | Decode only those reads that fall into one of several regions.--- Strategy:  We will scan the file mostly linearly, but only those--- regions that are actually needed.  We filter the decoded stuff so--- that it actually overlaps our regions.------ From the binning index, we get a list of segments per requested--- region.  Using the checkpoints, we prune them:  if we have a--- checkpoint to the left of the beginning of the interesting region, we--- can move the start of each segment forward to the checkpoint.  If--- that makes the segment empty, it can be droppped.------ The resulting segment lists are merged, then traversed.  We seek to--- the beginning of the earliest segment and start decoding.  Once the--- virtual file position leaves the segment or the alignment position--- moves past the end of the requested region, we move to the next.--- Moving is a seek if it spans a sufficiently large gap or points--- backwards, else we just keep going.---- | A 'Segment' has a start and an end offset, and an "end coordinate"--- from the originating region.-data Segment = Segment {-# UNPACK #-} !Int64 {-# UNPACK #-} !Int64 {-# UNPACK #-} !Int deriving Show--segmentLists :: BamIndex a -> Refseq -> R.Subsequence -> [[Segment]]-segmentLists bi@BamIndex{..} (Refseq ref) (R.Subsequence imap)-        | Just bins <- refseq_bins V.!? fromIntegral ref,-          Just cpts <- refseq_ckpoints V.!? fromIntegral ref-        = [ rgnToSegments bi beg end bins cpts | (beg,end) <- M.toList imap ]-segmentLists _ _ _ = []---- from region to list of bins, then to list of segments-rgnToSegments :: BamIndex a -> Int -> Int -> Bins -> Ckpoints -> [Segment]-rgnToSegments bi@BamIndex{..} beg end bins cpts =-    [ Segment boff' eoff end-    | bin <- binList bi beg end-    , (boff,eoff) <- maybe [] U.toList $ M.lookup bin bins-    , let boff' = max boff cpt-    , boff' < eoff ]-  where-    !cpt = maybe 0 snd $ M.lookupLE beg cpts---- list of bins for given range of coordinates, from Heng's horrible code-binList :: BamIndex a -> Int -> Int -> [Int]-binList BamIndex{..} beg end = binlist' 0 (minshift + 3*depth) 0-  where-    binlist' l s t = if l > depth then [] else [b..e] ++ go-      where-        b = t + beg `shiftR` s-        e = t + (end-1) `shiftR` s-        go = binlist' (l+1) (s-3) (t + 1 `shiftL` (3*l))----- | Merges two lists of segments.  Lists must be sorted, the merge sort--- merges overlapping segments into one.-infix 4 ~~-(~~) :: [Segment] -> [Segment] -> [Segment]-Segment a b e : xs ~~ Segment u v f : ys-    |          b < u = Segment a b e : (xs ~~ Segment u v f : ys)     -- no overlap-    | a < u && b < v = Segment a v (max e f) : (xs ~~ ys)             -- some overlap-    |          b < v = Segment u v (max e f) : (xs ~~ ys)             -- contained-    | v < a          = Segment u v f : (xs ~~ Segment a b e : ys)     -- no overlap-    | u < a          = Segment u b (max e f) : (xs ~~ ys)             -- some overlap-    | otherwise      = Segment a b (max e f) : (xs ~~ ys)             -- contained-[] ~~ ys = ys-xs ~~ [] = xs----- | Reads any index we can find for a file.  If the file name has a--- .bai or .csi extension, we read it.  Else we look for the index by--- adding such an extension and by replacing the extension with these--- two, and finally in the file itself.  The first file that exists and--- can actually be parsed, is used.-readBamIndex :: FilePath -> IO (BamIndex ())-readBamIndex fp | ".bai" `isSuffixOf` fp = enumFile defaultBufSize fp readBaiIndex >>= run-                | ".csi" `isSuffixOf` fp = enumFile defaultBufSize fp readBaiIndex >>= run-                | otherwise = tryIx               (fp ++ ".bai") $-                              tryIx (dropExtension fp ++  "bai") $-                              tryIx               (fp ++ ".csi") $-                              tryIx (dropExtension fp ++  "csi") $-                              enumFile defaultBufSize fp readBaiIndex >>= run-  where-    dropExtension p = reverse $ (if null b then "." ++ f else b) ++ d-      where-        (f,d) = break (=='/') $ reverse p-        b     = dropWhile (/='.') f--    tryIx f k = do e <- fileExist f-                   if e then do r <- enumFile defaultBufSize f readBaiIndex >>= tryRun-                                case r of Right                     ix -> return ix-                                          Left (IterStringException _) -> k-                        else k---- | Read an index in BAI or CSI format, recognized automatically.--- Note that TBI is supposed to be compressed using bgzip; it must be--- decompressed before being passed to 'readBaiIndex'.--readBaiIndex :: MonadIO m => Iteratee Bytes m (BamIndex ())-readBaiIndex = iGetString 4 >>= switch-  where-    switch "BAI\1" = do nref <- fromIntegral `liftM` endianRead4 LSB-                        getIndexArrays nref 14 5 (const return) getIntervals--    switch "CSI\1" = do minshift <- fromIntegral `liftM` endianRead4 LSB-                        depth <- fromIntegral `liftM` endianRead4 LSB-                        endianRead4 LSB >>= dropStreamBS . fromIntegral -- aux data-                        nref <- fromIntegral `liftM` endianRead4 LSB-                        getIndexArrays nref minshift depth (addOneCheckpoint minshift depth) return--    switch magic   = throwErr . iterStrExc $ "index signature " ++ show magic ++ " not recognized"---    -- Insert one checkpoint.  If we already have an entry (can happen-    -- if it comes from a different bin), we conservatively take the min-    addOneCheckpoint minshift depth bin cp = do-            loffset <- fromIntegral `liftM` endianRead8 LSB-            let key = llim (fromIntegral bin) (3*depth) minshift-            return $! M.insertWith min key loffset cp--    -- compute left limit of bin-    llim bin dp sf | dp  ==  0 = 0-                   | bin >= ix = (bin - ix) `shiftL` sf-                   | otherwise = llim bin (dp-3) (sf+3)-            where ix = (1 `shiftL` dp - 1) `div` 7--type TabIndex = BamIndex TabMeta--data TabMeta = TabMeta { format :: TabFormat-                       , col_seq :: Int                           -- Column for the sequence name-                       , col_beg :: Int                           -- Column for the start of a region-                       , col_end :: Int                           -- Column for the end of a region-                       , comment_char :: Char-                       , skip_lines :: Int-                       , names :: V.Vector Bytes }-  deriving Show--data TabFormat = Generic | SamFormat | VcfFormat | ZeroBased   deriving Show---- | Reads a Tabix index.  Note that tabix indices are compressed, this--- is taken care of.-readTabix :: MonadIO m => Iteratee Bytes m TabIndex-readTabix = joinI $ decompressBgzf $ iGetString 4 >>= switch-  where-    switch "TBI\1" = do nref <- fromIntegral `liftM` endianRead4 LSB-                        format       <- liftM toFormat     (endianRead4 LSB)-                        col_seq      <- liftM fromIntegral (endianRead4 LSB)-                        col_beg      <- liftM fromIntegral (endianRead4 LSB)-                        col_end      <- liftM fromIntegral (endianRead4 LSB)-                        comment_char <- liftM (chr . fromIntegral) (endianRead4 LSB)-                        skip_lines   <- liftM fromIntegral (endianRead4 LSB)-                        names        <- liftM (V.fromList . B.split 0) . iGetString . fromIntegral =<< endianRead4 LSB--                        ix <- getIndexArrays nref 14 5 (const return) getIntervals-                        fin <- isFinished-                        if fin then return $! ix { extensions = TabMeta{..} }-                               else do unaln <- fromIntegral `liftM` endianRead8 LSB-                                       return $! ix { unaln_off = unaln, extensions = TabMeta{..} }--    switch magic   = throwErr . iterStrExc $ "index signature " ++ show magic ++ " not recognized"--    toFormat 1 = SamFormat-    toFormat 2 = VcfFormat-    toFormat x = if testBit x 16 then ZeroBased else Generic---- Read the intervals.  Each one becomes a checkpoint.-getIntervals :: Monad m => (IntMap Int64, Int64) -> Iteratee Bytes m (IntMap Int64, Int64)-getIntervals (cp,mx0) = do-    nintv <- fromIntegral `liftM` endianRead4 LSB-    reduceM 0 nintv (cp,mx0) $ \(!im,!mx) int -> do-        oo <- fromIntegral `liftM` endianRead8 LSB-        return (if oo == 0 then im else M.insert (int * 0x4000) oo im, max mx oo)---getIndexArrays :: MonadIO m => Int -> Int -> Int-               -> (Word32 -> Ckpoints -> Iteratee Bytes m Ckpoints)-               -> ((Ckpoints, Int64) -> Iteratee Bytes m (Ckpoints, Int64))-               -> Iteratee Bytes m (BamIndex ())-getIndexArrays nref minshift depth addOneCheckpoint addManyCheckpoints-    | nref  < 1 = return $ BamIndex minshift depth 0 () V.empty V.empty-    | otherwise = do-        rbins  <- liftIO $ W.new nref-        rckpts <- liftIO $ W.new nref-        mxR <- reduceM 0 nref 0 $ \mx0 r -> do-                nbins <- endianRead4 LSB-                (!bins,!cpts,!mx1) <- reduceM 0 nbins (M.empty,M.empty,mx0) $ \(!im,!cp,!mx) _ -> do-                        bin <- endianRead4 LSB -- the "distinct bin"-                        cp' <- addOneCheckpoint bin cp-                        segsarr <- getSegmentArray-                        let !mx' = if U.null segsarr then mx else max mx (snd (U.last segsarr))-                        return (M.insert (fromIntegral bin) segsarr im, cp', mx')-                (!cpts',!mx2) <- addManyCheckpoints (cpts,mx1)-                liftIO $ W.write rbins r bins >> W.write rckpts r cpts'-                return mx2-        liftM2 (BamIndex minshift depth mxR ()) (liftIO $ V.unsafeFreeze rbins) (liftIO $ V.unsafeFreeze rckpts)---- | Reads the list of segments from an index file and makes sure--- it is sorted.-getSegmentArray :: MonadIO m => Iteratee Bytes m Segments-getSegmentArray = do-    nsegs <- fromIntegral `liftM` endianRead4 LSB-    segsarr <- liftIO $ N.new nsegs-    loopM 0 nsegs $ \i -> do beg <- fromIntegral `liftM` endianRead8 LSB-                             end <- fromIntegral `liftM` endianRead8 LSB-                             liftIO $ N.write segsarr i (beg,end)-    liftIO $ N.sort segsarr >> U.unsafeFreeze segsarr--{-# INLINE reduceM #-}-reduceM :: (Monad m, Enum ix, Eq ix) => ix -> ix -> a -> (a -> ix -> m a) -> m a-reduceM beg end acc cons = if beg /= end then cons acc beg >>= \n -> reduceM (succ beg) end n cons else return acc--{-# INLINE loopM #-}-loopM :: (Monad m, Enum ix, Eq ix) => ix -> ix -> (ix -> m ()) -> m ()-loopM beg end k = if beg /= end then k beg >> loopM (succ beg) end k else return ()----- | Seeks to a given sequence in a Bam file and enumerates only those--- records aligning to that reference.  We use the first checkpoint--- available for the sequence.  This requires an appropriate index, and--- the file must have been opened in such a way as to allow seeking.--- Enumerates over the @BamRaw@ records of the correct sequence only,--- doesn't enumerate at all if the sequence isn't found.--eneeBamRefseq :: Monad m => BamIndex b -> Refseq -> Enumeratee [BamRaw] [BamRaw] m a-eneeBamRefseq BamIndex{..} (Refseq r) iter-    | Just ckpts <- refseq_ckpoints V.!? fromIntegral r-    , Just (voff, _) <- M.minView ckpts-    , voff /= 0 = do seek $ fromIntegral voff-                     breakE ((Refseq r /=) . b_rname . unpackBam) iter-    | otherwise = return iter---- | Seeks to the part of a Bam file that contains unaligned reads and--- enumerates those.  Sort of the dual to 'eneeBamRefseq'.  We use the--- best guess at where the unaligned stuff starts.  If no such guess is--- available, we decode everything.--eneeBamUnaligned :: Monad m => BamIndex b -> Enumeratee [BamRaw] [BamRaw] m a-eneeBamUnaligned BamIndex{..} iter = do when (unaln_off /= 0) $ seek $ fromIntegral unaln_off-                                        filterStream (not . isValidRefseq . b_rname . unpackBam) iter---- | Enumerates one 'Segment'.  Seeks to the start offset, unless--- reading over the skipped part looks cheaper.  Enumerates until we--- either cross the end offset or the max position.-eneeBamSegment :: Monad m => Segment -> Enumeratee [BamRaw] [BamRaw] m r-eneeBamSegment (Segment beg end mpos) out = do-    -- seek if it's a backwards seek or more than 512k forwards-    peekStream >>= \x -> case x of-        Just br | beg <= o && beg + 0x8000 > o -> return ()-            where o = fromIntegral $ virt_offset br-        _                                      -> seek $ fromIntegral beg--    let in_segment br = virt_offset br <= fromIntegral end && b_pos (unpackBam br) <= mpos-    takeWhileE in_segment out--eneeBamSubseq :: Monad m => BamIndex b -> Refseq -> R.Subsequence -> Enumeratee [BamRaw] [BamRaw] m a-eneeBamSubseq bi ref subs = foldr ((>=>) . eneeBamSegment) return segs ><> filterStream olap-  where-    segs = foldr (~~) [] $ segmentLists bi ref subs-    olap br = b_rname == ref && R.overlaps b_pos (b_pos + alignedLength b_cigar) subs-                    where BamRec{..} = unpackBam br--eneeBamRegions :: Monad m => BamIndex b -> [R.Region] -> Enumeratee [BamRaw] [BamRaw] m a-eneeBamRegions bi = foldr ((>=>) . uncurry (eneeBamSubseq bi)) return . R.toList . R.fromList-
− src/Bio/Bam/Pileup.hs
@@ -1,469 +0,0 @@-{-# LANGUAGE Rank2Types, DeriveGeneric #-}---- | Pileup, similar to Samtools------ Pileup turns a sorted sequence of reads into a sequence of \"piles\",--- one for each site where a genetic variant might be called.  We will--- scan each read's CIGAR line and MD field in concert with the sequence--- and effective quality.  Effective quality is the lowest available--- quality score of QUAL, MAPQ, and BQ.  For aDNA calling, a base is--- represented as four probabilities, derived from a position dependent--- damage model.--module Bio.Bam.Pileup where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Iteratee-import Bio.Prelude--import qualified Data.ByteString        as B-import qualified Data.Vector.Generic    as V-import qualified Data.Vector.Unboxed    as U---- | The primitive pieces for genotype calling:  A position, a base--- represented as four likelihoods, an inserted sequence, and the--- length of a deleted sequence.  The logic is that we look at a base--- followed by some indel, and all those indels are combined into a--- single insertion and a single deletion.-data PrimChunks = Seek {-# UNPACK #-} !Int PrimBase                   -- ^ skip to position (at start or after N operation)-                | Indel [Nucleotides] [DamagedBase] PrimBase          -- ^ observed deletion and insertion between two bases-                | EndOfRead                                           -- ^ nothing anymore-  deriving Show--data PrimBase = Base { _pb_wait   :: {-# UNPACK #-} !Int              -- ^ number of bases to wait due to a deletion-                     , _pb_base   :: {-# UNPACK #-} !DamagedBase-                     , _pb_mapq   :: {-# UNPACK #-} !Qual             -- ^ map quality-                     , _pb_chunks ::                 PrimChunks }     -- ^ more chunks-  deriving Show--type PosPrimChunks = (Refseq, Int, Bool, PrimChunks)---- | Represents our knowledge about a certain base, which consists of--- the base itself (A,C,G,T, encoded as 0..3; no Ns), the quality score--- (anything that isn't A,C,G,T becomes A with quality 0), and a--- substitution matrix representing post-mortem but pre-sequencing--- substitutions.------ Unfortunately, none of this can be rolled into something more simple,--- because damage and sequencing error behave so differently.------ Damage information is polymorphic.  We might run with a simple--- version (a matrix) for calling, but we need more (a matrix and a--- mutable matrix, I think) for estimation.--data DamagedBase = DB { db_call    :: {-# UNPACK #-} !Nucleotide           -- ^ called base-                      , db_qual    :: {-# UNPACK #-} !Qual                 -- ^ quality of called base-                      , db_dmg_tk  :: {-# UNPACK #-} !DmgToken             -- ^ damage information-                      , db_dmg_pos :: {-# UNPACK #-} !Int                  -- ^ damage information-                      , db_ref     :: {-# UNPACK #-} !Nucleotides }        -- ^ reference base from MD field--newtype DmgToken = DmgToken { fromDmgToken :: Int }--instance Show DamagedBase where-    showsPrec _ (DB n q _ _ r)-        | nucToNucs n == r = shows n .                     (:) '@' . shows q-        | otherwise        = shows n . (:) '/' . shows r . (:) '@' . shows q----- | Decomposes a BAM record into chunks suitable for piling up.  We--- pick apart the CIGAR and MD fields, and combine them with sequence--- and quality as appropriate.  Clipped bases are removed/skipped as--- needed.  We also apply a substitution matrix to each base, which must--- be supplied along with the read.-{-# INLINE decompose #-}-decompose :: DmgToken -> BamRaw -> [PosPrimChunks]-decompose dtok br =-    if isUnmapped b || isDuplicate b || not (isValidRefseq b_rname)-    then [] else [(b_rname, b_pos, isReversed b, pchunks)]-  where-    b@BamRec{..} = unpackBam br-    pchunks = firstBase b_pos 0 0 (fromMaybe [] $ getMd b)--    !max_cig = V.length b_cigar-    !max_seq = V.length b_seq-    !baq     = extAsString "BQ" b--    -- This will compute the effective quality.  As far as I can see-    -- from the BAM spec V1.4, the qualities that matter are QUAL, MAPQ,-    -- and BAQ.  If QUAL is invalid, we replace it (arbitrarily) with-    -- 23 (assuming a rather conservative error rate of ~0.5%), BAQ is-    -- added to QUAL, and MAPQ is an upper limit for effective quality.--    get_seq :: Int -> (Nucleotides -> Nucleotides) -> DamagedBase-    get_seq i f = case b_seq `V.unsafeIndex` i of                                -- nucleotide-            n | n == nucsA -> DB nucA qe dtok dmg (f n)-              | n == nucsC -> DB nucC qe dtok dmg (f n)-              | n == nucsG -> DB nucG qe dtok dmg (f n)-              | n == nucsT -> DB nucT qe dtok dmg (f n)-              | otherwise  -> DB nucA (Q 0) dtok dmg (f n)-      where-        !q   = case b_qual `V.unsafeIndex` i of Q 0xff -> Q 30 ; x -> x         -- quality; invalid (0xff) becomes 30-        !q'  | i >= B.length baq = q                                            -- no BAQ available-             | otherwise = Q (unQ q + (B.index baq i - 64))                     -- else correct for BAQ-        !qe  = min q' b_mapq                                                    -- use MAPQ as upper limit-        !dmg = if i+i > max_seq then i-max_seq else i--    -- Look for first base following the read's start or a gap (CIGAR-    -- code N).  Indels are skipped, since these are either bugs in the-    -- aligner or the aligner getting rid of essentially unalignable-    -- bases.-    firstBase :: Int -> Int -> Int -> [MdOp] -> PrimChunks-    firstBase !pos !is !ic mds-        | is >= max_seq || ic >= max_cig = EndOfRead-        | otherwise = case b_cigar `V.unsafeIndex` ic of-            Ins :* cl ->            firstBase  pos (cl+is) (ic+1) mds-            SMa :* cl ->            firstBase  pos (cl+is) (ic+1) mds-            Del :* cl ->            firstBase (pos+cl) is  (ic+1) (drop_del cl mds)-            Nop :* cl ->            firstBase (pos+cl) is  (ic+1) mds-            HMa :*  _ ->            firstBase  pos     is  (ic+1) mds-            Pad :*  _ ->            firstBase  pos     is  (ic+1) mds-            Mat :*  0 ->            firstBase  pos     is  (ic+1) mds-            Mat :*  _ -> Seek pos $ nextBase 0 pos     is   ic 0  mds-      where-        -- We have to treat (MdNum 0), because samtools actually-        -- generates(!) it all over the place and if not handled as a-        -- special case, it looks like an inconsistent MD field.-        drop_del n (MdDel ns : mds')-            | n < length ns = MdDel (drop n ns) : mds'-            | n > length ns = drop_del (n - length ns) mds'-            | otherwise     = mds'-        drop_del n (MdNum 0 : mds') = drop_del n mds'-        drop_del _ mds'     = mds'--    -- Generate likelihoods for the next base.  When this gets called,-    -- we are looking at an M CIGAR operation and all the subindices are-    -- valid.-    -- I don't think we can ever get (MdDel []), but then again, who-    -- knows what crazy shit samtools decides to generate.  There is-    -- little harm in special-casing it.-    nextBase :: Int -> Int -> Int -> Int -> Int -> [MdOp] -> PrimBase-    nextBase !wt !pos !is !ic !io mds = case mds of-        MdNum   0 : mds' -> nextBase wt pos is ic io mds'-        MdDel  [] : mds' -> nextBase wt pos is ic io mds'-        MdNum   1 : mds' -> nextBase' (get_seq is id   ) mds'-        MdNum   n : mds' -> nextBase' (get_seq is id   ) (MdNum (n-1) : mds')-        MdRep ref : mds' -> nextBase' (get_seq is $ const ref  ) mds'-        MdDel   _ : _    -> nextBase' (get_seq is $ const nucsN) mds-        [              ] -> nextBase' (get_seq is $ const nucsN) [ ]-      where-        nextBase' ref mds' = Base wt ref b_mapq $ nextIndel  [] [] (pos+1) (is+1) ic (io+1) mds'--    -- Look for the next indel after a base.  We collect all indels (I-    -- and D codes) into one combined operation.  If we hit N or the-    -- read's end, we drop all of it (indels next to a gap indicate-    -- trouble).  Other stuff is skipped: we could check for stuff that-    -- isn't valid in the middle of a read (H and S), but then what-    -- would we do about it anyway?  Just ignoring it is much easier and-    -- arguably at least as correct.-    nextIndel :: [[DamagedBase]] -> [Nucleotides] -> Int -> Int -> Int -> Int -> [MdOp] -> PrimChunks-    nextIndel ins del !pos !is !ic !io mds-        | is >= max_seq || ic >= max_cig = EndOfRead-        | otherwise = case b_cigar `V.unsafeIndex` ic of-            Ins :* cl ->             nextIndel (isq cl) del   pos (cl+is) (ic+1) 0 mds-            SMa :* cl ->             nextIndel  ins     del   pos (cl+is) (ic+1) 0 mds-            Del :* cl ->             nextIndel ins (del++dsq) (pos+cl) is (ic+1) 0 mds'-                where (dsq,mds') = split_del cl mds-            Pad :*  _ ->             nextIndel  ins     del   pos     is  (ic+1) 0 mds-            HMa :*  _ ->             nextIndel  ins     del   pos     is  (ic+1) 0 mds-            Nop :* cl ->             firstBase               (pos+cl) is  (ic+1)   mds      -- ends up generating a 'Seek'-            Mat :* cl | io == cl  -> nextIndel  ins     del   pos     is  (ic+1) 0 mds-                      | otherwise -> indel del out $ nextBase (length del) pos is ic io mds -- ends up generating a 'Base'-      where-        indel d o k = foldr seq (Indel d o k) o-        out    = concat $ reverse ins-        isq cl = [ get_seq i $ const gap | i <- [is..is+cl-1] ] : ins--        -- We have to treat (MdNum 0), because samtools actually-        -- generates(!) it all over the place and if not handled as a-        -- special case, it looks like an incinsistend MD field.-        split_del n (MdDel ns : mds')-            | n < length ns = (take n ns, MdDel (drop n ns) : mds')-            | n > length ns = let (ns', mds'') = split_del (n - length ns) mds' in (ns++ns', mds'')-            | otherwise     = (ns, mds')-        split_del n (MdNum 0 : mds') = split_del n mds'-        split_del n mds'    = (replicate n nucsN, mds')---- | Statistics about a genotype call.  Probably only useful for--- fitlering (so not very useful), but we keep them because it's easy to--- track them.--data CallStats = CallStats-    { read_depth       :: {-# UNPACK #-} !Int       -- number of contributing reads-    , reads_mapq0      :: {-# UNPACK #-} !Int       -- number of (non-)contributing reads with MAPQ==0-    , sum_mapq         :: {-# UNPACK #-} !Int       -- sum of map qualities of contributing reads-    , sum_mapq_squared :: {-# UNPACK #-} !Int }     -- sum of squared map qualities of contributing reads-  deriving (Show, Eq, Generic)--instance Monoid CallStats where-    mempty      = CallStats { read_depth       = 0-                            , reads_mapq0      = 0-                            , sum_mapq         = 0-                            , sum_mapq_squared = 0 }-    mappend     = (<>)--instance Semigroup CallStats where-    x <> y = CallStats { read_depth       = read_depth x + read_depth y-                       , reads_mapq0      = reads_mapq0 x + reads_mapq0 y-                       , sum_mapq         = sum_mapq x + sum_mapq y-                       , sum_mapq_squared = sum_mapq_squared x + sum_mapq_squared y }--newtype V_Nuc  = V_Nuc  (U.Vector Nucleotide)  deriving (Eq, Ord, Show)-newtype V_Nucs = V_Nucs (U.Vector Nucleotides) deriving (Eq, Ord, Show)--data IndelVariant = IndelVariant { deleted_bases  :: !V_Nucs, inserted_bases :: !V_Nuc }-      deriving (Eq, Ord, Show, Generic)----- | Map quality and a list of encountered bases, with damage--- information and reference base if known.-type BasePile  =                          [DamagedBase]---- | Map quality and a list of encountered indel variants.  The deletion--- has the reference sequence, if known, an insertion has the inserted--- sequence with damage information.-type IndelPile = [( Qual, ([Nucleotides], [DamagedBase]) )]   -- a list of indel variants---- | Running pileup results in a series of piles.  A 'Pile' has the--- basic statistics of a 'VarCall', but no likelihood values and a--- pristine list of variants instead of a proper call.  We emit one pile--- with two 'BasePile's (one for each strand) and one 'IndelPile' (the--- one immediately following) at a time.--data Pile' a b = Pile { p_refseq     :: {-# UNPACK #-} !Refseq-                      , p_pos        :: {-# UNPACK #-} !Int-                      , p_snp_stat   :: {-# UNPACK #-} !CallStats-                      , p_snp_pile   :: a-                      , p_indel_stat :: {-# UNPACK #-} !CallStats-                      , p_indel_pile :: b }-  deriving Show---- | Raw pile.  Bases and indels are piled separately on forward and--- backward strands.-type Pile = Pile' (BasePile, BasePile) (IndelPile, IndelPile)---- | The pileup enumeratee takes 'BamRaw's, decomposes them, interleaves--- the pieces appropriately, and generates 'Pile's.  The output will--- contain at most one 'BasePile' and one 'IndelPile' for each position,--- piles are sorted by position.------ This top level driver receives 'BamRaw's.  Unaligned reads and--- duplicates are skipped (but not those merely failing quality checks).--- Processing stops when the first read with invalid 'br_rname' is--- encountered or a t end of file.--{-# INLINE pileup #-}-pileup :: Enumeratee [PosPrimChunks] [Pile] IO b-pileup = eneeCheckIfDonePass (icont . runPileM pileup' finish (Refseq 0) 0 ([],[]) (Empty,Empty))-  where-    finish () _r _p ([],[]) (Empty,Empty) out inp = idone (liftI out) inp-    finish () _ _ _ _ _ _ = error "logic error: leftovers after pileup"----- | The pileup logic keeps a current coordinate (just two integers) and--- two running queues: one of /active/ 'PrimBase's that contribute to--- current genotype calling and on of /waiting/ 'PrimBase's that will--- contribute at a later point.------ Oppan continuation passing style!  Not only is the CPS version of the--- state monad (we have five distinct pieces of state) somewhat faster,--- we also need CPS to interact with the mechanisms of 'Iteratee'.  It--- makes implementing 'yield', 'peek', and 'bump' straight forward.--newtype PileM m a = PileM { runPileM :: forall r . (a -> PileF m r) -> PileF m r }---- | The things we drag along in 'PileM'.  Notes:--- * The /active/ queue is a simple stack.  We add at the front when we---   encounter reads, which reverses them.  When traversing it, we traverse---   reads backwards, but since we accumulate the 'BasePile', it gets reversed---   back.  The new /active/ queue, however, is no longer reversed (as it should---   be).  So after the traversal, we reverse it again.  (Yes, it is harder to---   understand than using a proper deque type, but it is cheaper.---   There may not be much point in the reversing, though.)--type PileF m r = Refseq -> Int ->                             -- current position-                 ( [PrimBase], [PrimBase] ) ->                -- active queues-                 ( Heap, Heap ) ->                            -- waiting queues-                 (Stream [Pile] -> Iteratee [Pile] m r) ->    -- output function-                 Stream [PosPrimChunks] ->                    -- pending input-                 Iteratee [PosPrimChunks] m (Iteratee [Pile] m r)--instance Functor (PileM m) where-    {-# INLINE fmap #-}-    fmap f (PileM m) = PileM $ \k -> m (k . f)--instance Applicative (PileM m) where-    {-# INLINE pure #-}-    pure a = PileM $ \k -> k a-    {-# INLINE (<*>) #-}-    u <*> v = PileM $ \k -> runPileM u (\a -> runPileM v (k . a))--instance Monad (PileM m) where-    {-# INLINE return #-}-    return a = PileM $ \k -> k a-    {-# INLINE (>>=) #-}-    m >>=  k = PileM $ \k' -> runPileM m (\a -> runPileM (k a) k')--{-# INLINE get_refseq #-}-get_refseq :: PileM m Refseq-get_refseq = PileM $ \k r -> k r r--{-# INLINE get_pos #-}-get_pos :: PileM m Int-get_pos = PileM $ \k r p -> k p r p--{-# INLINE upd_pos #-}-upd_pos :: (Int -> Int) -> PileM m ()-upd_pos f = PileM $ \k r p -> k () r $! f p---- | Sends one piece of output downstream.  You are not expected to--- understand how this works, but inlining 'eneeCheckIfDone' plugged an--- annoying memory leak.-{-# INLINE yieldPile #-}-yieldPile :: CallStats -> BasePile -> BasePile -> CallStats -> IndelPile -> IndelPile -> PileM m ()-yieldPile x1 x2a x2b x3 x4a x4b = PileM $ \ !kont !r !p !a !w !out !inp -> Iteratee $ \od oc ->-      let recurse           = kont () r p a w-          onDone y s        = od (idone y s) inp-          onCont k Nothing  = runIter (recurse k inp) od oc-          onCont k (Just e) = runIter (throwRecoverableErr e (recurse k . (<>) inp)) od oc-          pile              = Pile r p x1 (x2a,x2b) x3 (x4a,x4b)-      in runIter (out (Chunk [pile])) onDone onCont---- | The actual pileup algorithm.  If /active/ contains something,--- continue here.  Else find the coordinate to continue from, which is--- the minimum of the next /waiting/ coordinate and the next coordinate--- in input; if found, continue there, else we're all done.-pileup' :: PileM m ()-pileup' = PileM $ \ !k !refseq !pos !active !waiting !out !inp ->--    let recurse     = runPileM pileup'  k refseq pos active waiting out-        cont2 rs po = runPileM pileup'' k     rs po  active waiting out inp-        leave       =                k () refseq pos active waiting out inp--    in case (active, getMinKeysH waiting, inp) of-        ( (_:_,_),       _,                  _  ) -> cont2 refseq pos-        ( (_,_:_),       _,                  _  ) -> cont2 refseq pos-        (    _,    Just nw, EOF              _  ) -> cont2 refseq nw-        (    _,    Nothing, EOF              _  ) -> leave-        (    _,          _, Chunk [           ] ) -> liftI recurse-        (    _,    Nothing, Chunk ((r,p,_,_):_) ) -> cont2 r p-        (    _,    Just nw, Chunk ((r,p,_,_):_) )-                           | (refseq,nw) <= (r,p) -> cont2 refseq nw-                           | otherwise            -> cont2 r p-  where-    getMinKeysH :: (Heap, Heap) -> Maybe Int-    getMinKeysH (a,b) = case (getMinKeyH a, getMinKeyH b) of-        ( Nothing, Nothing ) -> Nothing-        ( Just  x, Nothing ) -> Just  x-        ( Nothing, Just  y ) -> Just  y-        ( Just  x, Just  y ) -> Just (min x y)---pileup'' :: PileM m ()-pileup'' = do-    -- Input is still 'BamRaw', since these can be relied on to be-    -- sorted.  First see if there is any input at the current location,-    -- if so, decompose it and add it to the appropriate queue.-    p'feed_input-    p'check_waiting-    ((fin_bsL, fin_bpL), (fin_bsR, fin_bpR), (fin_isL, fin_ipL), (fin_isR, fin_ipR)) <- p'scan_active--    -- Output, but don't bother emitting empty piles.  Note that a plain-    -- basecall still yields an entry in the 'IndelPile'.  This is necessary,-    -- because actual indel calling will want to know how many reads /did not/-    -- show the variant.  However, if no reads show any variant, and here is the-    -- first place where we notice that, the pile is useless.-    let uninteresting (_,(d,i)) = null d && null i-    unless (null fin_bpL && null fin_bpR && all uninteresting fin_ipL && all uninteresting fin_ipR) $-        yieldPile (fin_bsL <> fin_bsR) fin_bpL fin_bpR-                  (fin_isL <> fin_isR) fin_ipL fin_ipR--    -- Bump coordinate and loop.  (Note that the bump to the next-    -- reference /sequence/ is done implicitly, because we will run out of-    -- reads and restart in 'pileup''.)-    upd_pos succ-    pileup'---- | Feeds input as long as it starts at the current position-p'feed_input :: PileM m ()-p'feed_input = PileM $ \kont rs po ac@(af,ar) wt@(wf,wr) out inp -> case inp of-        Chunk [   ] -> liftI $ runPileM p'feed_input kont rs po ac wt out-        Chunk ((rs', po', str, prim):bs)-            | rs == rs' && po == po' ->-                case prim of-                    Seek   !p !pb -> let wf' = Node p pb Empty Empty `unionH` wf-                                         wr' = Node p pb Empty Empty `unionH` wr-                                     in runPileM p'feed_input kont rs po ac (if str then (wf,wr') else (wf',wr))     out (Chunk bs)--                    Indel _ _ !pb ->    runPileM p'feed_input kont rs po (if str then (af,pb:ar) else (pb:af,ar)) wt out (Chunk bs)--                    EndOfRead     ->    runPileM p'feed_input kont rs po ac wt                                       out (Chunk bs)--        _           -> kont () rs po ac wt out inp---- | Checks /waiting/ queue.  If there is anything waiting for the--- current position, moves it to /active/ queue.-p'check_waiting :: PileM m ()-p'check_waiting = PileM $ \kont rs po (af0,ar0) (wf0,wr0) ->-        let go1 af wf = case viewMinH wf of-                Just (!mk, !pb, !wf') | mk == po -> go1 (pb:af) wf'-                _                                -> go2 af wf ar0 wr0--            go2 af wf ar wr = case viewMinH wr of-                Just (!mk, !pb, !wr') | mk == po -> go2 af wf (pb:ar) wr'-                _                                -> kont () rs po (af,ar) (wf,wr)--        in go1 af0 wf0----- | Separately scans the two /active/ queues and makes one 'BasePile'--- from each.  Also sees what's next in the 'PrimChunks':  'Indel's--- contribute to two separate 'IndelPile's, 'Seek's are pushed back to--- the /waiting/ queue, 'EndOfRead's are removed, and everything else is--- added to two fresh /active/ queues.-p'scan_active :: PileM m (( CallStats, BasePile ),  ( CallStats, BasePile ),-                          ( CallStats, IndelPile ), ( CallStats, IndelPile ))-p'scan_active = do-    (bpf,ipf) <- PileM $ \kont rs pos (af,ar) (wf,wr) -> go (\r af' wf' -> kont r rs pos (af',ar) (wf',wr)) [] wf mempty mempty af-    (bpr,ipr) <- PileM $ \kont rs pos (af,ar) (wf,wr) -> go (\r ar' wr' -> kont r rs pos (af,ar') (wf,wr')) [] wr mempty mempty ar-    return (bpf,bpr,ipf,ipr)-  where-    go k !ac !wt !bpile !ipile [                           ] = k (bpile, ipile) (reverse ac) wt-    go k !ac !wt !bpile !ipile (Base nwt qs mq pchunks : bs) =-        case pchunks of-            _ | nwt > 0     -> b' `seq` go k  (b':ac)   wt     bpile     ipile  bs-            Seek p' pb'     -> go k      ac (ins p' pb' wt) (z bpile)    ipile  bs-            Indel nd ni pb' -> go k (pb':ac)            wt  (z bpile) (y ipile) bs where y = put (,) mq (nd,ni)-            EndOfRead       -> go k      ac             wt  (z bpile)    ipile  bs-        where-            b' = Base (nwt-1) qs mq pchunks-            z  = put (\q x -> x { db_qual = min q (db_qual x) }) mq qs--    ins q v w = Node q v Empty Empty `unionH` w--    put f (Q !q) !x (!st,!vs) = ( st { read_depth       = read_depth st + 1-                                     , reads_mapq0      = reads_mapq0 st + (if q == 0 then 1 else 0)-                                     , sum_mapq         = sum_mapq st + fromIntegral q-                                     , sum_mapq_squared = sum_mapq_squared st + fromIntegral q * fromIntegral q }-                                , f (Q q) x : vs )----- | We need a simple priority queue.  Here's a skew heap (specialized--- to strict 'Int' priorities and 'PrimBase' values).-data Heap = Empty | Node {-# UNPACK #-} !Int PrimBase Heap Heap--unionH :: Heap -> Heap -> Heap-Empty                 `unionH` t2                    = t2-t1                    `unionH` Empty                 = t1-t1@(Node k1 x1 l1 r1) `unionH` t2@(Node k2 x2 l2 r2)-   | k1 <= k2                                        = Node k1 x1 (t2 `unionH` r1) l1-   | otherwise                                       = Node k2 x2 (t1 `unionH` r2) l2--getMinKeyH :: Heap -> Maybe Int-getMinKeyH Empty          = Nothing-getMinKeyH (Node x _ _ _) = Just x--viewMinH :: Heap -> Maybe (Int, PrimBase, Heap)-viewMinH Empty          = Nothing-viewMinH (Node k v l r) = Just (k, v, l `unionH` r)-
− src/Bio/Bam/Reader.hs
@@ -1,295 +0,0 @@--- | Parsers for BAM and SAM.------ TONOTDO:------ * Reader for gzipped\/bzipped\/bgzf'ed SAM.  Storing SAM is a bad idea,---   so why would anyone ever want to compress, much less index it?--- * Proper support for the "=" symbol.  It's completely alien to the---   ususal representation of sequences.--module Bio.Bam.Reader (-    Block(..),-    decompressBgzfBlocks,-    decompressBgzf,-    compressBgzf,--    decodeBam,-    getBamRaw,-    decodeAnyBam,-    decodeAnyBamFile,--    BamrawEnumeratee,-    BamEnumeratee,-    isBamOrSam,--    isBam,-    isPlainBam,-    isGzipBam,-    isBgzfBam,--    decodeSam,-    decodeSam',--    decodeAnyBamOrSam,-    decodeAnyBamOrSamFile,--    concatInputs,-    concatDefaultInputs,-    mergeInputs,-    mergeDefaultInputs,-    combineCoordinates,-    combineNames,-                      ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Iteratee-import Bio.Iteratee.Bgzf-import Bio.Iteratee.ZLib hiding ( CompressionLevel )-import Bio.Prelude--import Data.Attoparsec.ByteString   ( anyWord8 )-import Data.Sequence                ( (|>) )--import qualified Data.Attoparsec.ByteString.Char8   as P-import qualified Data.ByteString                    as B-import qualified Data.ByteString.Char8              as S-import qualified Data.Foldable                      as F-import qualified Data.HashMap.Strict                as M-import qualified Data.Sequence                      as Z-import qualified Data.Vector.Generic                as V-import qualified Data.Vector.Storable               as VS-import qualified Data.Vector.Unboxed                as U--type BamrawEnumeratee m b = Enumeratee' BamMeta Bytes [BamRaw] m b-type BamEnumeratee m b = Enumeratee' BamMeta Bytes [BamRec] m b--isBamOrSam :: MonadIO m => Iteratee Bytes m (BamEnumeratee m a)-isBamOrSam = maybe decodeSam wrap `liftM` isBam-  where-    wrap enee it' = enee (mapStream unpackBam . it') >>= lift . run----- | Checks if a file contains BAM in any of the common forms,--- then decompresses it appropriately.  If the stream doesn't contain--- BAM at all, it is instead decoded as SAM.  Since SAM is next to--- impossible to recognize reliably, we don't even try.  Any old junk is--- decoded as SAM and will fail later.-decodeAnyBamOrSam :: MonadIO m => BamEnumeratee m a-decodeAnyBamOrSam it = isBamOrSam >>= \k -> k it--decodeAnyBamOrSamFile :: MonadBracketIO m-                      => FilePath -> (BamMeta -> Iteratee [BamRec] m a) -> m (Iteratee [BamRec] m a)-decodeAnyBamOrSamFile fn k = enumFileRandom defaultBufSize fn (decodeAnyBamOrSam k) >>= run---- | Iteratee-style parser for SAM files, designed to be compatible with--- the BAM parsers.  Parses plain uncompressed SAM, nothing else.  Since--- it is supposed to work the same way as the BAM parser, it requires--- the presense of the SQ header lines.  These are stripped from the--- header text and turned into the symbol table.-decodeSam :: Monad m => (BamMeta -> Iteratee [BamRec] m a) -> Iteratee Bytes m (Iteratee [BamRec] m a)-decodeSam inner = joinI $ enumLinesBS $ do-    let pHeaderLine acc str = case P.parseOnly parseBamMetaLine str of Right f -> return $ f : acc-                                                                       Left e  -> fail $ e ++ ", " ++ show str-    meta <- liftM (foldr ($) mempty . reverse) (joinI $ breakE (not . S.isPrefixOf "@") $ foldStreamM pHeaderLine [])-    decodeSamLoop (meta_refs meta) (inner meta)--decodeSamLoop :: Monad m => Refs -> Enumeratee [Bytes] [BamRec] m a-decodeSamLoop refs = convStream (liftI parse_record)-  where-    !refs' = M.fromList $ zip [ nm | BamSQ { sq_name = nm } <- F.toList refs ] [toEnum 0..]-    ref x = M.lookupDefault invalidRefseq x refs'--    parse_record (EOF x) = icont parse_record x-    parse_record (Chunk []) = liftI parse_record-    parse_record (Chunk (l:ls)) | "@" `S.isPrefixOf` l = parse_record (Chunk ls)-    parse_record (Chunk (l:ls)) = case P.parseOnly (parseSamRec ref) l of-        Right  r -> idone [r] (Chunk ls)-        Left err -> icont parse_record (Just $ iterStrExc $ err ++ ", " ++ show l)---- | Parser for SAM that doesn't look for a header.  Has the advantage--- that it doesn't stall on a pipe that never delivers data.  Has the--- disadvantage that it never reads the header and therefore needs a--- list of allowed RNAMEs.-decodeSam' :: Monad m => Refs -> Enumeratee Bytes [BamRec] m a-decodeSam' refs inner = joinI $ enumLinesBS $ decodeSamLoop refs inner--parseSamRec :: (Bytes -> Refseq) -> P.Parser BamRec-parseSamRec ref = mkBamRec-                  <$> word <*> num <*> (ref <$> word) <*> (subtract 1 <$> num)-                  <*> (Q <$> num') <*> (VS.fromList <$> cigar) <*> rnext <*> (subtract 1 <$> num)-                  <*> snum <*> sequ <*> quals <*> exts <*> pure 0-  where-    sep      = P.endOfInput <|> () <$ P.char '\t'-    word     = P.takeTill ('\t' ==) <* sep-    num      = P.decimal <* sep-    num'     = P.decimal <* sep-    snum     = P.signed P.decimal <* sep--    rnext    = id <$ P.char '=' <* sep <|> const . ref <$> word-    sequ     = {-# SCC "parseSamRec/sequ" #-}-               (V.empty <$ P.char '*' <|>-               V.fromList . map toNucleotides . S.unpack <$> P.takeWhile is_nuc) <* sep--    quals    = {-# SCC "parseSamRec/quals" #-} defaultQs <$ P.char '*' <* sep <|> bsToVec <$> word-        where-            defaultQs sq = VS.replicate (V.length sq) (Q 0xff)-            bsToVec qs _ = VS.fromList . map (Q . subtract 33) $ B.unpack qs--    cigar    = [] <$ P.char '*' <* sep <|>-               P.manyTill (flip (:*) <$> P.decimal <*> cigop) sep--    cigop    = P.choice $ zipWith (\c r -> r <$ P.char c) "MIDNSHP" [Mat,Ins,Del,Nop,SMa,HMa,Pad]-    exts     = ext `P.sepBy` sep-    ext      = (\a b v -> (fromString [a,b],v)) <$> P.anyChar <*> P.anyChar <*> (P.char ':' *> value)--    value    = P.char 'A' *> P.char ':' *> (Char <$>               anyWord8) <|>-               P.char 'i' *> P.char ':' *> (Int  <$>     P.signed P.decimal) <|>-               P.char 'Z' *> P.char ':' *> (Text <$>   P.takeTill ('\t' ==)) <|>-               P.char 'H' *> P.char ':' *> (Bin  <$>               hexarray) <|>-               P.char 'f' *> P.char ':' *> (Float . realToFrac <$> P.double) <|>-               P.char 'B' *> P.char ':' *> (-                    P.satisfy (P.inClass "cCsSiI") *> (intArr   <$> many (P.char ',' *> P.signed P.decimal)) <|>-                    P.char 'f'                     *> (floatArr <$> many (P.char ',' *> P.double)))--    intArr   is = IntArr   $ U.fromList is-    floatArr fs = FloatArr $ U.fromList $ map realToFrac fs-    hexarray    = B.pack . repack . S.unpack <$> P.takeWhile (P.inClass "0-9A-Fa-f")-    repack (a:b:cs) = fromIntegral (digitToInt a * 16 + digitToInt b) : repack cs ; repack _ = []-    is_nuc = P.inClass "acgtswkmrybdhvnACGTSWKMRYBDHVN"--    mkBamRec nm fl rn po mq cg rn' mp is sq qs' =-                BamRec nm fl rn po mq cg (rn' rn) mp is sq (qs' sq)---- | Tests if a data stream is a Bam file.--- Recognizes plain Bam, gzipped Bam and bgzf'd Bam.  If a file is--- recognized as Bam, a decoder (suitable Enumeratee) for it is--- returned.  This uses 'iLookAhead' internally, so it shouldn't consume--- anything from the stream.-isBam, isEmptyBam, isPlainBam, isBgzfBam, isGzipBam :: MonadIO m-    => Iteratee Bytes m (Maybe (BamrawEnumeratee m a))-isBam = firstOf [ isEmptyBam, isPlainBam, isBgzfBam, isGzipBam ]-  where-    firstOf [] = return Nothing-    firstOf (k:ks) = iLookAhead k >>= maybe (firstOf ks) (return . Just)--isEmptyBam = (\e -> if e then Just (\k -> return $ k mempty) else Nothing) `liftM` isFinished--isPlainBam = (\n -> if n == "BAM\SOH" then Just (joinI . decompressPlain . decodeBam) else Nothing)-             `liftM` iGetString 4---- Interesting... iLookAhead interacts badly with the parallel--- decompression of BGZF.  (The chosen interface doesn't allow the EOF--- signal to be passed on.)  One workaround would be to run sequential--- BGZF decompression to check if the content is BAM, but since BGZF is--- actually GZip in disguise, the easier workaround if to use the--- ordinary GZip decompressor.--- (A clean workaround would be an @Alternative@ instance for--- @Iteratee@.)-isBgzfBam  = do b <- isBgzf-                k <- if b then joinI $ enumInflate GZip defaultDecompressParams isPlainBam else return Nothing-                return $ const (joinI . decompressBgzfBlocks . decodeBam) `fmap` k--isGzipBam  = do b <- isGzip-                k <- if b then joinI $ enumInflate GZip defaultDecompressParams isPlainBam else return Nothing-                return $ ((joinI . enumInflate GZip defaultDecompressParams) .) `fmap` k---- | Checks if a file contains BAM in any of the common forms, then--- decompresses it appropriately.  We support plain BAM, Bgzf'd BAM,--- and Gzip'ed BAM.------ The recommendation for these functions is to use @decodeAnyBam@ (or--- @decodeAnyBamFile@) for any code that can handle @BamRaw@ input, but--- @decodeAnyBamOrSam@ (or @decodeAnyBamOrSamFile@) for code that needs--- @BamRec@.  That way, SAM is supported automatically, and seeking will--- be supported if possible.-decodeAnyBam :: MonadIO m => BamrawEnumeratee m a-decodeAnyBam it = do mk <- isBam ; case mk of Just  k -> k it-                                              Nothing -> fail "this isn't BAM."--decodeAnyBamFile :: MonadBracketIO m => FilePath -> (BamMeta -> Iteratee [BamRaw] m a) -> m (Iteratee [BamRaw] m a)-decodeAnyBamFile fn k = enumFileRandom defaultBufSize fn (decodeAnyBam k) >>= run--concatDefaultInputs :: MonadBracketIO m => Enumerator' BamMeta [BamRaw] m a-concatDefaultInputs it0 = liftIO getArgs >>= \fs -> concatInputs fs it0--concatInputs :: MonadBracketIO m => [FilePath] -> Enumerator' BamMeta [BamRaw] m a-concatInputs [       ] = enumFd defaultBufSize stdInput . decodeAnyBam >=> run-concatInputs (fp0:fps) = enum1 fp0 >=> go fps-  where-    go = foldr (\fp1 -> (>=>) (enum1 fp1 . const)) return-    enum1 "-" = enumFd   defaultBufSize stdInput . decodeAnyBam >=> run-    enum1  fp = enumFile defaultBufSize fp       . decodeAnyBam >=> run--mergeDefaultInputs :: MonadBracketIO m-    => (BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a)-    -> Enumerator' BamMeta [BamRaw] m a-mergeDefaultInputs (?) it0 = liftIO getArgs >>= \fs -> mergeInputs (?) fs it0--mergeInputs :: MonadBracketIO m-    => (BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a)-    -> [FilePath] -> Enumerator' BamMeta [BamRaw] m a-mergeInputs  _  [        ] = \k -> enumFd defaultBufSize stdInput (decodeAnyBam k) >>= run-mergeInputs (?) (fp0:fps0) = go fp0 fps0-  where-    enum1 "-" k1 = enumFd   defaultBufSize stdInput (decodeAnyBam k1) >>= run-    enum1  fp k1 = enumFile defaultBufSize fp       (decodeAnyBam k1) >>= run--    go fp [       ] = enum1 fp-    go fp (fp1:fps) = mergeEnums' (go fp1 fps) (enum1 fp) (?)--{-# INLINE combineCoordinates #-}-combineCoordinates :: Monad m => BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a-combineCoordinates _ = mergeSortStreams (?)-  where u ? v = if (b_rname &&& b_pos) (unpackBam u) < (b_rname &&& b_pos) (unpackBam v) then Less else NotLess--{-# INLINE combineNames #-}-combineNames :: Monad m => BamMeta -> Enumeratee [BamRaw] [BamRaw] (Iteratee [BamRaw] m) a-combineNames _ = mergeSortStreams (?)-  where u ? v = case b_qname (unpackBam u) `compareNames` b_qname (unpackBam v) of LT -> Less ; _ -> NotLess---- | Decode a BAM stream into raw entries.  Note that the entries can be--- unpacked using @decodeBamEntry@.  Also note that this is an--- Enumeratee in spirit, only the @BamMeta@ and @Refs@ need to get--- passed separately.-{-# INLINE decodeBam #-}-decodeBam :: Monad m => (BamMeta -> Iteratee [BamRaw] m a) -> Iteratee Block m (Iteratee [BamRaw] m a)-decodeBam inner = do meta <- liftBlock get_bam_header-                     refs <- liftBlock get_ref_array-                     convStream getBamRaw $ inner $! mmerge meta refs-  where-    get_bam_header  = do magic <- iGetString 4-                         when (magic /= "BAM\SOH") $ do-                                s <- iGetString 10-                                fail $ "BAM signature not found: " ++ show magic ++ " " ++ show s-                         hdr_len <- endianRead4 LSB-                         joinI $ takeStreamBS (fromIntegral hdr_len) $ parserToIteratee parseBamMeta--    get_ref_array = do nref <- endianRead4 LSB-                       foldM (\acc _ -> do-                                   nm <- endianRead4 LSB >>= iGetString . fromIntegral-                                   ln <- endianRead4 LSB-                                   return $! acc |> BamSQ (S.init nm) (fromIntegral ln) []-                             ) Z.empty [1..nref]--    -- Need to merge information from header into actual reference list.-    -- The latter is the authoritative source for the *order* of the-    -- sequences, so leftovers from the header are discarded.  Merging-    -- is by name.  So we merge information from the header into the-    -- list, then replace the header information.-    mmerge meta refs =-        let tbl = M.fromList [ (sq_name sq, sq) | sq <- F.toList (meta_refs meta) ]-        in meta { meta_refs = fmap (\s -> maybe s (mmerge' s) (M.lookup (sq_name s) tbl)) refs }--    mmerge' l r | sq_length l == sq_length r = l { sq_other_shit = sq_other_shit l ++ sq_other_shit r }-                | otherwise                  = l -- contradiction in header, but we'll just ignore it---{-# INLINE getBamRaw #-}-getBamRaw :: Monad m => Iteratee Block m [BamRaw]-getBamRaw = do off <- getOffset-               raw <- liftBlock $ do-                        bsize <- endianRead4 LSB-                        when (bsize < 32) $ fail "short BAM record"-                        iGetString (fromIntegral bsize)-               return [bamRaw off raw]
− src/Bio/Bam/Rec.hs
@@ -1,391 +0,0 @@-{-# LANGUAGE TypeFamilies #-}---- | Parsers and Printers for BAM and SAM.  We employ an @Iteratee@--- interface, and we strive to support everything possible in BAM.  So--- far, the implementation of the nucleotides is somewhat lacking:  we--- do not have support for ambiguity codes, and the "=" symbol is not--- understood.--module Bio.Bam.Rec (-    BamRaw,-    bamRaw,-    virt_offset,-    raw_data,--    BamRec(..),-    unpackBam,-    nullBamRec,-    getMd,--    Cigar(..),-    CigOp(..),-    alignedLength,--    Nucleotides(..), Vector_Nucs_half,-    Extensions, Ext(..),-    extAsInt, extAsString, setQualFlag,-    deleteE, insertE, updateE, adjustE,--    isPaired,-    isProperlyPaired,-    isUnmapped,-    isMateUnmapped,-    isReversed,-    isMateReversed,-    isFirstMate,-    isSecondMate,-    isAuxillary,-    isSecondary,-    isFailsQC,-    isDuplicate,-    isSupplementary,-    isTrimmed,-    isMerged,-    isAlternative,-    isExactIndex,-    type_mask,--    progressBam,-    Word32-) where--import Bio.Bam.Header-import Bio.Iteratee-import Bio.Prelude-import Bio.Util.Storable--import Control.Monad.Primitive      ( unsafePrimToPrim, unsafeInlineIO )-import Foreign.C.Types              ( CInt(..), CSize(..) )-import Foreign.Marshal.Alloc        ( alloca )--import qualified Data.ByteString                    as B-import qualified Data.ByteString.Char8              as S-import qualified Data.ByteString.Internal           as B-import qualified Data.ByteString.Unsafe             as B-import qualified Data.Vector.Generic                as V-import qualified Data.Vector.Generic.Mutable        as VM-import qualified Data.Vector.Storable               as VS-import qualified Data.Vector.Unboxed                as U----- | Cigar line in BAM coding--- Bam encodes an operation and a length into a single integer, we keep--- those integers in an array.-data Cigar = !CigOp :* !Int deriving (Eq, Ord)-infix 9 :*--data CigOp = Mat | Ins | Del | Nop | SMa | HMa | Pad-    deriving ( Eq, Ord, Enum, Show, Bounded, Ix )--instance Show Cigar where-    showsPrec _ (op :* num) = shows num . (:) (S.index "MIDNSHP" (fromEnum op))--instance Storable Cigar where-    sizeOf    _ = 4-    alignment _ = 1--    peek p = do w <- fromIntegral <$> peekUnalnWord32LE p-                return $ toEnum (w .&. 0xf) :* shiftR w 4--    poke p (op :* num) = pokeUnalnWord32LE p . fromIntegral $ fromEnum op .|. shiftL num 4----- | Extracts the aligned length from a cigar line.--- This gives the length of an alignment as measured on the reference,--- which is different from the length on the query or the length of the--- alignment.-{-# INLINE alignedLength #-}-alignedLength :: V.Vector v Cigar => v Cigar -> Int-alignedLength = V.foldl' (\a -> (a +) . l) 0-  where l (op :* n) = if op == Mat || op == Del || op == Nop then n else 0----- | internal representation of a BAM record-data BamRec = BamRec {-        b_qname :: Seqid,-        b_flag  :: Int,-        b_rname :: Refseq,-        b_pos   :: Int,-        b_mapq  :: Qual,-        b_cigar :: VS.Vector Cigar,-        b_mrnm  :: Refseq,-        b_mpos  :: Int,-        b_isize :: Int,-        b_seq   :: Vector_Nucs_half Nucleotides,-        b_qual  :: VS.Vector Qual,-        b_exts  :: Extensions,-        b_virtual_offset :: FileOffset -- ^ virtual offset for indexing purposes-    } deriving Show--nullBamRec :: BamRec-nullBamRec = BamRec {-        b_qname = S.empty,-        b_flag  = flagUnmapped,-        b_rname = invalidRefseq,-        b_pos   = invalidPos,-        b_mapq  = Q 0,-        b_cigar = VS.empty,-        b_mrnm  = invalidRefseq,-        b_mpos  = invalidPos,-        b_isize = 0,-        b_seq   = V.empty,-        b_qual  = VS.empty,-        b_exts  = [],-        b_virtual_offset = 0-    }--getMd :: BamRec -> Maybe [MdOp]-getMd r = case lookup "MD" $ b_exts r of-    Just (Text mdfield) -> readMd mdfield-    Just (Char mdfield) -> readMd $ B.singleton mdfield-    _                   -> Nothing---- | A vector that packs two 'Nucleotides' into one byte, just like Bam does.-data Vector_Nucs_half a = Vector_Nucs_half !Int !Int !(ForeignPtr Word8)---- | A mutable vector that packs two 'Nucleotides' into one byte, just like Bam does.-data MVector_Nucs_half s a = MVector_Nucs_half !Int !Int !(ForeignPtr Word8)--type instance V.Mutable Vector_Nucs_half = MVector_Nucs_half--instance V.Vector Vector_Nucs_half Nucleotides where-    {-# INLINE basicUnsafeFreeze #-}-    basicUnsafeFreeze (MVector_Nucs_half o l fp) = return $  Vector_Nucs_half o l fp-    {-# INLINE basicUnsafeThaw #-}-    basicUnsafeThaw    (Vector_Nucs_half o l fp) = return $ MVector_Nucs_half o l fp--    {-# INLINE basicLength #-}-    basicLength          (Vector_Nucs_half _ l  _) = l-    {-# INLINE basicUnsafeSlice #-}-    basicUnsafeSlice s l (Vector_Nucs_half o _ fp) = Vector_Nucs_half (o + s) l fp--    {-# INLINE basicUnsafeIndexM #-}-    basicUnsafeIndexM (Vector_Nucs_half o _ fp) i-        | even (o+i) = return . Ns $! (b `shiftR` 4) .&. 0xF-        | otherwise  = return . Ns $!  b             .&. 0xF-      where !b = unsafeInlineIO $ withForeignPtr fp $ \p -> peekByteOff p ((o+i) `shiftR` 1)--instance VM.MVector MVector_Nucs_half Nucleotides where-    {-# INLINE basicLength #-}-    basicLength          (MVector_Nucs_half _ l  _) = l-    {-# INLINE basicUnsafeSlice #-}-    basicUnsafeSlice s l (MVector_Nucs_half o _ fp) = MVector_Nucs_half (o + s) l fp--    {-# INLINE basicOverlaps #-}-    basicOverlaps (MVector_Nucs_half _ _ fp1) (MVector_Nucs_half _ _ fp2) = fp1 == fp2-    {-# INLINE basicUnsafeNew #-}-    basicUnsafeNew l = unsafePrimToPrim $ MVector_Nucs_half 0 l <$> mallocForeignPtrBytes ((l+1) `shiftR` 1)--    {-# INLINE basicInitialize #-}-    basicInitialize v@(MVector_Nucs_half o l fp)--        | even    o = do unsafePrimToPrim $ withForeignPtr fp $ \p ->-                            memset (plusPtr p (o `shiftR` 1)) 0 (fromIntegral $ l `shiftR` 1)-                         when (odd l) $ VM.basicUnsafeWrite v (l-1) (Ns 0)--        | otherwise = do when (odd o) $ VM.basicUnsafeWrite v 0 (Ns 0)-                         unsafePrimToPrim $ withForeignPtr fp $ \p ->-                            memset (plusPtr p ((o+1) `shiftR` 1)) 0 (fromIntegral $ (l-1) `shiftR` 1)-                         when (even l) $ VM.basicUnsafeWrite v (l-1) (Ns 0)---    {-# INLINE basicUnsafeRead #-}-    basicUnsafeRead (MVector_Nucs_half o _ fp) i-        | even (o+i) = liftM (Ns . (.&.) 0xF . (`shiftR` 4)) b-        | otherwise  = liftM (Ns . (.&.) 0xF               ) b-      where b = unsafePrimToPrim $ withForeignPtr fp $ \p -> peekByteOff p ((o+i) `shiftR` 1)--    {-# INLINE basicUnsafeWrite #-}-    basicUnsafeWrite (MVector_Nucs_half o _ fp) i (Ns x) =-        unsafePrimToPrim $ withForeignPtr fp $ \p -> do-            y <- peekByteOff p ((o+i) `shiftR` 1)-            let y' | even (o+i) = x `shiftL` 4 .|. y .&. 0x0F-                   | otherwise  = x            .|. y .&. 0xF0-            pokeByteOff p ((o+i) `shiftR` 1) y'--foreign import ccall unsafe "string.h memset" memset-    :: Ptr Word8 -> CInt -> CSize -> IO ()--instance Show (Vector_Nucs_half Nucleotides) where-    show = show . V.toList---- | Bam record in its native encoding along with virtual address.-data BamRaw = BamRaw { virt_offset :: {-# UNPACK #-} !FileOffset-                     , raw_data    :: {-# UNPACK #-} !Bytes }---- | Smart constructor.  Makes sure we got a at least a full record.-{-# INLINE bamRaw #-}-bamRaw :: FileOffset -> Bytes -> BamRaw-bamRaw o s = if good then BamRaw o s else error $ "broken BAM record " ++ shows (S.length s, m) " " ++ show (S.unpack (S.take 10 s))-  where-    good | S.length s < 32 = False-         | otherwise       = S.length s >= sum m-    m = [ 32, l_rnm, l_seq, (l_seq+1) `div` 2, l_cig * 4 ]-    l_rnm = fromIntegral (B.unsafeIndex s  8) - 1-    l_cig = fromIntegral (B.unsafeIndex s 12)             .|. fromIntegral (B.unsafeIndex s 13) `shiftL`  8-    l_seq = fromIntegral (B.unsafeIndex s 16)             .|. fromIntegral (B.unsafeIndex s 17) `shiftL`  8 .|.-            fromIntegral (B.unsafeIndex s 18) `shiftL` 16 .|. fromIntegral (B.unsafeIndex s 19) `shiftL` 24--{-# INLINE[1] unpackBam #-}-unpackBam :: BamRaw -> BamRec-unpackBam br = BamRec {-        b_rname =      Refseq $ getWord32  0,-        b_pos   =               getInt32   4,-        b_mapq  =           Q $ getInt8    9,-        b_flag  =               getInt16  14,-        b_mrnm  =      Refseq $ getWord32 20,-        b_mpos  =               getInt32  24,-        b_isize =               getInt32  28,--        b_qname = B.unsafeTake l_read_name $ B.unsafeDrop 32 $ raw_data br,-        b_cigar = VS.unsafeCast $ VS.unsafeFromForeignPtr fp (off0+off_c) (4*l_cigar),-        b_seq   = Vector_Nucs_half (2 * (off_s+off0)) l_seq fp,-        b_qual  = VS.unsafeCast $ VS.unsafeFromForeignPtr fp (off0+off_q) l_seq,--        b_exts  = unpackExtensions $ S.drop off_e $ raw_data br,-        b_virtual_offset = virt_offset br }-  where-        (fp, off0, _) = B.toForeignPtr $ raw_data br-        off_c =    33 + l_read_name-        off_s = off_c + 4 * l_cigar-        off_q = off_s + (l_seq + 1) `div` 2-        off_e = off_q +  l_seq--        l_read_name = getInt8    8 - 1-        l_seq       = getWord32 16-        l_cigar     = getInt16  12--        getInt8 :: Num a => Int -> a-        getInt8  o = fromIntegral (B.unsafeIndex (raw_data br) o)--        getInt16 :: Num a => Int -> a-        getInt16 o = unsafeDupablePerformIO $ B.unsafeUseAsCString (raw_data br) $-                     fmap fromIntegral . peekUnalnWord16LE . flip plusPtr o--        getWord32 :: Num a => Int -> a-        getWord32 o = unsafeDupablePerformIO $ B.unsafeUseAsCString (raw_data br) $-                      fmap fromIntegral . peekUnalnWord32LE . flip plusPtr o--        -- ensures proper sign extension-        getInt32 :: Num a => Int -> a-        getInt32 o = fromIntegral (getWord32 o :: Int32)---- | A collection of extension fields.  A 'BamKey' is actually two ASCII--- characters.-type Extensions = [( BamKey, Ext )]---- | Deletes all occurences of some extension field.-deleteE :: BamKey -> Extensions -> Extensions-deleteE k = filter ((/=) k . fst)---- | Blindly inserts an extension field.  This can create duplicates--- (and there is no telling how other tools react to that).-insertE :: BamKey -> Ext -> Extensions -> Extensions-insertE k v = (:) (k,v)---- | Deletes all occurences of an extension field, then inserts it with--- a new value.  This is safer than 'insertE', but also more expensive.-updateE :: BamKey -> Ext -> Extensions -> Extensions-updateE k v = insertE k v . deleteE k---- | Adjusts a named extension by applying a function.-adjustE :: (Ext -> Ext) -> BamKey -> Extensions -> Extensions-adjustE _ _ [         ]             = []-adjustE f k ((k',v):es) | k  ==  k' = (k', f v) : es-                        | otherwise = (k',   v) : adjustE f k es--data Ext = Int Int | Float Float | Text Bytes | Bin Bytes | Char Word8-         | IntArr (U.Vector Int) | FloatArr (U.Vector Float)-    deriving (Show, Eq, Ord)--{-# INLINE unpackExtensions #-}-unpackExtensions :: Bytes -> Extensions-unpackExtensions = go-  where-    go s | S.length s < 4 = []-         | otherwise = let key = fromString [ S.index s 0, S.index s 1 ]-                       in case S.index s 2 of-                         'Z' -> case S.break (== '\0') (S.drop 3 s) of (l,r) -> (key, Text l) : go (S.drop 1 r)-                         'H' -> case S.break (== '\0') (S.drop 3 s) of (l,r) -> (key, Bin  l) : go (S.drop 1 r)-                         'A' -> (key, Char (B.index s 3)) : go (S.drop 4 s)-                         'B' -> let tp = S.index s 3-                                    n  = getInt 'I' (S.drop 4 s)-                                in case tp of-                                      'f' -> (key, FloatArr (U.fromListN (n+1) [ getFloat (S.drop i s) | i <- [8, 12 ..] ]))-                                             : go (S.drop (12+4*n) s)-                                      _   -> (key, IntArr (U.fromListN (n+1) [ getInt tp (S.drop i s) | i <- [8, 8 + size tp ..] ]))-                                             : go (S.drop (8 + size tp * (n+1)) s)-                         'f' -> (key, Float (getFloat (S.drop 3 s))) : go (S.drop 7 s)-                         tp  -> (key, Int  (getInt tp (S.drop 3 s))) : go (S.drop (3 + size tp) s)--    size 'C' = 1-    size 'c' = 1-    size 'S' = 2-    size 's' = 2-    size 'I' = 4-    size 'i' = 4-    size 'f' = 4-    size  _  = 0--    getInt 'C' s | S.length s >= 1 = fromIntegral (fromIntegral (B.index s 0) :: Word8)-    getInt 'c' s | S.length s >= 1 = fromIntegral (fromIntegral (B.index s 0) ::  Int8)-    getInt 'S' s | S.length s >= 2 = fromIntegral                         (i :: Word16)-        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord16LE-    getInt 's' s | S.length s >= 2 = fromIntegral            (fromIntegral i ::  Int16)-        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord16LE-    getInt 'I' s | S.length s >= 4 = fromIntegral                         (i :: Word32)-        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord32LE-    getInt 'i' s | S.length s >= 4 = fromIntegral            (fromIntegral i ::  Int32)-        where i = unsafeDupablePerformIO $ B.unsafeUseAsCString s $ peekUnalnWord32LE-    getInt _ _ = 0--    getFloat s = unsafeDupablePerformIO $ alloca $ \buf ->-                 pokeByteOff buf 0 (getInt 'I' s :: Word32) >> peek buf---isPaired, isProperlyPaired, isUnmapped, isMateUnmapped, isReversed,-    isMateReversed, isFirstMate, isSecondMate, isAuxillary, isSecondary,-    isFailsQC, isDuplicate, isSupplementary,-    isTrimmed, isMerged, isAlternative, isExactIndex :: BamRec -> Bool--isPaired         = flip testBit  0 . b_flag-isProperlyPaired = flip testBit  1 . b_flag-isUnmapped       = flip testBit  2 . b_flag-isMateUnmapped   = flip testBit  3 . b_flag-isReversed       = flip testBit  4 . b_flag-isMateReversed   = flip testBit  5 . b_flag-isFirstMate      = flip testBit  6 . b_flag-isSecondMate     = flip testBit  7 . b_flag-isAuxillary      = flip testBit  8 . b_flag-isSecondary      = flip testBit  8 . b_flag-isFailsQC        = flip testBit  9 . b_flag-isDuplicate      = flip testBit 10 . b_flag-isSupplementary  = flip testBit 11 . b_flag--isTrimmed        = flip testBit 0 . extAsInt 0 "FF"-isMerged         = flip testBit 1 . extAsInt 0 "FF"-isAlternative    = flip testBit 2 . extAsInt 0 "FF"-isExactIndex     = flip testBit 3 . extAsInt 0 "FF"--type_mask :: Int-type_mask = flagFirstMate .|. flagSecondMate .|. flagPaired--extAsInt :: Int -> BamKey -> BamRec -> Int-extAsInt d nm br = case lookup nm (b_exts br) of Just (Int i) -> i ; _ -> d--extAsString :: BamKey -> BamRec -> Bytes-extAsString nm br = case lookup nm (b_exts br) of-    Just (Char c) -> B.singleton c-    Just (Text s) -> s-    _             -> B.empty--setQualFlag :: Char -> BamRec -> BamRec-setQualFlag c br = br { b_exts = updateE "ZQ" (Text s') $ b_exts br }-  where-    s  = extAsString "ZQ" br-    s' = if c `S.elem` s then s else c `S.cons` s---- | A simple progress indicator that prints sequence id and position.-progressBam :: MonadIO m => String -> Refs -> Int -> (String -> IO ()) -> Enumeratee [BamRaw] [BamRaw] m a-progressBam = progressPos (\br -> case unpackBam br of b -> (b_rname b, b_pos b))-
− src/Bio/Bam/Regions.hs
@@ -1,45 +0,0 @@-module Bio.Bam.Regions where--import Bio.Bam.Header ( Refseq(..) )-import Bio.Prelude--import qualified Data.IntMap.Strict as IM--data Region = Region { refseq :: !Refseq, start :: !Int, end :: !Int }-  deriving (Eq, Ord, Show)---- | A subset of a genome.  The idea is to map the reference sequence--- (represented by its number) to a 'Subseqeunce'.-newtype Regions = Regions (IM.IntMap Subsequence) deriving Show---- | A mostly contiguous subset of a sequence, stored as a set of--- non-overlapping intervals in an 'IntMap' from start position to end--- position (half-open intervals, naturally).-newtype Subsequence = Subsequence (IM.IntMap Int) deriving Show--toList :: Regions -> [(Refseq, Subsequence)]-toList (Regions m) = [ (Refseq $ fromIntegral k, v) | (k,v) <- IM.toList m ]--fromList :: [Region] -> Regions-fromList = foldl' (flip add) (Regions IM.empty)--add :: Region -> Regions -> Regions-add (Region (Refseq r) b e) (Regions m) =-    let single = Just . Subsequence $ IM.singleton b e-    in Regions $ IM.alter (maybe single (Just . addInt b e)) (fromIntegral r) m---addInt :: Int -> Int -> Subsequence -> Subsequence-addInt b e (Subsequence m0) = Subsequence $ merge_into b e m0-  where-    merge_into x y m = case IM.lookupLT y m of-        Just (u,v) | x < u && y <= v -> merge_into x v $ IM.delete u m    -- extend to the left-                   | x < u           -> merge_into x y $ IM.delete u m    -- subsume-                   | y <= v          -> m                                 -- subsumed-                   | x <= v          -> merge_into u y $ IM.delete u m    -- extend to the right-        _                            -> IM.insert  x y m                  -- no overlap--overlaps :: Int -> Int -> Subsequence -> Bool-overlaps b e (Subsequence m) = case IM.lookupLT e m of-        Just (_,v) -> b < v-        Nothing    -> False
− src/Bio/Bam/Rmdup.hs
@@ -1,688 +0,0 @@-module Bio.Bam.Rmdup(-            rmdup, Collapse, cons_collapse, cheap_collapse,-            cons_collapse_keep, cheap_collapse_keep,-            check_sort, normalizeTo, wrapTo,-            ECig(..), toECig, setMD, toCigar-    ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Iteratee-import Bio.Prelude hiding ( left, right )--import qualified Data.ByteString        as B-import qualified Data.ByteString.Char8  as T-import qualified Data.Map.Strict        as M-import qualified Data.Vector.Generic    as V-import qualified Data.Vector.Storable   as VS-import qualified Data.Vector.Unboxed    as U--data Collapse = Collapse {-        collapse  :: [BamRec] -> (Decision,[BamRec]),    -- cluster to consensus and stuff or representative and stuff-        originals :: [BamRec] -> [BamRec] }              -- treatment of the redundant original reads--data Decision = Consensus      { fromDecision :: BamRec }-              | Representative { fromDecision :: BamRec }--cons_collapse :: Qual -> Collapse-cons_collapse maxq = Collapse (do_collapse maxq) (const [])--cons_collapse_keep :: Qual -> Collapse-cons_collapse_keep maxq = Collapse (do_collapse maxq) (map (\b -> b { b_flag = b_flag b .|. flagDuplicate }))--cheap_collapse :: Collapse-cheap_collapse = Collapse do_cheap_collapse (const [])--cheap_collapse_keep :: Collapse-cheap_collapse_keep = Collapse do_cheap_collapse (map (\b -> b { b_flag = b_flag b .|. flagDuplicate }))----- | Removes duplicates from an aligned, sorted BAM stream.------ The incoming stream must be sorted by coordinate, and we check for--- violations of that assumption.  We cannot assume that length was--- taken into account when sorting (samtools doesn't do so), so--- duplicates may be separated by reads that start at the same position--- but have different length or different strand.------ We are looking at three different kinds of reads:  paired reads, true--- single ended reads, merged or trimmed reads.  They are somewhat--- different, but here's the situation if we wanted to treat them--- separately.  These conditions define a set of duplicates:------ Merged or trimmed:  We compare the leftmost coordinates and the--- aligned length.  If the library prep is strand-preserving, we also--- compare the strand.------ Paired: We compare both left-most coordinates (b_pos and b_mpos).  If--- the library prep is strand-preserving, only first-mates can be--- duplicates of first-mates.  Else a first-mate can be the duplicate of--- a second-mate.  There may be pairs with one unmapped mate.  This is--- not a problem as they get assigned synthetic coordinates and will be--- handled smoothly.------ True singles:  We compare only the leftmost coordinate.  It does not--- matter if the library prep is strand-preserving, the strand always--- matters.------ Across these classes, we can see more duplicates:------ Merged/trimmed and paired:  these can be duplicates if the merging--- failed for the pair.  We would need to compare the outer coordinates--- of the merged reads to the two 5' coordinates of the pair.  However,--- since we don't have access to the mate, we cannot actually do--- anything right here.  This case should be solved externally by--- merging those pairs that overlap in coordinate space.------ Single and paired:  in the single case, we only have one coordinate--- to compare.  This will inevitably lead to trouble, as we could find--- that the single might be the duplicate of two pairs, but those two--- pairs are definitely not duplicates of each other.  We solve it by--- removing the single read(s).------ Single and merged/trimmed:  same trouble as in the single+paired--- case.  We remove the single to solve it.--------- In principle, we might want to allow some wiggle room in the--- coordinates.  So far, this has not been implemented.  It adds the--- complication that groups of separated reads can turn into a set of--- duplicates because of the appearance of a new reads.  Needs some--- thinking about... or maybe it's not too important.------ Once a set of duplicates is collected, we perform a majority vote on--- the correct CIGAR line.  Of all those reads that agree on this CIGAR--- line, a consensus is called, quality scores are adjusted and clamped--- to a maximum, the MD field is updated and the XP field is assigned--- the number of reads in the original cluster.  The new MAPQ becomes--- the RMSQ of the map qualities of all reads.------ Treatment of Read Groups:  We generalize by providing a "label"--- function; only reads that have the same label are considered--- duplicates of each other.  The typical label function would extract--- read groups, libraries or samples.--rmdup :: (Monad m, Ord l) => (BamRec -> l) -> Bool -> Collapse -> Enumeratee [BamRec] [(Int,BamRec)] m r-rmdup label strand_preserved collapse_cfg =-    -- Easiest way to go about this:  We simply collect everything that-    -- starts at some specific coordinate and group it appropriately.-    -- Treat the groups separately, output, go on.-    check_sort id "input must be sorted for rmdup to work" ><>-    mapGroups rmdup_group ><>-    check_sort snd "internal error, output isn't sorted anymore"-  where-    rmdup_group = nice_sort . do_rmdup label strand_preserved collapse_cfg-    same_pos u v = b_cpos u == b_cpos v-    b_cpos u = (b_rname u, b_pos u)--    nice_sort = sortBy $ comparing (V.length . b_seq . snd)--    mapGroups f o = tryHead >>= maybe (return o) (\a -> eneeCheckIfDone (mg1 f a []) o)-    mg1 f a acc k = tryHead >>= \case-                        Nothing -> return . k . Chunk . f $ a:acc-                        Just b | same_pos a b -> mg1 f a (b:acc) k-                               | otherwise -> eneeCheckIfDone (mg1 f b []) . k . Chunk . f $ a:acc--check_sort :: Monad m => (a -> BamRec) -> String -> Enumeratee [a] [a] m b-check_sort f msg out = tryHead >>= maybe (return out) (\a -> eneeCheckIfDone (step a) out)-  where-    step a k = tryHead >>= maybe (return . k $ Chunk [a]) (step' a k)-    step' a k b | (b_rname (f a), b_pos (f a)) > (b_rname (f b), b_pos (f b)) = fail $ "rmdup: " ++ msg-                | otherwise = eneeCheckIfDone (step b) . k $ Chunk [a]---{- | Workhorse for duplicate removal.-- - Unmapped fragments should not be considered to be duplicates of-   mapped fragments.  The /unmapped/ flag can serve for that:  while-   there are two classes of /unmapped/ reads (those that are not mapped-   and those that are mapped to an invalid position), the two sets will-   always have different coordinates.  (Unfortunately, correct duplicate-   removal now relies on correct /unmapped/ and /mate unmapped/ flags,-   and we don't get them from unmodified BWA.  So correct operation-   requires patched BWA or a run of @bam-fixpair@.)--   (1) Other definitions (e.g. lack of CIGAR) don't work, because that-       information won't be available for the mate.--   (2) This would amount to making the /unmapped/ flag part of the-       coordinate, but samtools is not going to take it into account-       when sorting.--   (3) Instead, both flags become part of the /mate pos/ grouping-       criterion.-- - First Mates should (probably) not be considered duplicates of Second-   Mates.  This is unconditionally true for libraries with A\/B-style-   adapters (definitely 454, probably Mathias' ds protocol) and the ss-   protocol, it is not true for fork adapter protocols (vanilla Illumina-   protocol).  So it has to be an option, which would ideally be derived-   from header information.-- - This code ignores read groups, but it will do a majority vote on the-   @RG@ field and call consensi for the index sequences.  If you believe-   that duplicates across read groups are impossible, you must call it-   with an appropriately filtered stream.-- - Half-Aligned Pairs (meaning one known coordinate, while the validity-   of the alignments is immaterial) are rather complicated:--   (1) Given that only one coordinate is known (5' of the aligned mate),-       we want to treat them like true singles.  But the unaligned mate-       should be kept if possible, though it should not contribute to a-       consensus sequence.  We assume nothing about the unaligned mate,-       not even that it /shouldn't/ be aligned, never mind the fact that-       it /couldn't/ be.  (The difference is in the finite abilities of-       real world aligners, naturally.)--   (2) Therefore, aligned reads with unaligned mates go to the same-       potential duplicate set as true singletons.  If at least one pair-       exists that might be a duplicate of those, all singletons and-       half-aligned mates are discarded.  Else a consensus is computed-       and replaces the aligned mates.--   (3) The unaligned mates end up in the same place in a BAM stream as-       the aligned mates (therefore we see them and can treat them-       locally).  We cannot call a consensus, since these molecules may-       well have different length, so we select one.  It doesn't really-       matter which one is selected, and since we're treating both mates-       at the same time, it doesn't even need to be reproducible without-       local information.  This is made to be the mate of the consensus.--   (4) See 'merge_singles' for how it's actually done.--}--do_rmdup :: Ord l => (BamRec -> l) -> Bool -> Collapse -> [BamRec] -> [(Int,BamRec)]-do_rmdup label strand_preserved Collapse{..} =-    concatMap do_rmdup1 . M.elems . accumMap label id-  where-    do_rmdup1 rds = results ++ map ((,) 0) (originals (leftovers ++ r1 ++ r2 ++ r3))-      where-        (results, leftovers) = merge_singles singles' unaligned' $-                [ (str, second fromDecision b) | ((_,str  ),b) <- M.toList merged' ] ++-                [ (str, second fromDecision b) | ((_,str,_),b) <- M.toList pairs' ]--        (raw_pairs, raw_singles)       = partition isPaired rds-        (merged, true_singles)         = partition (liftA2 (||) isMerged isTrimmed) raw_singles--        (pairs, raw_half_pairs)        = partition b_totally_aligned raw_pairs-        (half_unaligned, half_aligned) = partition isUnmapped raw_half_pairs--        mkMap :: Ord a => (BamRec -> a) -> [BamRec] -> (M.Map a (Int,Decision), [BamRec])-        mkMap f x = let m1 = M.map (length &&& collapse) $ accumMap f id x-                    in (M.map (second fst) m1, concatMap (snd.snd) $ M.elems m1)--        (pairs',r1)   = mkMap (\b -> (b_mate_pos b,   b_strand b, b_mate b)) pairs-        (merged',r2)  = mkMap (\b -> (alignedLength (b_cigar b), b_strand b))           merged-        (singles',r3) = mkMap                         b_strand (true_singles++half_aligned)-        unaligned'    = accumMap b_strand id half_unaligned--        b_strand b = strand_preserved && isReversed  b-        b_mate   b = strand_preserved && isFirstMate b----- | Merging information about true singles, merged singles,--- half-aligned pairs, actually aligned pairs.------ We collected aligned reads with unaligned mates together with aligned--- true singles (@singles@).  We collected the unaligned mates, which--- necessarily have the exact same alignment coordinates, separately--- (@unaligned@).  If we don't find a matching true pair (that case is--- already handled smoothly), we keep the highest quality unaligned--- mate, pair it with the consensus of the aligned mates and aligned--- singletons, and give it the lexically smallest name of the--- half-aligned pairs.---- NOTE:  I need to decide when to run 'make_singleton'.  Basically,--- when we call a consensus for half-aligned pairs and keep--- everything(?).  Then we don't have a mate for the consensus... though--- we could decide to duplicate one mate read to get it.--merge_singles :: M.Map Bool (Int,Decision)              -- strand --> true singles & half aligned-              -> M.Map Bool [BamRec]                    -- strand --> half unaligned-              -> [ (Bool, (Int, BamRec)) ]              -- strand --> paireds & mergeds-              -> ([(Int,BamRec)],[BamRec])              -- results, leftovers--merge_singles singles unaligneds = go-  where-    -- Say we generated a consensus or passed something through.  If-    -- there is a singleton consensus with the same strand, we should-    -- add in its XP field and discard it.  If there is a singleton-    -- representative, we add in its XP field and put it into the-    -- leftovers.  If there is unaligned stuff here that has the same-    -- strand, it goes to the leftovers.-    go ( (str, (m,v)) : paireds) =-        let (r,l) = merge_singles (M.delete str singles) (M.delete str unaligneds) paireds-            unal  = M.findWithDefault [] str unaligneds ++ l--        in case M.lookup str singles of-            Nothing                    -> (              (m,v) : r,     unal )-            Just (n, Consensus      w) -> ( (n, add_xp_of w v) : r,     unal )-            Just (n, Representative w) -> ( (n, add_xp_of w v) : r, w : unal )--    -- No more pairs, delegate the problem-    go [] = merge_halves unaligneds (M.toList singles)--    add_xp_of w v = v { b_exts = updateE "XP" (Int $ extAsInt 1 "XP" w `oplus` extAsInt 1 "XP" v) (b_exts v) }---- | Merging of half-aligned reads.  The first argument is a map of--- unaligned reads (their mates are aligned to the current position),--- the second is a list of reads that are aligned (their mates are not--- aligned).------ So, suppose we're looking at a 'Representative' that was passed--- through.  We need to emit it along with its mate, which may be hidden--- inside a list.  (Alternatively, we could force it to single, but that--- fails if we're passing everything along somehow.)------ Suppose we're looking at a 'Consensus'.  We could pair it with some--- mate (which we'd need to duplicate), or we could turn it into a--- singleton.  Duplication is ugly, so in this case, we force it to--- singleton.--merge_halves :: M.Map Bool [BamRec]                     -- strand --> half unaligned-             -> [(Bool, (Int,Decision))]                -- strand --> true singles & half aligned-             -> ([(Int,BamRec)],[BamRec])               -- results, leftovers---- Emitting a consensus: make it a single.  Nothing goes to leftovers;--- we may still need it for something else to be emitted.  (While that--- would be strange, making sure the BAM file stays completely valid is--- probably better.)-merge_halves unaligneds ((_, (n, Consensus v)) : singles) = ( (n, v { b_flag = b_flag v .&. complement pflags }) : r, l )-  where-    (r,l)  = merge_halves unaligneds singles-    pflags = flagPaired .|. flagProperlyPaired .|. flagMateUnmapped .|. flagMateReversed .|. flagFirstMate .|. flagSecondMate----- Emitting a representative:  find the mate in the list of unaligned--- reads (take up to one match to be robust), and emit that, too, as a--- result.  Everything else goes to leftovers.  If the representative--- happens to be unpaired, no mate is found and that case therefore is--- handled smoothly.-merge_halves unaligneds ((str, (n, Representative v)) : singles) = ((n,v) : map ((,)1) (take 1 same) ++ r, drop 1 same ++ diff ++ l)-  where-    (r,l)          = merge_halves (M.delete str unaligneds) singles-    (same,diff)    = partition (is_mate_of v) $ M.findWithDefault [] str unaligneds-    is_mate_of a b = b_qname a == b_qname b && isPaired a && isPaired b && isFirstMate a == isSecondMate b---- No more singles, all unaligneds are leftovers.-merge_halves unaligneds [] = ( [], concat $ M.elems unaligneds )-----type MPos = (Refseq, Int, Bool, Bool)--b_mate_pos :: BamRec -> MPos-b_mate_pos b = (b_mrnm b, b_mpos b, isUnmapped b, isMateUnmapped b)--b_totally_aligned :: BamRec -> Bool-b_totally_aligned b = not (isUnmapped b || isMateUnmapped b)---accumMap :: Ord k => (a -> k) -> (a -> v) -> [a] -> M.Map k [v]-accumMap f g = go M.empty-  where-    go m [    ] = m-    go m (a:as) = let ws = M.findWithDefault [] (f a) m ; g' = g a-                  in g' `seq` go (M.insert (f a) (g':ws) m) as---{- We need to deal sensibly with each field, but different fields have-   different needs.  We can take the value from the first read to-   preserve determinism or because all reads should be equal anyway,-   aggregate over all reads computing either RMSQ or the most common-   value, delete a field because it wouldn't make sense anymore or-   because doing something sensible would be hard and we're going to-   ignore it anyway, or we calculate some special value; see below.-   Unknown fields will be taken from the first read, which seems to be a-   safe default.--   QNAME and most fields              taken from first-   FLAG qc fail                       majority vote-        dup                           deleted-   MAPQ                               rmsq-   CIGAR, SEQ, QUAL, MD, NM, XP       generated-   XA                                 concatenate all--   BQ, CM, FZ, Q2, R2, XM, XO, XG, YQ, EN-         deleted because they would become wrong--   CQ, CS, E2, FS, OQ, OP, OC, U2, H0, H1, H2, HI, NH, IH, ZQ-         delete because they will be ignored anyway--   AM, AS, MQ, PQ, SM, UQ-         compute rmsq--   X0, X1, XT, XS, XF, XE, BC, LB, RG, XI, YI, XJ, YJ-         majority vote -}--do_collapse :: Qual -> [BamRec] -> (Decision, [BamRec])-do_collapse maxq [br] | V.all (<= maxq) (b_qual br) = ( Representative br, [  ] )     -- no modifcation, pass through-                      | otherwise                   = ( Consensus   lq_br, [br] )     -- qualities reduced, must keep original-  where-    lq_br = br { b_qual  = V.map (min maxq) $ b_qual br-               , b_virtual_offset = 0-               , b_qname = b_qname br `B.snoc` c2w 'c' }--do_collapse maxq  brs = ( Consensus b0 { b_exts  = modify_extensions $ b_exts b0-                                       , b_flag  = failflag .&. complement flagDuplicate-                                       , b_mapq  = Q $ rmsq $ map (unQ . b_mapq) $ good brs-                                       , b_cigar = cigar'-                                       , b_seq   = V.fromList $ map fst cons_seq_qual-                                       , b_qual  = V.fromList $ map snd cons_seq_qual-                                       , b_qname = b_qname b0 `B.snoc` 99-                                       , b_virtual_offset = 0 }, brs )              -- many modifications, must keep everything-  where-    !b0 = minimumBy (comparing b_qname) brs-    !most_fail = 2 * length (filter isFailsQC brs) > length brs-    !failflag | most_fail = b_flag b0 .|. flagFailsQC-              | otherwise = b_flag b0 .&. complement flagFailsQC--    rmsq xs = case foldl' (\(!n,!d) x -> (n + fromIntegral x * fromIntegral x, d + 1)) (0,0) xs of-        (!n,!d) -> round $ sqrt $ (n::Double) / fromIntegral (d::Int)--    maj xs = head . maximumBy (comparing length) . group . sort $ xs-    nub' = concatMap head . group . sort--    -- majority vote on the cigar lines, then filter-    !cigar' = maj $ map b_cigar brs-    good = filter ((==) cigar' . b_cigar)--    cons_seq_qual = [ consensus maxq [ (V.unsafeIndex (b_seq b) i, q)-                                     | b <- good brs, let q = if V.null (b_qual b) then Q 23 else b_qual b V.! i ]-                    | i <- [0 .. len - 1] ]-        where !len = V.length . b_seq . head $ good brs--    md' = case [ (b_seq b,md,b) | b <- good brs, Just md <- [ getMd b ] ] of-                [               ] -> []-                (seq1, md1,b) : _ -> case mk_new_md' [] (V.toList cigar') md1 (V.toList seq1) (map fst cons_seq_qual) of-                    Right x -> x-                    Left (MdFail cigs ms osq nsq) -> error $ unlines-                                    [ "Broken MD field when trying to construct new MD!"-                                    , "QNAME: " ++ show (b_qname b)-                                    , "POS:   " ++ shows (unRefseq (b_rname b)) ":" ++ show (b_pos b)-                                    , "CIGAR: " ++ show cigs-                                    , "MD:    " ++ show ms-                                    , "refseq:  " ++ show osq-                                    , "readseq: " ++ show nsq ]---    nm' = sum $ [ n | Ins :* n <- VS.toList cigar' ] ++ [ n | Del :* n <- VS.toList cigar' ] ++ [ 1 | MdRep _ <- md' ]-    xa' = nub' [ T.split ';' xas | Just (Text xas) <- map (lookup "XA" . b_exts) brs ]--    modify_extensions es = foldr ($!) es $-        [ let vs = mapMaybe (lookup k . b_exts) brs-          in if null vs then id else updateE k $! maj vs | k <- do_maj ] ++-        [ let vs = [ v | Just (Int v) <- map (lookup k . b_exts) brs ]-          in if null vs then id else updateE k $! Int (rmsq vs) | k <- do_rmsq ] ++-        map deleteE useless ++-        [ updateE "NM" $! Int nm'-        , updateE "XP" $! Int (foldl' (\a b -> a `oplus` extAsInt 1 "XP" b) 0 brs)-        , if null xa' then id else updateE "XA" $! (Text $ T.intercalate (T.singleton ';') xa')-        , if null md' then id else updateE "MD" $! (Text $ showMd md') ]--    useless = map fromString $ words "BQ CM FZ Q2 R2 XM XO XG YQ EN CQ CS E2 FS OQ OP OC U2 H0 H1 H2 HI NH IH ZQ"-    do_rmsq = map fromString $ words "AM AS MQ PQ SM UQ"-    do_maj  = map fromString $ words "X0 X1 XT XS XF XE BC LB RG XI XJ YI YJ"--minViewBy :: (a -> a -> Ordering) -> [a] -> (a,[a])-minViewBy  _  [    ] = error "minViewBy on empty list"-minViewBy cmp (x:xs) = go x [] xs-  where-    go m acc [    ] = (m,acc)-    go m acc (a:as) = case m `cmp` a of GT -> go a (m:acc) as-                                        _  -> go m (a:acc) as--data MdFail = MdFail [Cigar] [MdOp] [Nucleotides] [Nucleotides]--mk_new_md' :: [MdOp] -> [Cigar] -> [MdOp] -> [Nucleotides] -> [Nucleotides] -> Either MdFail [MdOp]-mk_new_md' acc [] [] [] [] = Right $ normalize [] acc-    where-        normalize          a  (MdNum  0:os) = normalize               a  os-        normalize (MdNum n:a) (MdNum  m:os) = normalize (MdNum  (n+m):a) os-        normalize          a  (MdDel []:os) = normalize               a  os-        normalize (MdDel u:a) (MdDel  v:os) = normalize (MdDel (v++u):a) os-        normalize          a  (       o:os) = normalize            (o:a) os-        normalize          a  [           ] = a--mk_new_md' acc ( _ :* 0 : cigs) mds  osq nsq = mk_new_md' acc cigs mds osq nsq-mk_new_md' acc cigs (MdNum  0 : mds) osq nsq = mk_new_md' acc cigs mds osq nsq-mk_new_md' acc cigs (MdDel [] : mds) osq nsq = mk_new_md' acc cigs mds osq nsq--mk_new_md' acc (Mat :* u : cigs) (MdRep b : mds) (_:osq) (n:nsq)-    | b == n    = mk_new_md' (MdNum 1 : acc) (Mat :* (u-1):cigs) mds osq nsq-    | otherwise = mk_new_md' (MdRep b : acc) (Mat :* (u-1):cigs) mds osq nsq--mk_new_md' acc (Mat :* u : cigs) (MdNum v : mds) (o:osq) (n:nsq)-    | o == n    = mk_new_md' (MdNum 1 : acc) (Mat :* (u-1):cigs) (MdNum (v-1) : mds) osq nsq-    | otherwise = mk_new_md' (MdRep o : acc) (Mat :* (u-1):cigs) (MdNum (v-1) : mds) osq nsq--mk_new_md' acc (Del :* n : cigs) (MdDel bs : mds) osq nsq | n == length bs = mk_new_md' (MdDel bs : acc)         cigs               mds  osq nsq-mk_new_md' acc (Del :* n : cigs) (MdDel (b:bs) : mds) osq nsq = mk_new_md' (MdDel     [b] : acc) (Del :* (n-1) : cigs) (MdDel    bs:mds) osq nsq-mk_new_md' acc (Del :* n : cigs) (MdRep   b    : mds) osq nsq = mk_new_md' (MdDel     [b] : acc) (Del :* (n-1) : cigs)              mds  osq nsq-mk_new_md' acc (Del :* n : cigs) (MdNum   m    : mds) osq nsq = mk_new_md' (MdDel [nucsN] : acc) (Del :* (n-1) : cigs) (MdNum (m-1):mds) osq nsq--mk_new_md' acc (Ins :* n : cigs) md osq nsq = mk_new_md' acc cigs md (drop n osq) (drop n nsq)-mk_new_md' acc (SMa :* n : cigs) md osq nsq = mk_new_md' acc cigs md (drop n osq) (drop n nsq)-mk_new_md' acc (HMa :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq-mk_new_md' acc (Pad :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq-mk_new_md' acc (Nop :* _ : cigs) md osq nsq = mk_new_md' acc cigs md         osq          nsq--mk_new_md' _acc cigs ms osq nsq = Left $ MdFail cigs ms osq nsq--consensus :: Qual -> [ (Nucleotides, Qual) ] -> (Nucleotides, Qual)-consensus (Q maxq) nqs = if qr > 3 then (n0, Q qr) else (nucsN, Q 0)-  where-    accs :: U.Vector Int-    accs = U.accum (+) (U.replicate 16 0) [ (fromIntegral n, fromIntegral q) | (Ns n,Q q) <- nqs ]--    (n0,q0) : (_,q1) : _ = sortBy (flip $ comparing snd) $ zip [Ns 0 ..] $ U.toList accs-    qr = fromIntegral $ (q0-q1) `min` fromIntegral maxq----- Cheap version: simply takes the lexically first record, adds XP field-do_cheap_collapse :: [BamRec] -> ( Decision, [BamRec] )-do_cheap_collapse [b] = ( Representative                     b, [] )-do_cheap_collapse  bs = ( Representative $ replaceXP new_xp b0, bx )-  where-    (b0, bx) = minViewBy (comparing b_qname) bs-    new_xp   = foldl' (\a b -> a `oplus` extAsInt 1 "XP" b) 0 bs--replaceXP :: Int -> BamRec -> BamRec-replaceXP new_xp b0 = b0 { b_exts = updateE "XP" (Int new_xp) $ b_exts b0 }--oplus :: Int -> Int -> Int-_ `oplus` (-1) = -1-(-1) `oplus` _ = -1-a `oplus` b = a + b---- | Normalize a read's alignment to fall into the canonical region--- of [0..l].  Takes the name of the reference sequence and its length.--- Returns @Left x@ if the coordinate decreased so the result is out of--- order now, @Right x@ if the coordinate is unchanged.-normalizeTo :: Seqid -> Int -> BamRec -> Either BamRec BamRec-normalizeTo nm l b = lr $ b { b_pos  = b_pos b `mod` l-                            , b_mpos = b_mpos b `mod` l-                            , b_mapq = if dups_are_fine then Q 37 else b_mapq b-                            , b_exts = if dups_are_fine then deleteE "XA" (b_exts b) else b_exts b }-  where-    lr = if b_pos b >= l then Left else Right-    dups_are_fine  = all_match_XA (extAsString "XA" b)-    all_match_XA s = case T.split ';' s of [xa1, xa2] | T.null xa2 -> one_match_XA xa1-                                           [xa1]                   -> one_match_XA xa1-                                           _                       -> False-    one_match_XA s = case T.split ',' s of (sq:pos:_) | sq == nm   -> pos_match_XA pos ; _ -> False-    pos_match_XA s = case T.readInt s   of Just (p,z) | T.null z   -> int_match_XA p ;   _ -> False-    int_match_XA p | p >= 0    =  (p-1) `mod` l == b_pos b `mod` l && not (isReversed b)-                   | otherwise = (-p-1) `mod` l == b_pos b `mod` l && isReversed b----- | Wraps a read to be fully contained in the canonical interval--- [0..l].  If the read overhangs, it is duplicated and both copies are--- suitably masked.  A piece with changed coordinate that is now out of--- order is returned as @Left x@, if the order is fine, it is returned--- as @Right x@.-wrapTo :: Int -> BamRec -> [Either BamRec BamRec]-wrapTo l b = if overhangs then do_wrap else [Right b]-  where-    overhangs = not (isUnmapped b) && b_pos b < l && l < b_pos b + alignedLength (b_cigar b)--    do_wrap = case split_ecig (l - b_pos b) $ toECig (b_cigar b) (fromMaybe [] $ getMd b) of-                  (left,right) -> [ Right $ b { b_cigar = toCigar  left }            `setMD` left-                                  , Left  $ b { b_cigar = toCigar right, b_pos = 0 } `setMD` right ]---- | Split an 'ECig' into two at some position.  The position is counted--- in terms of the reference (therefore, deletions count, insertions--- don't).  The parts that would be skipped if we were splitting lists--- are replaced by soft masks.-split_ecig :: Int -> ECig -> (ECig, ECig)-split_ecig _    WithMD = (WithMD,       WithMD)-split_ecig _ WithoutMD = (WithoutMD, WithoutMD)-split_ecig 0       ecs = (mask_all ecs,    ecs)--split_ecig i (Ins' n ecs) = case split_ecig i ecs of (u,v) -> (Ins' n u, SMa' n v)-split_ecig i (SMa' n ecs) = case split_ecig i ecs of (u,v) -> (SMa' n u, SMa' n v)-split_ecig i (HMa' n ecs) = case split_ecig i ecs of (u,v) -> (HMa' n u, HMa' n v)-split_ecig i (Pad' n ecs) = case split_ecig i ecs of (u,v) -> (Pad' n u,        v)--split_ecig i (Mat' n ecs)-    | i >= n    = case split_ecig (i-n) ecs of (u,v) -> (Mat' n u, SMa' n v)-    | otherwise = (Mat' i $ SMa' (n-i) $ mask_all ecs, SMa' i $ Mat' (n-i) ecs)--split_ecig i (Rep' x ecs) = case split_ecig (i-1) ecs of (u,v) -> (Rep' x u, SMa' 1 v)-split_ecig i (Del' x ecs) = case split_ecig (i-1) ecs of (u,v) -> (Del' x u,        v)--split_ecig i (Nop' n ecs)-    | i >= n    = case split_ecig (i-n) ecs of (u,v) -> (Nop' n u,        v)-    | otherwise = (Nop' i $ mask_all ecs, Nop' (n-i) ecs)--mask_all :: ECig -> ECig-mask_all      WithMD = WithMD-mask_all   WithoutMD = WithoutMD-mask_all (Nop' _ ec) =          mask_all ec-mask_all (HMa' _ ec) =          mask_all ec-mask_all (Pad' _ ec) =          mask_all ec-mask_all (Del' _ ec) =          mask_all ec-mask_all (Rep' _ ec) = SMa' 1 $ mask_all ec-mask_all (Mat' n ec) = SMa' n $ mask_all ec-mask_all (Ins' n ec) = SMa' n $ mask_all ec-mask_all (SMa' n ec) = SMa' n $ mask_all ec---- | Extended CIGAR.  This subsumes both the CIGAR string and the--- optional MD field.  If we have MD on input, we generate it on output,--- too.  And in between, we break everything into /very small/--- operations.--data ECig = WithMD                      -- terminate, do generate MD field-          | WithoutMD                   -- terminate, don't bother with MD-          | Mat' Int ECig-          | Rep' Nucleotides ECig-          | Ins' Int ECig-          | Del' Nucleotides ECig-          | Nop' Int ECig-          | SMa' Int ECig-          | HMa' Int ECig-          | Pad' Int ECig---toECig :: VS.Vector Cigar -> [MdOp] -> ECig-toECig = go . VS.toList-  where-    go        cs  (MdNum  0:mds) = go cs mds-    go        cs  (MdDel []:mds) = go cs mds-    go (_:*0 :cs)           mds  = go cs mds-    go [        ] [            ] = WithMD               -- all was fine to the very end-    go [        ]              _ = WithoutMD            -- here it wasn't fine--    go (Mat :* n : cs) (MdRep x:mds)   = Rep'   x   $ go     (Mat :* (n-1) : cs)             mds-    go (Mat :* n : cs) (MdNum m:mds)-       | n < m                         = Mat'   n   $ go                     cs (MdNum (m-n):mds)-       | n > m                         = Mat'   m   $ go     (Mat :* (n-m) : cs)             mds-       | otherwise                     = Mat'   n   $ go                     cs              mds-    go (Mat :* n : cs)            _    = Mat'   n   $ go'                    cs--    go (Ins :* n : cs)               mds  = Ins'   n   $ go                  cs              mds-    go (Del :* n : cs) (MdDel (x:xs):mds) = Del'   x   $ go  (Del :* (n-1) : cs) (MdDel xs:mds)-    go (Del :* n : cs)                 _  = Del' nucsN $ go' (Del :* (n-1) : cs)--    go (Nop :* n : cs) mds = Nop' n $ go cs mds-    go (SMa :* n : cs) mds = SMa' n $ go cs mds-    go (HMa :* n : cs) mds = HMa' n $ go cs mds-    go (Pad :* n : cs) mds = Pad' n $ go cs mds--    -- We jump here once the MD fiels ran out early or was messed up.-    -- We no longer bother with it (this also happens if the MD isn't-    -- present to begin with).-    go' (_ :* 0 : cs)   = go' cs-    go' [           ]   = WithoutMD                        -- we didn't have MD or it was broken--    go' (Mat :* n : cs) = Mat'   n   $ go'                 cs-    go' (Ins :* n : cs) = Ins'   n   $ go'                 cs-    go' (Del :* n : cs) = Del' nucsN $ go' (Del :* (n-1) : cs)--    go' (Nop :* n : cs) = Nop'   n   $ go' cs-    go' (SMa :* n : cs) = SMa'   n   $ go' cs-    go' (HMa :* n : cs) = HMa'   n   $ go' cs-    go' (Pad :* n : cs) = Pad'   n   $ go' cs----- We normalize matches, deletions and soft masks, because these are the--- operations we generate.  Everything else is either already normalized--- or nobody really cares anyway.-toCigar :: ECig -> VS.Vector Cigar-toCigar = V.fromList . go-  where-    go       WithMD = []-    go    WithoutMD = []--    go (Ins' n ecs) = Ins :* n : go ecs-    go (Nop' n ecs) = Nop :* n : go ecs-    go (HMa' n ecs) = HMa :* n : go ecs-    go (Pad' n ecs) = Pad :* n : go ecs-    go (SMa' n ecs) = go_sma n ecs-    go (Mat' n ecs) = go_mat n ecs-    go (Rep' _ ecs) = go_mat 1 ecs-    go (Del' _ ecs) = go_del 1 ecs--    go_sma !n (SMa' m ecs) = go_sma (n+m) ecs-    go_sma !n         ecs  = SMa :* n : go ecs--    go_mat !n (Mat' m ecs) = go_mat (n+m) ecs-    go_mat !n (Rep' _ ecs) = go_mat (n+1) ecs-    go_mat !n         ecs  = Mat :* n : go ecs--    go_del !n (Del' _ ecs) = go_del (n+1) ecs-    go_del !n         ecs  = Del :* n : go ecs------ | Create an MD field from an extended CIGAR and place it in a record.--- We build it piecemeal (in 'go'), call out to 'addNum', 'addRep',--- 'addDel' to make sure the operations are not generated in a--- degenerate manner, and finally check if we're even supposed to create--- an MD field.-setMD :: BamRec -> ECig -> BamRec-setMD b ec = case go ec of-    Just md -> b { b_exts = updateE "MD" (Text $ showMd md) (b_exts b) }-    Nothing -> b { b_exts = deleteE "MD"                    (b_exts b) }-  where-    go  WithMD      = Just []-    go  WithoutMD   = Nothing--    go (Ins' _ ecs) = go ecs-    go (Nop' _ ecs) = go ecs-    go (SMa' _ ecs) = go ecs-    go (HMa' _ ecs) = go ecs-    go (Pad' _ ecs) = go ecs-    go (Mat' n ecs) = (if n ==  0 then id else fmap (addNum n)) $ go ecs-    go (Rep' x ecs) = (if isGap x then id else fmap (addRep x)) $ go ecs-    go (Del' x ecs) = (if isGap x then id else fmap (addDel x)) $ go ecs--    addNum n (MdNum m : mds) = MdNum (n+m) : mds-    addNum n            mds  = MdNum   n   : mds--    addRep x            mds  = MdRep   x   : mds--    addDel x (MdDel y : mds) = MdDel (x:y) : mds-    addDel x            mds  = MdDel  [x]  : mds
− src/Bio/Bam/Trim.hs
@@ -1,441 +0,0 @@--- | Trimming of reads as found in BAM files.  Implements trimming low--- quality sequence from the 3' end.--module Bio.Bam.Trim-        ( trim_3-        , trim_3'-        , trim_low_quality-        , default_fwd_adapters-        , default_rev_adapters-        , find_merge-        , mergeBam-        , find_trim-        , trimBam-        , mergeTrimBam-        , twoMins-        , merged_seq-        , merged_qual-        ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Bam.Rmdup        ( ECig(..), setMD, toECig )-import Bio.Iteratee-import Bio.Prelude--import Foreign.C.Types      ( CInt(..) )--import qualified Data.ByteString                        as B-import qualified Data.Vector.Generic                    as V-import qualified Data.Vector.Storable                   as W---- | Trims from the 3' end of a sequence.--- @trim_3' p b@ trims the 3' end of the sequence in @b@ at the--- earliest position such that @p@ evaluates to true on every suffix--- that was trimmed off.  Note that the 3' end may be the beginning of--- the sequence if it happens to be stored in reverse-complemented form.--- Also note that trimming from the 3' end may not make sense for reads--- that were constructed by merging paired end data (but we cannot take--- care of that here).  Further note that trimming may break dependent--- information, notably the "mate" information of the mate and many--- optional fields.--trim_3' :: ([Nucleotides] -> [Qual] -> Bool) -> BamRec -> BamRec-trim_3' p b | b_flag b `testBit` 4 = trim_rev-            | otherwise            = trim_fwd-  where-    trim_fwd = let l = subtract 1 . fromIntegral . length . takeWhile (uncurry p) $-                            zip (inits . reverse . V.toList $ b_seq b)-                                (inits . reverse . V.toList $ b_qual b)-               in trim_3 l b--    trim_rev = let l = subtract 1 . fromIntegral . length . takeWhile (uncurry p) $-                            zip (inits . V.toList $ b_seq  b)-                                (inits . V.toList $ b_qual b)-               in trim_3 l b--trim_3 :: Int -> BamRec -> BamRec-trim_3 l b | b_flag b `testBit` 4 = trim_rev-           | otherwise            = trim_fwd-  where-    trim_fwd = let (_, cigar') = trim_back_cigar (b_cigar b) l-                   c = modMd (takeECig (V.length (b_seq  b) - l)) b-               in c { b_seq   = V.take (V.length (b_seq  c) - l) (b_seq  c)-                    , b_qual  = V.take (V.length (b_qual c) - l) (b_qual c)-                    , b_cigar = cigar'-                    , b_exts  = map (\(k,e) -> case e of-                                        Text t | k `elem` trim_set-                                          -> (k, Text (B.take (B.length t - l) t))-                                        _ -> (k,e)-                                    ) (b_exts c) }--    trim_rev = let (off, cigar') = trim_fwd_cigar (b_cigar b) l-                   c = modMd (dropECig l) b-               in c { b_seq   = V.drop l (b_seq  c)-                    , b_qual  = V.drop l (b_qual c)-                    , b_pos   = b_pos c + off-                    , b_cigar = cigar'-                    , b_exts  = map (\(k,e) -> case e of-                                        Text t | k `elem` trim_set-                                          -> (k, Text (B.drop l t))-                                        _ -> (k,e)-                                    ) (b_exts c) }--    trim_set = ["BQ","CQ","CS","E2","OQ","U2"]--    modMd :: (ECig -> ECig) -> BamRec -> BamRec-    modMd f br = maybe br (setMD br . f . toECig (b_cigar br)) (getMd br)--    endOf :: ECig -> ECig-    endOf  WithMD     = WithMD-    endOf  WithoutMD  = WithoutMD-    endOf (Mat' _ es) = endOf es-    endOf (Ins' _ es) = endOf es-    endOf (SMa' _ es) = endOf es-    endOf (Rep' _ es) = endOf es-    endOf (Del' _ es) = endOf es-    endOf (Nop' _ es) = endOf es-    endOf (HMa' _ es) = endOf es-    endOf (Pad' _ es) = endOf es--    takeECig :: Int -> ECig -> ECig-    takeECig 0  es          = endOf es-    takeECig _  WithMD      = WithMD-    takeECig _  WithoutMD   = WithoutMD-    takeECig n (Mat' m  es) = Mat' n  $ if n > m then takeECig (n-m) es else WithMD-    takeECig n (Ins' m  es) = Ins' n  $ if n > m then takeECig (n-m) es else WithMD-    takeECig n (SMa' m  es) = SMa' n  $ if n > m then takeECig (n-m) es else WithMD-    takeECig n (Rep' ns es) = Rep' ns $ takeECig (n-1) es-    takeECig n (Del' ns es) = Del' ns $ takeECig n es-    takeECig n (Nop' m  es) = Nop' m  $ takeECig n es-    takeECig n (HMa' m  es) = HMa' m  $ takeECig n es-    takeECig n (Pad' m  es) = Pad' m  $ takeECig n es--    dropECig :: Int -> ECig -> ECig-    dropECig 0  es         = es-    dropECig _  WithMD     = WithMD-    dropECig _  WithoutMD  = WithoutMD-    dropECig n (Mat' m es) = if n > m then dropECig (n-m) es else Mat' n WithMD-    dropECig n (Ins' m es) = if n > m then dropECig (n-m) es else Ins' n WithMD-    dropECig n (SMa' m es) = if n > m then dropECig (n-m) es else SMa' n WithMD-    dropECig n (Rep' _ es) = dropECig (n-1) es-    dropECig n (Del' _ es) = dropECig n es-    dropECig n (Nop' _ es) = dropECig n es-    dropECig n (HMa' _ es) = dropECig n es-    dropECig n (Pad' _ es) = dropECig n es---trim_back_cigar, trim_fwd_cigar :: V.Vector v Cigar => v Cigar -> Int -> ( Int, v Cigar )-trim_back_cigar c l = (o, V.fromList $ reverse c') where (o,c') = sanitize_cigar . trim_cigar l $ reverse $ V.toList c-trim_fwd_cigar  c l = (o, V.fromList           c') where (o,c') = sanitize_cigar $ trim_cigar l $ V.toList c--sanitize_cigar :: (Int, [Cigar]) -> (Int, [Cigar])-sanitize_cigar (o, [        ])                          = (o, [])-sanitize_cigar (o, (op:*l):xs) | op == Pad              = sanitize_cigar (o,xs)         -- del P-                               | op == Del || op == Nop = sanitize_cigar (o + l, xs)    -- adjust D,N-                               | op == Ins              = (o, (SMa :* l):xs)            -- I --> S-                               | otherwise              = (o, (op :* l):xs)             -- rest is fine--trim_cigar :: Int -> [Cigar] -> (Int, [Cigar])-trim_cigar 0 cs = (0, cs)-trim_cigar _ [] = (0, [])-trim_cigar l ((op:*ll):cs) | bad_op op = let (o,cs') = trim_cigar l cs in (o + reflen op ll, cs')-                           | otherwise = case l `compare` ll of-    LT -> (reflen op  l, (op :* (ll-l)):cs)-    EQ -> (reflen op ll,                cs)-    GT -> let (o,cs') = trim_cigar (l - ll) cs in (o + reflen op ll, cs')--  where-    reflen op' = if ref_op op' then id else const 0-    bad_op o = o /= Mat && o /= Ins && o /= SMa-    ref_op o = o == Mat || o == Del----- | Trim predicate to get rid of low quality sequence.--- @trim_low_quality q ns qs@ evaluates to true if all qualities in @qs@--- are smaller (i.e. worse) than @q@.-trim_low_quality :: Qual -> a -> [Qual] -> Bool-trim_low_quality q = const $ all (< q)----- | Finds the merge point.  Input is list of forward adapters, list of--- reverse adapters, sequence1, quality1, sequence2, quality2; output is--- merge point and two qualities (YM, YN).-find_merge :: [W.Vector Nucleotides] -> [W.Vector Nucleotides]-           -> W.Vector Nucleotides -> W.Vector Qual-           -> W.Vector Nucleotides -> W.Vector Qual-           -> (Int, Int, Int)-find_merge ads1 ads2 r1 q1 r2 q2 = (mlen, score2 - score1, plain_score - score1)-  where-    plain_score = 6 * fromIntegral (V.length r1 + V.length r2)-    (score1, mlen, score2) = twoMins plain_score (V.length r1 + V.length r2) $-                             merge_score ads1 ads2 r1 q1 r2 q2---- | Overlap-merging of read pairs.  We shall compute the likelihood--- for every possible overlap, then select the most likely one (unless it--- looks completely random), compute a quality from the second best--- merge, then merge and clamp the quality accordingly.--- (We could try looking for chimaera after completing the merge, if--- only we knew which ones to expect?)------ Two reads go in, with two adapter lists.  We return 'Nothing' if all--- merges looked mostly random.  Else we return the two original reads,--- flagged as 'eflagVestigial' *and* the merged version, flagged as--- 'eflagMerged' and optionally 'eflagTrimmed'.  All reads contain the--- computed qualities (in YM and YN), which we also return.------ The merging automatically limits quality scores some of the time.  We--- additionally impose a hard limit of 63 to avoid difficulties--- representing the result, and even that is ridiculous.  Sane people--- would further limit the returned quality!  (In practice, map quality--- later imposes a limit anyway, so no worries...)--mergeBam :: Int -> Int -> [W.Vector Nucleotides] -> [W.Vector Nucleotides] -> BamRec -> BamRec -> [BamRec]-mergeBam lowq highq ads1 ads2 r1 r2-    | V.null (b_seq r1) && V.null (b_seq r2) = [              ]-    | qual1 < lowq || mlen < 0               = [ r1', r2'     ]-    | qual1 >= highq && mlen == 0            = [              ]-    | qual1 >= highq                         = [           rm ]-    | mlen < len_r1-20 || mlen < len_r2-20   = [           rm ]-    | otherwise         = map flag_alternative [ r1', r2', rm ]-  where-    len_r1    = V.length  $ b_seq  r1-    len_r2    = V.length  $ b_seq  r2--    b_seq_r1  = V.convert $ b_seq  r1-    b_seq_r2  = V.convert $ b_seq  r2-    b_qual_r1 = V.convert $ b_qual r1-    b_qual_r2 = V.convert $ b_qual r2--    (mlen, qual1, qual2) = find_merge ads1 ads2 b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2--    flag_alternative br = br { b_exts = updateE "FF" (Int $ extAsInt 0 "FF" br .|. eflagAlternative) $ b_exts br }-    store_quals      br = br { b_exts = updateE "YM" (Int qual1) $ updateE "YN" (Int qual2) $ b_exts br }-    pair_flags = flagPaired.|.flagProperlyPaired.|.flagMateUnmapped.|.flagMateReversed.|.flagFirstMate.|.flagSecondMate--    r1' = store_quals r1-    r2' = store_quals r2-    rm  = store_quals $ merged_read mlen (fromIntegral $ min 63 qual1)--    merged_read l qmax = nullBamRec {-                b_qname = b_qname r1,-                b_flag  = flagUnmapped .|. complement pair_flags .&. b_flag r1,-                b_seq   = V.convert $ merged_seq l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2,-                b_qual  = V.convert $ merged_qual qmax l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2,-                b_exts  = let ff = if l < len_r1 then eflagTrimmed else 0-                          in updateE "FF" (Int $ extAsInt 0 "FF" r1 .|. eflagMerged .|. ff) $ b_exts r1 }--{-# INLINE merged_seq #-}-merged_seq :: (V.Vector v Nucleotides, V.Vector v Qual)-           => Int -> v Nucleotides -> v Qual -> v Nucleotides -> v Qual -> v Nucleotides-merged_seq l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2 = V.concat-        [ V.take (l - len_r2) b_seq_r1-        , V.zipWith4 zz          (V.take l $ V.drop (l - len_r2) b_seq_r1)-                                 (V.take l $ V.drop (l - len_r2) b_qual_r1)-                     (V.reverse $ V.take l $ V.drop (l - len_r1) b_seq_r2)-                     (V.reverse $ V.take l $ V.drop (l - len_r1) b_qual_r2)-        , V.reverse $ V.take (l - len_r1) b_seq_r2 ]-  where-    len_r1 = V.length b_qual_r1-    len_r2 = V.length b_qual_r2-    zz !n1 (Q !q1) !n2 (Q !q2) | n1 == compls n2 =        n1-                               | q1 > q2         =        n1-                               | otherwise       = compls n2--{-# INLINE merged_qual #-}-merged_qual :: (V.Vector v Nucleotides, V.Vector v Qual)-            => Word8 -> Int -> v Nucleotides -> v Qual -> v Nucleotides -> v Qual -> v Qual-merged_qual qmax l b_seq_r1 b_qual_r1 b_seq_r2 b_qual_r2 = V.concat-        [ V.take (l - len_r2) b_qual_r1-        , V.zipWith4 zz           (V.take l $ V.drop (l - len_r2) b_seq_r1)-                                  (V.take l $ V.drop (l - len_r2) b_qual_r1)-                      (V.reverse $ V.take l $ V.drop (l - len_r1) b_seq_r2)-                      (V.reverse $ V.take l $ V.drop (l - len_r1) b_qual_r2)-        , V.reverse $ V.take (l - len_r1) b_qual_r2 ]-  where-    len_r1 = V.length b_qual_r1-    len_r2 = V.length b_qual_r2-    zz !n1 (Q !q1) !n2 (Q !q2) | n1 == compls n2 = Q $ min qmax (q1 + q2)-                               | q1 > q2         = Q $           q1 - q2-                               | otherwise       = Q $           q2 - q1------ | Finds the trimming point.  Input is list of forward adapters,--- sequence, quality; output is trim point and two qualities (YM, YN).-find_trim :: [W.Vector Nucleotides]-          -> W.Vector Nucleotides -> W.Vector Qual-          -> (Int, Int, Int)-find_trim ads1 r1 q1 = (mlen, score2 - score1, plain_score - score1)-  where-    plain_score = 6 * fromIntegral (V.length r1)-    (score1, mlen, score2) = twoMins plain_score (V.length r1) $-                             merge_score ads1 [V.empty] r1 q1 V.empty V.empty---- | Trimming for a single read:  we need one adapter only (the one coming--- /after/ the read), here provided as a list of options, and then we--- merge with an empty second read.  Results in up to two reads (the--- original, possibly flagged, and the trimmed one, definitely flagged,--- and two qualities).-trimBam :: Int -> Int -> [W.Vector Nucleotides] -> BamRec -> [BamRec]-trimBam lowq highq ads1 r1-    | V.null (b_seq r1)              = [          ]-    | mlen == 0 && qual1 >= highq    = [          ]-    | qual1 < lowq || mlen < 0       = [ r1'      ]-    | qual1 >= highq                 = [      r1t ]-    | otherwise = map flag_alternative [ r1', r1t ]-  where-    -- the "merge" score if there is no trimming--    b_seq_r1 = V.convert $ b_seq r1-    b_qual_r1 = V.convert $ b_qual r1--    (mlen, qual1, qual2) = find_trim ads1 b_seq_r1 b_qual_r1--    flag_alternative br = br { b_exts = updateE "FF" (Int $ extAsInt 0 "FF" br .|. eflagAlternative) $ b_exts br }-    store_quals      br = br { b_exts = updateE "YM" (Int qual1) $ updateE "YN" (Int qual2) $ b_exts br }--    r1'  = store_quals r1-    r1t  = store_quals $ trimmed_read mlen--    trimmed_read l = nullBamRec {-            b_qname = b_qname r1,-            b_flag  = flagUnmapped .|. b_flag r1,-            b_seq   = V.take l $ b_seq  r1,-            b_qual  = V.take l $ b_qual r1,-            b_exts  = updateE "FF" (Int $ extAsInt 0 "FF" r1 .|. eflagTrimmed) $ b_exts r1 }----- | For merging, we don't need the complete adapters (length around 70!),--- only a sufficient prefix.  Taking only the more-or-less constant--- part (length around 30), there aren't all that many different--- adapters in the world.  To deal with pretty much every library, we--- only need the following forward adapters, which will be the default--- (defined here in the direction they would be sequenced in):  Genomic--- R2, Multiplex R2, Fraft P7.--default_fwd_adapters :: [ W.Vector Nucleotides ]-default_fwd_adapters = map (W.fromList. map toNucleotides)-         [ {- Genomic R2   -}  "AGATCGGAAGAGCGGTTCAG"-         , {- Multiplex R2 -}  "AGATCGGAAGAGCACACGTC"-         , {- Graft P7     -}  "AGATCGGAAGAGCTCGTATG" ]---- | Like 'default_rev_adapters', these are the few adapters needed for--- the reverse read (defined in the direction they would be sequenced in--- as part of the second read):  Genomic R1, CL 72.--default_rev_adapters :: [ W.Vector Nucleotides ]-default_rev_adapters = map (W.fromList. map toNucleotides)-         [ {- Genomic_R1   -}  "AGATCGGAAGAGCGTCGTGT"-         , {- CL72         -}  "GGAAGAGCGTCGTGTAGGGA" ]---- We need to compute the likelihood of a read pair given an assumed--- insert length.  The likelihood of the first read is the likelihood of--- a match with the adapter where it overlaps the 3' adapter, elsewhere--- it's 1/4 per position.  The likelihood of the second read is the--- likelihood of a match with the adapter where it overlaps the adapter,--- the likehood of a read-read match where it overlaps read one, 1/4 per--- position elsewhere.  (Yes, this ignores base composition.  It doesn't--- matter enough.)--merge_score-    :: [ W.Vector Nucleotides ]                 -- 3' adapters as they appear in the first read-    -> [ W.Vector Nucleotides ]                 -- 5' adapters as they appear in the second read-    -> W.Vector Nucleotides -> W.Vector Qual    -- first read, qual-    -> W.Vector Nucleotides -> W.Vector Qual    -- second read, qual-    -> Int                                      -- assumed insert length-    -> Int                                      -- score (roughly sum of qualities at mismatches)-merge_score fwd_adapters rev_adapters !read1 !qual1 !read2 !qual2 !l-    =   6 * fromIntegral (l `min` V.length read1)                                           -- read1, part before adapter-      + 6 * fromIntegral (max 0 (l - V.length read1))                                       -- read2, part before overlap--      + foldl' (\acc fwd_ad -> min acc-                    (match_adapter l read1 qual1 fwd_ad +                                   -- read1, match with forward adapter-                     6 * fromIntegral (max 0 (V.length read1 - V.length fwd_ad - l)))       -- read1, part after (known) adapter-               ) maxBound fwd_adapters--      + foldl' (\acc rev_ad -> min acc-                    (match_adapter l read2 qual2 rev_ad +                                   -- read2, match with reverse adapter-                     6 * fromIntegral (max 0 (V.length read2 - V.length rev_ad - l)))       -- read2, part after (known) adapter-               ) maxBound rev_adapters--      + match_reads l read1 qual1 read2 qual2--{-# INLINE match_adapter #-}-match_adapter :: Int -> W.Vector Nucleotides -> W.Vector Qual -> W.Vector Nucleotides -> Int-match_adapter !off !rd !qs !ad-    | V.length rd /= V.length qs = error "read/qual length mismatch"-    | efflength <= 0             = 0-    | otherwise-        = fromIntegral . unsafePerformIO $-          W.unsafeWith rd $ \p_rd ->-          W.unsafeWith qs $ \p_qs ->-          W.unsafeWith ad $ \p_ad ->-          prim_match_ad (fromIntegral off)-                        (fromIntegral efflength)-                        p_rd p_qs p_ad-  where-    !efflength =  (V.length rd - off) `min` V.length ad--foreign import ccall unsafe "prim_match_ad"-    prim_match_ad :: CInt -> CInt-                  -> Ptr Nucleotides -> Ptr Qual-                  -> Ptr Nucleotides -> IO CInt----- | Computes overlap score for two reads (with qualities) assuming an--- insert length.-{-# INLINE match_reads #-}-match_reads :: Int -> W.Vector Nucleotides -> W.Vector Qual -> W.Vector Nucleotides -> W.Vector Qual -> Int-match_reads !l !rd1 !qs1 !rd2 !qs2-    | V.length rd1 /= V.length qs1 || V.length rd2 /= V.length qs2 = error "read/qual length mismatch"-    | efflength <= 0                                               = 0-    | otherwise-        = fromIntegral . unsafePerformIO $-          W.unsafeWith rd1 $ \p_rd1 ->-          W.unsafeWith qs1 $ \p_qs1 ->-          W.unsafeWith rd2 $ \p_rd2 ->-          W.unsafeWith qs2 $ \p_qs2 ->-          prim_match_reads (fromIntegral minidx1)-                           (fromIntegral maxidx2)-                           (fromIntegral efflength)-                           p_rd1 p_qs1 p_rd2 p_qs2-  where-    -- vec1, forward-    !minidx1 = (l - V.length rd2) `max` 0-    -- vec2, backward-    !maxidx2 = l `min` V.length rd2-    -- effective length-    !efflength = ((V.length rd1 + V.length rd2 - l) `min` l) `max` 0---foreign import ccall unsafe "prim_match_reads"-    prim_match_reads :: CInt -> CInt -> CInt-                     -> Ptr Nucleotides -> Ptr Qual-                     -> Ptr Nucleotides -> Ptr Qual -> IO CInt---{-# INLINE twoMins #-}-twoMins :: (Bounded a, Ord a) => a -> Int -> (Int -> a) -> (a,Int,a)-twoMins a0 imax f = go a0 (-1) maxBound 0 0-  where-    go !m1 !i1 !m2 !i2 !i-        | i == imax = (m1,i1,m2)-        | otherwise =-            case f i of-                x | x < m1    -> go  x  i m1 i1 (i+1)-                  | x < m2    -> go m1 i1  x  i (i+1)-                  | otherwise -> go m1 i1 m2 i2 (i+1)---mergeTrimBam :: Monad m => Int -> Int -> [W.Vector Nucleotides] -> [W.Vector Nucleotides] -> Enumeratee [BamRec] [BamRec] m a-mergeTrimBam lowq highq fwd_ads rev_ads = convStream go-  where-    go = do r1 <- headStream-            if isPaired r1-              then tryHead >>= go2 r1-              else return $ trimBam lowq highq fwd_ads r1--    go2 r1  Nothing  = error $ "Lone mate found: " ++ show (b_qname r1)-    go2 r1 (Just r2) = return $ mergeBam lowq highq fwd_ads rev_ads r1 r2-
− src/Bio/Bam/Writer.hs
@@ -1,218 +0,0 @@--- | Printers for BAM and SAM.  BAM is properly supported, SAM can be--- piped to standard output.--module Bio.Bam.Writer (-    IsBamRec(..),-    encodeBamWith,--    packBam,-    writeBamFile,-    writeBamHandle,-    pipeBamOutput,-    pipeSamOutput-                      ) where--import Bio.Bam.Header-import Bio.Bam.Rec-import Bio.Iteratee-import Bio.Iteratee.Builder-import Bio.Prelude--import Data.ByteString.Builder      ( hPutBuilder, Builder, toLazyByteString )-import Data.ByteString.Internal     ( ByteString(..) )-import Data.ByteString.Lazy         ( foldrChunks )-import Foreign.Marshal.Alloc        ( alloca )-import System.IO                    ( openBinaryFile, IOMode(..) )--import qualified Data.ByteString                    as B-import qualified Data.ByteString.Char8              as S-import qualified Data.Vector.Generic                as V-import qualified Data.Vector.Storable               as VS-import qualified Data.Vector.Unboxed                as U-import qualified Data.Sequence                      as Z---- | write in SAM format to stdout--- This is useful for piping to other tools (say, AWK scripts) or for--- debugging.  No convenience function to send SAM to a file exists,--- because that's a stupid idea.-pipeSamOutput :: MonadIO m => BamMeta -> Iteratee [BamRec] m ()-pipeSamOutput meta = do liftIO . hPutBuilder stdout $ showBamMeta meta-                        mapStreamM_ $ \b -> liftIO . putStr $ encodeSamEntry (meta_refs meta) b "\n"--encodeSamEntry :: Refs -> BamRec -> String -> String-encodeSamEntry refs b = conjoin '\t' [-    unpck (b_qname b),-    shows (b_flag b .&. 0xffff),-    unpck (sq_name $ getRef refs $ b_rname b),-    shows (b_pos b + 1),-    shows (unQ $ b_mapq b),-    V.foldr ((.) . shows) id (b_cigar b),-    if isValidRefseq (b_mrnm b) && b_mrnm b == b_rname b-      then (:) '=' else unpck (sq_name $ getRef refs $ b_mrnm b),-    shows (b_mpos b + 1),-    shows (b_isize b),-    shows (V.toList $ b_seq b),-    (++)  (V.toList . V.map (chr . (+33) . fromIntegral . unQ) $ b_qual b) ] .-    foldr (\(k,v) f -> (:) '\t' . shows k . (:) ':' . extToSam v . f) id (b_exts b)-  where-    unpck = (++) . S.unpack-    conjoin c = foldr1 (\a f -> a . (:) c . f)--    extToSam (Int      i) = (:) 'i' . (:) ':' . shows i-    extToSam (Float    f) = (:) 'f' . (:) ':' . shows f-    extToSam (Text     t) = (:) 'Z' . (:) ':' . unpck t-    extToSam (Bin      x) = (:) 'H' . (:) ':' . tohex x-    extToSam (Char     c) = (:) 'A' . (:) ':' . (:) (w2c c)-    extToSam (IntArr   a) = (:) 'B' . (:) ':' . (:) 'i' . sarr a-    extToSam (FloatArr a) = (:) 'B' . (:) ':' . (:) 'f' . sarr a--    tohex = B.foldr (\c f -> w2d (c `shiftR` 4) . w2d (c .&. 0xf) . f) id-    w2d = (:) . S.index "0123456789ABCDEF" . fromIntegral-    sarr v = conjoin ',' . map shows $ U.toList v--class IsBamRec a where-    pushBam :: a -> BgzfTokens -> BgzfTokens--instance IsBamRec BamRaw where-    {-# INLINE pushBam #-}-    pushBam = pushBamRaw--instance IsBamRec BamRec where-    {-# INLINE pushBam #-}-    pushBam = pushBamRec--instance (IsBamRec a, IsBamRec b) => IsBamRec (Either a b) where-    {-# INLINE pushBam #-}-    pushBam = either pushBam pushBam---- | Encodes BAM records straight into a dynamic buffer, the BGZF's it.--- Should be fairly direct and perform well.-{-# INLINE encodeBamWith #-}-encodeBamWith :: (MonadIO m, IsBamRec r) => Int -> BamMeta -> Enumeratee [r] S.ByteString m ()-encodeBamWith lv meta = eneeBam ><> encodeBgzf lv-  where-    eneeBam  = eneeCheckIfDone (\k -> mapChunks (foldMap (Endo . pushBam)) . k $ Chunk pushHeader)--    pushHeader :: Endo BgzfTokens-    pushHeader = Endo $ TkString "BAM\1"-                      . TkSetMark                        -- the length byte-                      . pushBuilder (showBamMeta meta)-                      . TkEndRecord                      -- fills the length in-                      . TkWord32 (fromIntegral . Z.length $ meta_refs meta)-                      . appEndo (foldMap (Endo . pushRef) (meta_refs meta))--    pushRef :: BamSQ -> BgzfTokens -> BgzfTokens-    pushRef bs = TkWord32 (fromIntegral $ B.length (sq_name bs) + 1)-               . TkString (sq_name bs)-               . TkWord8 0-               . TkWord32 (fromIntegral $ sq_length bs)--    pushBuilder :: Builder -> BgzfTokens -> BgzfTokens-    pushBuilder b tk = foldrChunks TkString tk (toLazyByteString b)--{-# INLINE pushBamRaw #-}-pushBamRaw :: BamRaw -> BgzfTokens -> BgzfTokens-pushBamRaw r = TkWord32 (fromIntegral $ B.length $ raw_data r) .-               TkString (raw_data r)---- | Writes BAM encoded stuff to a file.-writeBamFile :: IsBamRec r => FilePath -> BamMeta -> Iteratee [r] IO ()-writeBamFile fp meta =-    bracketIO (openBinaryFile fp WriteMode)-              (hClose)-              (flip writeBamHandle meta)---- | write BAM encoded stuff to stdout--- This send uncompressed BAM to stdout.  Useful for piping to other--- tools.-pipeBamOutput :: IsBamRec r => BamMeta -> Iteratee [r] IO ()-pipeBamOutput meta = encodeBamWith 0 meta =$ mapChunksM_ (liftIO . S.hPut stdout)---- | Writes BAM encoded stuff to a 'Handle'.-writeBamHandle :: (MonadIO m, IsBamRec r) => Handle -> BamMeta -> Iteratee [r] m ()-writeBamHandle hdl meta = encodeBamWith 6 meta =$ mapChunksM_ (liftIO . S.hPut hdl)--{-# RULES-    "pushBam/unpackBam"     forall b . pushBamRec (unpackBam b) = pushBamRaw b-  #-}--{-# INLINE[1] pushBamRec #-}-pushBamRec :: BamRec -> BgzfTokens -> BgzfTokens-pushBamRec BamRec{..} =-      TkSetMark-    . TkWord32 (unRefseq b_rname)-    . TkWord32 (fromIntegral b_pos)-    . TkWord8  (fromIntegral $ B.length b_qname + 1)-    . TkWord8  (unQ b_mapq)-    . TkWord16 (fromIntegral bin)-    . TkWord16 (fromIntegral $ VS.length b_cigar)-    . TkWord16 (fromIntegral b_flag)-    . TkWord32 (fromIntegral $ V.length b_seq)-    . TkWord32 (unRefseq b_mrnm)-    . TkWord32 (fromIntegral b_mpos)-    . TkWord32 (fromIntegral b_isize)-    . TkString b_qname-    . TkWord8 0-    . VS.foldr ((.) . TkWord8) id (VS.unsafeCast b_cigar :: VS.Vector Word8)-    . pushSeq b_seq-    . VS.foldr ((.) . TkWord8 . unQ) id b_qual-    . foldr ((.) . pushExt) id b_exts-    . TkEndRecord-  where-    bin = distinctBin b_pos (alignedLength b_cigar)--    pushSeq :: V.Vector vec Nucleotides => vec Nucleotides -> BgzfTokens -> BgzfTokens-    pushSeq v = case v V.!? 0 of-                    Nothing -> id-                    Just a  -> case v V.!? 1 of-                        Nothing -> TkWord8 (unNs a `shiftL` 4)-                        Just b  -> TkWord8 (unNs a `shiftL` 4 .|. unNs b) . pushSeq (V.drop 2 v)--    pushExt :: (BamKey, Ext) -> BgzfTokens -> BgzfTokens-    pushExt (BamKey k, e) = case e of-        Text  t -> common 'Z' . TkString t . TkWord8 0-        Bin   t -> common 'H' . TkString t . TkWord8 0-        Char  c -> common 'A' . TkWord8 c-        Float f -> common 'f' . TkWord32 (fromIntegral $ fromFloat f)--        Int i   -> case put_some_int (U.singleton i) of-                        (c,op) -> common c . op i--        IntArr  ia -> case put_some_int ia of-                        (c,op) -> common 'B' . TkWord8 (fromIntegral $ ord c)-                                  . TkWord32 (fromIntegral $ U.length ia-1)-                                  . U.foldr ((.) . op) id ia--        FloatArr fa -> common 'B' . TkWord8 (fromIntegral $ ord 'f')-                       . TkWord32 (fromIntegral $ U.length fa-1)-                       . U.foldr ((.) . TkWord32 . fromFloat) id fa-      where-        common :: Char -> BgzfTokens -> BgzfTokens-        common z = TkWord16 k . TkWord8 (fromIntegral $ ord z)--        put_some_int :: U.Vector Int -> (Char, Int -> BgzfTokens -> BgzfTokens)-        put_some_int is-            | U.all (between        0    0xff) is = ('C', TkWord8  . fromIntegral)-            | U.all (between   (-0x80)   0x7f) is = ('c', TkWord8  . fromIntegral)-            | U.all (between        0  0xffff) is = ('S', TkWord16 . fromIntegral)-            | U.all (between (-0x8000) 0x7fff) is = ('s', TkWord16 . fromIntegral)-            | U.all                      (> 0) is = ('I', TkWord32 . fromIntegral)-            | otherwise                           = ('i', TkWord32 . fromIntegral)--        between :: Int -> Int -> Int -> Bool-        between l r x = l <= x && x <= r--        fromFloat :: Float -> Word32-        fromFloat float = unsafeDupablePerformIO $ alloca $ \buf ->-                          pokeByteOff buf 0 float >> peek buf--packBam :: BamRec -> IO BamRaw-packBam br = do bb <- newBuffer 1000-                (bb', TkEnd) <- store_loop bb (pushBamRec br TkEnd)-                return . bamRaw 0 $ PS (buffer bb') 4 (used bb' - 4)-  where-    store_loop bb tk = do (bb',tk') <- fillBuffer bb tk-                          case tk' of TkEnd -> return (bb',tk')-                                      _     -> do bb'' <- expandBuffer (128*1024) bb'-                                                  store_loop bb'' tk'-
− src/Bio/Base.hs
@@ -1,369 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies, CPP #-}-{-# LANGUAGE ExistentialQuantification, TemplateHaskell    #-}--- | Common data types used everywhere.  This module is a collection of--- very basic "bioinformatics" data types that are simple, but don't--- make sense to define over and over.--module Bio.Base(-    Nucleotide(..), Nucleotides(..),-    Qual(..), toQual, fromQual, fromQualRaised, probToQual,-    Prob'(..), Prob, toProb, fromProb, qualToProb, pow,--    Word8,-    nucA, nucC, nucG, nucT,-    nucsA, nucsC, nucsG, nucsT, nucsN, gap,-    toNucleotide, toNucleotides, nucToNucs,-    showNucleotide, showNucleotides,-    isGap,-    isBase,-    isProperBase,-    properBases,-    compl, compls,--    Seqid,--    Position(..),-    shiftPosition,-    p_is_reverse,--    Range(..),-    shiftRange,-    reverseRange,-    extendRange,-    insideRange,-    wrapRange,--    w2c,-    c2w,--    findAuxFile-) where--import BasePrelude-#if MIN_VERSION_base(4,9,0)-                             hiding ( log1pexp, log1mexp )-#endif-import Bio.Util.Numeric             ( log1pexp, log1mexp )-import Data.ByteString.Internal     ( c2w, w2c )-import Data.Vector.Unboxed.Deriving ( derivingUnbox )-import System.Posix.Files           ( fileExist )--import qualified Data.ByteString.Char8 as S-import qualified Data.Vector.Unboxed   as U---- | A nucleotide base.  We only represent A,C,G,T.  The contained--- 'Word8' ist guaranteed to be 0..3.-newtype Nucleotide = N { unN :: Word8 } deriving ( Eq, Ord, Enum, Ix, Storable )--derivingUnbox "Nucleotide" [t| Nucleotide -> Word8 |] [| unN |] [| N |]--instance Bounded Nucleotide where-    minBound = N 0-    maxBound = N 3---- | A nucleotide base in an alignment.--- Experience says we're dealing with Ns and gaps all the type, so--- purity be damned, they are included as if they were real bases.------ To allow @Nucleotides@s to be unpacked and incorporated into--- containers, we choose to represent them the same way as the BAM file--- format:  as a 4 bit wide field.  Gaps are encoded as 0 where they--- make sense, N is 15.  The contained 'Word8' is guaranteed to be--- 0..15.--newtype Nucleotides = Ns { unNs :: Word8 } deriving ( Eq, Ord, Enum, Ix, Storable )--derivingUnbox "Nucleotides" [t| Nucleotides -> Word8 |] [| unNs |] [| Ns |]--instance Bounded Nucleotides where-    minBound = Ns  0-    maxBound = Ns 15--nucToNucs :: Nucleotide -> Nucleotides-nucToNucs (N x) = Ns $ 1 `shiftL` fromIntegral (x .&. 3)---- | Qualities are stored in deciban, also known as the Phred scale.  To--- represent a value @p@, we store @-10 * log_10 p@.  Operations work--- directly on the \"Phred\" value, as the name suggests.  The same goes--- for the 'Ord' instance:  greater quality means higher \"Phred\"--- score, meand lower error probability.--newtype Qual = Q { unQ :: Word8 } deriving ( Eq, Ord, Storable, Bounded )--derivingUnbox "Qual" [t| Qual -> Word8 |] [| unQ |] [| Q |]--instance Show Qual where-    showsPrec p (Q q) = (:) 'q' . showsPrec p q--toQual :: (Floating a, RealFrac a) => a -> Qual-toQual a = Q $ round (-10 * log a / log 10)--fromQual :: Qual -> Double-fromQual (Q q) = 10 ** (- fromIntegral q / 10)--fromQualRaised :: Double -> Qual -> Double-fromQualRaised k (Q q) = 10 ** (- k * fromIntegral q / 10)---- | A positive floating point value stored in log domain.  We store the--- natural logarithm (makes computation easier), but allow conversions--- to the familiar \"Phred\" scale used for 'Qual' values.-newtype Prob' a = Pr { unPr :: a } deriving ( Eq, Ord, Storable )---- | Common way of using 'Prob''.-type Prob = Prob' Double--derivingUnbox "Prob'" [t| forall a . U.Unbox a => Prob' a -> a |] [| unPr |] [| Pr |]--instance RealFloat a => Show (Prob' a) where-    showsPrec _ (Pr p) = (:) 'q' . showFFloat (Just 1) q-      where q = - 10 * p / log 10--instance (Floating a, Ord a) => Num (Prob' a) where-    {-# INLINE fromInteger #-}-    fromInteger a = Pr (log (fromInteger a))-    {-# INLINE (+) #-}-    Pr x + Pr y = Pr $ if x >= y then x + log1pexp (y-x) else y + log1pexp (x-y)-    {-# INLINE (-) #-}-    Pr x - Pr y = Pr $ if x >= y then x + log1mexp (y-x) else error "no negative error probabilities"-    {-# INLINE (*) #-}-    Pr a * Pr b = Pr $ a + b-    {-# INLINE negate #-}-    negate    _ = Pr $ error "no negative error probabilities"-    {-# INLINE abs #-}-    abs       x = x-    {-# INLINE signum #-}-    signum    _ = Pr 0--instance (Floating a, Fractional a, Ord a) => Fractional (Prob' a) where-    fromRational a = Pr (log (fromRational a))-    Pr a  /  Pr b = Pr (a - b)-    recip  (Pr a) = Pr (negate a)--infixr 8 `pow`-pow :: Num a => Prob' a -> a -> Prob' a-pow (Pr a) e = Pr $ a * e---toProb :: Floating a => a -> Prob' a-toProb p = Pr (log p)--fromProb :: Floating a => Prob' a -> a-fromProb (Pr q) = exp q--qualToProb :: Floating a => Qual -> Prob' a-qualToProb (Q q) = Pr (- log 10 * fromIntegral q / 10)--probToQual :: (Floating a, RealFrac a) => Prob' a -> Qual-probToQual (Pr p) = Q (round (- 10 * p / log 10))--nucA, nucC, nucG, nucT :: Nucleotide-nucA = N 0-nucC = N 1-nucG = N 2-nucT = N 3--gap, nucsA, nucsC, nucsG, nucsT, nucsN :: Nucleotides-gap   = Ns 0-nucsA = Ns 1-nucsC = Ns 2-nucsG = Ns 4-nucsT = Ns 8-nucsN = Ns 15----- | Sequence identifiers are ASCII strings--- Since we tend to store them for a while, we use strict byte strings.-type Seqid = S.ByteString---- | Coordinates in a genome.--- The position is zero-based, no questions about it.  Think of the--- position as pointing to the crack between two bases: looking forward--- you see the next base to the right, looking in the reverse direction--- you see the complement of the first base to the left.------ To encode the strand, we (virtually) reverse-complement any sequence--- and prepend it to the normal one.  That way, reversed coordinates--- have a negative sign and automatically make sense.  Position 0 could--- either be the beginning of the sequence or the end on the reverse--- strand... that ambiguity shouldn't really matter.--data Position = Pos {-        p_seq   :: {-# UNPACK #-} !Seqid,   -- ^ sequence (e.g. some chromosome)-        p_start :: {-# UNPACK #-} !Int      -- ^ offset, zero-based-    } deriving (Show, Eq, Ord)--p_is_reverse :: Position -> Bool-p_is_reverse = (< 0) . p_start---- | Ranges in genomes--- We combine a position with a length.  In 'Range pos len', 'pos' is--- always the start of a stretch of length 'len'.  Positions therefore--- move in the opposite direction on the reverse strand.  To get the--- same stretch on the reverse strand, shift r_pos by r_length, then--- reverse direction (or call reverseRange).-data Range = Range {-        r_pos    :: {-# UNPACK #-} !Position,-        r_length :: {-# UNPACK #-} !Int-    } deriving (Show, Eq, Ord)----- | Converts a character into a 'Nucleotides'.--- The usual codes for A,C,G,T and U are understood, '-' and '.' become--- gaps and everything else is an N.-toNucleotide :: Char -> Nucleotide-toNucleotide c = if ord c < 128 then N (ar `U.unsafeIndex` ord c) else N 0-  where-    ar = U.replicate 128 0 U.//-          ( [ (ord          x,  n) | (x, N n) <- pairs ] ++-            [ (ord (toUpper x), n) | (x, N n) <- pairs ] )--    pairs = [ ('a', nucA), ('c', nucC), ('g', nucG), ('t', nucT) ]----- | Converts a character into a 'Nucleotides'.--- The usual codes for A,C,G,T and U are understood, '-' and '.' become--- gaps and everything else is an N.-toNucleotides :: Char -> Nucleotides-toNucleotides c = if ord c < 128 then Ns (ar `U.unsafeIndex` ord c) else nucsN-  where-    ar = U.replicate 128 (unNs nucsN) U.//-          ( [ (ord          x,  n) | (x, Ns n) <- pairs ] ++-            [ (ord (toUpper x), n) | (x, Ns n) <- pairs ] )--    Ns a `plus` Ns b = Ns (a .|. b)--    pairs = [ ('a', nucsA), ('c', nucsC), ('g', nucsG), ('t', nucsT),-              ('u', nucsT), ('-', gap),  ('.', gap),-              ('b', nucsC `plus` nucsG `plus` nucsT),-              ('d', nucsA `plus` nucsG `plus` nucsT),-              ('h', nucsA `plus` nucsC `plus` nucsT),-              ('v', nucsA `plus` nucsC `plus` nucsG),-              ('k', nucsG `plus` nucsT),-              ('m', nucsA `plus` nucsC),-              ('s', nucsC `plus` nucsG),-              ('w', nucsA `plus` nucsT),-              ('r', nucsA `plus` nucsG),-              ('y', nucsC `plus` nucsT) ]---- | Tests if a 'Nucleotides' is a base.--- Returns 'True' for everything but gaps.-isBase :: Nucleotides -> Bool-isBase (Ns n) = n /= 0---- | Tests if a 'Nucleotides' is a proper base.--- Returns 'True' for A,C,G,T only.-isProperBase :: Nucleotides -> Bool-isProperBase x = x == nucsA || x == nucsC || x == nucsG || x == nucsT--properBases :: [ Nucleotides ]-properBases = [ nucsA, nucsC, nucsG, nucsT ]---- | Tests if a 'Nucleotides' is a gap.--- Returns true only for the gap.-isGap :: Nucleotides -> Bool-isGap x = x == gap---{-# INLINE showNucleotide #-}-showNucleotide :: Nucleotide -> Char-showNucleotide (N x) = S.index str $ fromIntegral $ x .&. 3-  where str = S.pack "ACGT"--{-# INLINE showNucleotides #-}-showNucleotides :: Nucleotides -> Char-showNucleotides (Ns x) = S.index str $ fromIntegral $ x .&. 15-  where str = S.pack "-ACMGRSVTWYHKDBN"--instance Show Nucleotide where-    show     x = [ showNucleotide x ]-    showList l = (map showNucleotide l ++)--instance Read Nucleotide where-    readsPrec _ ('a':cs) = [(nucA, cs)]-    readsPrec _ ('A':cs) = [(nucA, cs)]-    readsPrec _ ('c':cs) = [(nucC, cs)]-    readsPrec _ ('C':cs) = [(nucC, cs)]-    readsPrec _ ('g':cs) = [(nucG, cs)]-    readsPrec _ ('G':cs) = [(nucG, cs)]-    readsPrec _ ('t':cs) = [(nucT, cs)]-    readsPrec _ ('T':cs) = [(nucT, cs)]-    readsPrec _ ('u':cs) = [(nucT, cs)]-    readsPrec _ ('U':cs) = [(nucT, cs)]-    readsPrec _     _    = [          ]--    readList ('-':cs) = readList cs-    readList (c:cs) | isSpace c = readList cs-                    | otherwise = case reads (c:cs) of-                            [] -> [ ([],c:cs) ]-                            xs -> [ (n:ns,r2) | (n,r1) <- xs, (ns,r2) <- readList r1 ]-    readList [] = [([],[])]--instance Show Nucleotides where-    show     x = [ showNucleotides x ]-    showList l = (map showNucleotides l ++)--instance Read Nucleotides where-    readsPrec _ (c:cs) = [(toNucleotides c, cs)]-    readsPrec _ [    ] = []-    readList s = let (hd,tl) = span (\c -> isAlpha c || isSpace c || '-' == c) s-                 in [(map toNucleotides $ filter (not . isSpace) hd, tl)]---- | Complements a Nucleotides.-{-# INLINE compl #-}-compl :: Nucleotide -> Nucleotide-compl (N n) = N $ n `xor` 3---- | Complements a Nucleotides.-{-# INLINE compls #-}-compls :: Nucleotides -> Nucleotides-compls (Ns x) = Ns $ ar `U.unsafeIndex` fromIntegral (x .&. 15)-  where-    !ar = U.fromListN 16 [ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 ]----- | Moves a @Position@.  The position is moved forward according to the--- strand, negative indexes move backward accordingly.-shiftPosition :: Int -> Position -> Position-shiftPosition a p = p { p_start = p_start p + a }---- | Moves a @Range@.  This is just @shiftPosition@ lifted.-shiftRange :: Int -> Range -> Range-shiftRange a r = r { r_pos = shiftPosition a (r_pos r) }---- | Reverses a 'Range' to give the same @Range@ on the opposite strand.-reverseRange :: Range -> Range-reverseRange (Range (Pos sq pos) len) = Range (Pos sq (-pos-len)) len---- | Extends a range.  The length of the range is simply increased.-extendRange :: Int -> Range -> Range-extendRange a r = r { r_length = r_length r + a }---- | Expands a subrange.--- @(range1 `insideRange` range2)@ interprets @range1@ as a subrange of--- @range2@ and computes its absolute coordinates.  The sequence name of--- @range1@ is ignored.-insideRange :: Range -> Range -> Range-insideRange r1@(Range (Pos _ start1) length1) r2@(Range (Pos sq start2) length2)-    | start2 < 0         = reverseRange (insideRange r1 (reverseRange r2))-    | start1 <= length2  = Range (Pos sq (start2 + start1)) (min length1 (length2 - start1))-    | otherwise          = Range (Pos sq (start2 + length2)) 0----- | Wraps a range to a region.  This simply normalizes the start--- position to be in the interval '[0,n)', which only makes sense if the--- @Range@ is to be mapped onto a circular genome.  This works on both--- strands and the strand information is retained.-wrapRange :: Int -> Range -> Range-wrapRange n (Range (Pos sq s) l) = Range (Pos sq (s `mod` n)) l---- | Finds a file by searching the environment variable BIOHAZARD like a--- PATH.-findAuxFile :: FilePath -> IO FilePath-findAuxFile fn | "/" `isPrefixOf` fn = return fn-               | otherwise = go . fromMaybe "." . lookup "BIOHAZARD" =<< getEnvironment-  where-    go "" = return fn-    go pp = let (p,ps) = break (==':') pp-            in fileExist (p ++ "/" ++ fn) >>=-               bool (return $ p ++ "/" ++ fn) (go $ drop 1 ps)-
− src/Bio/Iteratee.hs
@@ -1,318 +0,0 @@-module Bio.Iteratee (-    iGetString,-    iterLoop,-    iLookAhead,--    protectTerm,-    parMapChunksIO,-    parRunIO,-    progressGen,-    progressNum,-    progressPos,--    ($==),-    MonadIO, MonadMask,-    lift, liftIO,-    stdin, stdout, stderr,--    enumAuxFile,-    enumInputs,-    enumDefaultInputs,--    Ordering'(..),-    mergeSortStreams,--    Enumerator',-    Enumeratee',-    mergeEnums',--    QQ(..),-    emptyQ,-    lengthQ,-    pushQ,-    popQ,-    cancelAll,--    ParseError(..),-    parserToIteratee,-    stream2vector,-    stream2vectorN,--    Fd,-    withFileFd,--    module Bio.Iteratee.Bytes,-    module Bio.Iteratee.IO,-    module Bio.Iteratee.Iteratee,-    module Bio.Iteratee.List-        ) where--import Bio.Bam.Header-import Bio.Iteratee.Base-import Bio.Iteratee.Bytes-import Bio.Iteratee.IO-import Bio.Iteratee.Iteratee-import Bio.Iteratee.List-import Bio.Prelude-import Bio.Util.Numeric                     ( showNum )-import Control.Concurrent.Async             ( Async, async, wait, cancel )-import Control.Monad.Catch                  ( MonadMask(..) )-import Control.Monad.IO.Class               ( MonadIO(..) )-import Control.Monad.Trans.Class            ( MonadTrans(..) )-import System.IO                            ( hIsTerminalDevice )--import qualified Control.Monad.Catch            as CMC-import qualified Data.Attoparsec.ByteString     as A-import qualified Data.ByteString.Char8          as S-import qualified Data.Vector.Generic            as VG-import qualified Data.Vector.Generic.Mutable    as VM---- | Run an Iteratee, collect the input.  When it finishes, return the--- result along with *all* input.  Effectively allows lookahead.  Be--- careful, this will eat memory if the @Iteratee@ doesn't return--- speedily.-iLookAhead :: Monoid s => Iteratee s m a -> Iteratee s m a-iLookAhead = go mempty-  where-    go acc it = Iteratee $ \od oc -> runIter it (\x _ -> od x (Chunk acc)) (oc . step acc)--    step acc k c@(Chunk str) = go (acc `mappend` str) (k c)-    step acc k c@(EOF     _) = Iteratee $ \od1 -> runIter (k c) (\x _ -> od1 x (Chunk acc))----- | Collects a string of a given length.  Don't use this for long--- strings, use 'takeStream' instead.-iGetString :: Int -> Iteratee S.ByteString m S.ByteString-iGetString 0 = idone S.empty (Chunk S.empty)-iGetString n = liftI $ step [] 0-  where-    step acc l c@(EOF _) = icont (step acc l) (Just $ setEOF c)-    step acc l (Chunk c) | l + S.length c >= n = let r = S.concat . reverse $ S.take (n-l) c : acc-                                                 in idone r (Chunk $ S.drop (n-l) c)-                         | otherwise           = liftI $ step (c:acc) (l + S.length c)---- | Repeatedly apply an 'Iteratee' to a value until end of stream.--- Returns the final value.-iterLoop :: (Nullable s, Monad m) => (a -> Iteratee s m a) -> a -> Iteratee s m a-iterLoop it a = do e <- isFinished-                   if e then return a-                        else it a >>= iterLoop it-infixl 1 $==-{-# INLINE ($==) #-}--- | Compose an 'Enumerator'' with an 'Enumeratee', giving a new--- 'Enumerator''.-($==) :: Monad m => Enumerator' hdr input m (Iteratee output m result)-                 -> Enumeratee      input             output m result-                 -> Enumerator' hdr                   output m result-($==) enum enee iter = run =<< enum (enee . iter)---- | Merge two 'Enumerator''s into one.  The header provided by the--- inner 'Enumerator'' is passed to the output iterator, the header--- provided by the outer 'Enumerator'' is passed to the merging iteratee--{-# INLINE mergeEnums' #-}-mergeEnums' :: (Nullable s2, Nullable s1, Monad m)-            => Enumerator' hi s1 m a                            -- ^ inner enumerator-            -> Enumerator' ho s2 (Iteratee s1 m) a              -- ^ outer enumerator-            -> (ho -> Enumeratee  s2 s1 (Iteratee s1 m) a)      -- ^ merging enumeratee-            -> Enumerator' hi s1 m a-mergeEnums' e1 e2 etee i = e1 $ \hi -> e2 (\ho -> joinI . etee ho $ ilift lift (i hi)) >>= run--type Enumerator' h eo m b = (h -> Iteratee eo m b) -> m (Iteratee eo m b)-type Enumeratee' h ei eo m b = (h -> Iteratee eo m b) -> Iteratee ei m (Iteratee eo m b)--enumAuxFile :: MonadBracketIO m => FilePath -> Iteratee S.ByteString m a -> m a-enumAuxFile fp it = liftIO (findAuxFile fp) >>= \f -> enumFile defaultBufSize f it >>= run--enumDefaultInputs :: MonadBracketIO m => Enumerator S.ByteString m a-enumDefaultInputs it0 = liftIO getArgs >>= flip enumInputs it0--enumInputs :: MonadBracketIO m => [FilePath] -> Enumerator S.ByteString m a-enumInputs [] = enumFd defaultBufSize stdInput-enumInputs xs = go xs-  where go ("-":fs) = enumFd defaultBufSize stdInput >=> go fs-        go ( f :fs) = enumFile defaultBufSize f >=> go fs-        go [      ] = return--data Ordering' a = Less | Equal a | NotLess--mergeSortStreams :: Monad m => (a -> a -> Ordering' a) -> Enumeratee [a] [a] (Iteratee [a] m) b-mergeSortStreams comp = eneeCheckIfDone step-  where-    step out = peekStream >>= \mx -> lift peekStream >>= \my -> case (mx, my) of-        (Just x, Just y) -> case x `comp` y of-            Less    -> do dropStream 1 ;                       eneeCheckIfDone step . out $ Chunk [x]-            NotLess -> do                lift (dropStream 1) ; eneeCheckIfDone step . out $ Chunk [y]-            Equal z -> do dropStream 1 ; lift (dropStream 1) ; eneeCheckIfDone step . out $ Chunk [z]--        (Just  x, Nothing) -> do       dropStream 1  ; eneeCheckIfDone step . out $ Chunk [x]-        (Nothing, Just  y) -> do lift (dropStream 1) ; eneeCheckIfDone step . out $ Chunk [y]-        (Nothing, Nothing) -> idone (liftI out) $ EOF Nothing----- | Parallel map of an IO action over the elements of a stream------ This 'Enumeratee' applies an 'IO' action to every chunk of the input--- stream.  These 'IO' actions are run asynchronously in a limited--- parallel way.  Don't forget to `evaluate`--parMapChunksIO :: (MonadIO m, Nullable s) => Int -> (s -> IO t) -> Enumeratee s t m a-parMapChunksIO np f = eneeCheckIfDonePass (go emptyQ)-  where-    -- check if the queue is full-    go !qq k (Just e) = cancelAll qq >> icont (go' emptyQ k) (Just e)-    go !qq k Nothing = case popQ qq of-        Just (a,qq') | lengthQ qq == np -> liftIO (wait a) >>= eneeCheckIfDonePass (go qq') . k . Chunk-        _                               -> liftI $ go' qq k--    -- we have room for input-    go' !qq k (EOF  mx) = do a <- liftIO (async (f emptyP))-                             goE mx (pushQ a qq) k Nothing-    go' !qq k (Chunk c) = do a <- liftIO (async (f c))-                             go (pushQ a qq) k Nothing--    -- input ended, empty the queue-    goE  _ !qq k (Just e) = cancelAll qq >> icont (go' emptyQ k) (Just e)-    goE mx !qq k Nothing = case popQ qq of-        Nothing      -> idone (liftI k) (EOF mx)-        Just (a,qq') -> liftIO (wait a) >>= eneeCheckIfDonePass (goE mx qq') . k . Chunk--parRunIO :: MonadIO m => Int -> Enumeratee [IO a] a m b-parRunIO np = eneeCheckIfDonePass (go emptyQ)-  where-    -- check if the queue is full-    go !qq k (Just  e) = cancelAll qq >> icont (go' emptyQ k) (Just e)-    go !qq k  Nothing  = case popQ qq of-        Just (a,qq') | lengthQ qq == np -> liftIO (wait a) >>= eneeCheckIfDonePass (go qq') . k . Chunk-        _                               -> liftI $ go' qq k--    -- we have room for input-    go' !qq k (Chunk (c:cs)) = liftIO (async c) >>= \a -> go' (pushQ a qq) k (Chunk cs)-    go' !qq k (Chunk [    ]) = go qq k Nothing-    go' !qq k (EOF       mx) = goE mx qq k Nothing--    -- input ended, empty the queue-    goE  _ !qq k (Just e) = cancelAll qq >> icont (go' emptyQ k) (Just e)-    goE mx !qq k Nothing = case popQ qq of-        Nothing      -> idone (liftI k) (EOF mx)-        Just (a,qq') -> liftIO (wait a) >>= eneeCheckIfDonePass (goE mx qq') . k . Chunk---- | Protects the terminal from binary junk.  If @i@ is an 'Iteratee'--- that might write binary to 'stdout', then @protectTerm i@ is the same--- 'Iteratee', but it will abort if 'stdout' is a terminal device.-protectTerm :: (Nullable s, MonadIO m) => Iteratee s m a -> Iteratee s m a-protectTerm itr = do-    t <- liftIO $ hIsTerminalDevice stdout-    if t then err else itr-  where-    err = error "cowardly refusing to write binary data to terminal"---- | A general progress indicator that prints some message after a set--- number of records have passed through.-progressGen :: MonadIO m-            => (Int -> a -> String) -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b-progressGen msg sz put = eneeCheckIfDonePass (icont . go 0)-  where-    go !_ k (EOF   mx) = idone (liftI k) (EOF mx)-    go !n k (Chunk as)-        | null as    = liftI $ go n k-        | otherwise  = let !n' = n + length as-                       in when (n' `div` sz /= n `div` sz) (liftIO . put $-                                "\27[K" ++ msg n' (head as) ++ "\r")-                          `ioBind_` eneeCheckIfDonePass (icont . go n') (k $ Chunk as)---- | A simple progress indicator that prints the number of records.-progressNum :: MonadIO m-            => String -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b-progressNum msg = progressGen (\n _ -> msg ++ " " ++ showNum n)---- | A simple progress indicator that prints a position every set number--- of passed records.-progressPos :: MonadIO m-            => (a -> (Refseq, Int)) -> String -> Refs-            -> Int -> (String -> IO ()) -> Enumeratee [a] [a] m b-progressPos f msg refs =-    progressGen $ \_ a -> let (!rs1, !po1) = f a-                              !nm = unpack . sq_name $ getRef refs rs1-                          in msg ++ " " ++ nm ++ ":" ++ showNum po1---- A very simple queue data type.--- Invariants: q = QQ l f b --> l == length f + length b---                          --> l == 0 || not (null f)--data QQ a = QQ !Int [a] [a]--emptyQ :: QQ a-emptyQ = QQ 0 [] []--lengthQ :: QQ a -> Int-lengthQ (QQ l _ _) = l--pushQ :: a -> QQ a -> QQ a-pushQ a (QQ l [] b) = QQ (l+1) (reverse (a:b)) []-pushQ a (QQ l  f b) = QQ (l+1) f (a:b)--popQ :: QQ a -> Maybe (a, QQ a)-popQ (QQ _ [    ] _) = Nothing-popQ (QQ l [ a  ] b) = Just (a, QQ (l-1) (reverse b) [])-popQ (QQ l (a:fs) b) = Just (a, QQ (l-1) fs b)--cancelAll :: MonadIO m => QQ (Async a) -> m ()-cancelAll (QQ _ ff bb) = liftIO $ mapM_ cancel (ff ++ bb)--data ParseError = ParseError {errorContexts :: [String], errorMessage :: String}-    deriving (Show, Typeable)--instance Exception ParseError---- | A function to convert attoparsec 'Parser's into 'Iteratee's.-parserToIteratee :: A.Parser a -> Iteratee S.ByteString m a-parserToIteratee p = icont (f (A.parse p)) Nothing-  where-    f k (EOF Nothing) =-        case A.feed (k S.empty) S.empty of-          A.Fail _ err dsc            -> throwErr (toException $ ParseError err dsc)-          A.Partial _                 -> throwErr (toException EofException)-          A.Done rest v | S.null rest -> idone v (EOF Nothing)-                           | otherwise   -> idone v (Chunk rest)-    f _ (EOF (Just e)) = throwErr e-    f k (Chunk s)-        | S.null s = icont (f k) Nothing-        | otherwise =-            case k s of-              A.Fail _ err dsc -> throwErr (toException $ ParseError err dsc)-              A.Partial k'     -> icont (f k') Nothing-              A.Done rest v    -> idone v (Chunk rest)----- | Equivalent to @joinI $ takeStream n $ stream2vector@, but more--- efficient.-stream2vectorN :: (MonadIO m, VG.Vector v a) => Int -> Iteratee [a] m (v a)-stream2vectorN n = do-    mv <- liftIO $ VM.new n-    l <- go mv 0-    liftIO $ VG.unsafeFreeze $ VM.take l mv-  where-    go mv i-        | i == n    = return n-        | otherwise =-            tryHead >>= \case-                Nothing -> return i-                Just  a -> liftIO (VM.write mv i a) >> go mv (i+1)---- | Reads the whole stream into a 'VG.Vector'.-stream2vector :: (MonadIO m, VG.Vector v a) => Iteratee [a] m (v a)-stream2vector = liftIO (VM.new 1024) >>= go 0-  where-    go !i !mv = tryHead >>= \case-                  Nothing -> liftIO $ VG.unsafeFreeze $ VM.take i mv-                  Just  a -> do mv' <- if VM.length mv == i then liftIO (VM.grow mv (VM.length mv)) else return mv-                                when (i `rem` 0x10000 == 0) $ liftIO performGC-                                liftIO $ VM.write mv' i a-                                go (i+1) mv'--withFileFd :: (MonadIO m, MonadMask m) => FilePath -> (Fd -> m a) -> m a-withFileFd filepath = CMC.bracket-    (liftIO $ openFd filepath ReadOnly Nothing defaultFileFlags)-    (liftIO . closeFd)-
− src/Bio/Iteratee/Base.hs
@@ -1,242 +0,0 @@-{-# LANGUAGE Rank2Types #-}---- |Monadic Iteratees:--- incremental input parsers, processors and transformers--module Bio.Iteratee.Base (-  -- * Types-  Stream (..)-  ,StreamStatus (..)-  -- ** Exception types-  ,module Bio.Iteratee.Exception-  -- ** Iteratees-  ,Iteratee (..)-  -- * Functions-  -- ** Control functions-  ,run-  ,tryRun-  ,ilift-  ,ifold-  -- ** Creating Iteratees-  ,idone-  ,icont-  ,liftI-  ,idoneM-  ,icontM-  -- ** Stream Functions-  ,setEOF-  -- * Classes-  ,NullPoint(..)-  ,Nullable(..)-)-where--import Bio.Iteratee.Exception-import Bio.Prelude--import Control.Monad.Catch as CIO-import Control.Monad.IO.Class ( MonadIO(..) )-import Control.Monad.Trans.Class ( MonadTrans(..) )--import qualified Control.Exception    as E-import qualified Data.ByteString      as B-import qualified Data.ByteString.Lazy as L---- | NullPoint class.  Containers that have a null representation,--- corresponding to Data.Monoid.mempty.-class NullPoint c where emptyP :: c--instance NullPoint     (Endo a) where emptyP = Endo id-instance NullPoint          [a] where emptyP = []-instance NullPoint B.ByteString where emptyP = B.empty-instance NullPoint L.ByteString where emptyP = L.empty---- | Nullable container class-class NullPoint c => Nullable c where nullC :: c -> Bool--instance Nullable          [a] where nullC [] = True ; nullC _  = False-instance Nullable B.ByteString where nullC    = B.null-instance Nullable L.ByteString where nullC    = L.null---- |A stream is a (continuing) sequence of elements bundled in Chunks.--- The first variant indicates termination of the stream.--- Chunk a gives the currently available part of the stream.--- The stream is not terminated yet.--- The case (null Chunk) signifies a stream with no currently available--- data but which is still continuing. A stream processor should,--- informally speaking, ``suspend itself'' and wait for more data--- to arrive.--data Stream c = EOF (Maybe SomeException) | Chunk c-  deriving (Show, Typeable)--instance (Eq c) => Eq (Stream c) where-  (Chunk c1) == (Chunk c2)           = c1 == c2-  (EOF Nothing) == (EOF Nothing)     = True-  (EOF (Just e1)) == (EOF (Just e2)) = typeOf e1 == typeOf e2-  _ == _                             = False--instance Semigroup c => Semigroup (Stream c) where-  EOF mErr <>        _ = EOF mErr-  _        <> EOF mErr = EOF mErr-  Chunk s1 <> Chunk s2 = Chunk (s1 <> s2)--instance Monoid c => Monoid (Stream c) where-  mempty                      = Chunk mempty-  EOF mErr `mappend`        _ = EOF mErr-  _        `mappend` EOF mErr = EOF mErr-  Chunk s1 `mappend` Chunk s2 = Chunk (mappend s1 s2)---- |Map a function over a stream.-instance Functor Stream where-  fmap f (Chunk xs) = Chunk $ f xs-  fmap _ (EOF mErr) = EOF mErr---- |Describe the status of a stream of data.-data StreamStatus =-  DataRemaining-  | EofNoError-  | EofError SomeException-  deriving (Show, Typeable)---- ------------------------------------------------- create exception type hierarchy---- |Produce the 'EOF' error message.  If the stream was terminated because--- of an error, keep the error message.-setEOF :: Stream c -> SomeException-setEOF (EOF (Just e)) = e-setEOF _              = toException EofException---- ------------------------------------------------- | Monadic iteratee-newtype Iteratee s m a = Iteratee{ runIter :: forall r.-          (a -> Stream s -> m r) ->-          ((Stream s -> Iteratee s m a) -> Maybe SomeException -> m r) ->-          m r}---- ------------------------------------------------idone :: a -> Stream s -> Iteratee s m a-idone a s = Iteratee $ \onDone _ -> onDone a s--icont :: (Stream s -> Iteratee s m a) -> Maybe SomeException -> Iteratee s m a-icont k e = Iteratee $ \_ onCont -> onCont k e--liftI :: (Stream s -> Iteratee s m a) -> Iteratee s m a-liftI k = Iteratee $ \_ onCont -> onCont k Nothing---- Monadic versions, frequently used by enumerators-idoneM :: Monad m => a -> Stream s -> m (Iteratee s m a)-idoneM x str = return $ Iteratee $ \onDone _ -> onDone x str--icontM-  :: Monad m =>-     (Stream s -> Iteratee s m a)-     -> Maybe SomeException-     -> m (Iteratee s m a)-icontM k e = return $ Iteratee $ \_ onCont -> onCont k e--instance (Functor m) => Functor (Iteratee s m) where-  fmap f m = Iteratee $ \onDone onCont ->-    let od = onDone . f-        oc = onCont . (fmap f .)-    in runIter m od oc--instance (Functor m, Monad m, Nullable s) => Applicative (Iteratee s m) where-    pure x  = idone x (Chunk emptyP)-    {-# INLINE (<*>) #-}-    m <*> a = m >>= flip fmap a--instance (Monad m, Nullable s) => Monad (Iteratee s m) where-  {-# INLINE return #-}-  return x = Iteratee $ \onDone _ -> onDone x (Chunk emptyP)-  {-# INLINE (>>=) #-}-  (>>=) = bindIteratee--{-# INLINE bindIteratee #-}-bindIteratee :: Nullable s-    => Iteratee s m a-    -> (a -> Iteratee s m b)-    -> Iteratee s m b-bindIteratee m f = Iteratee $ \onDone onCont ->-    let m_done a (Chunk s)-          | nullC s     = runIter (f a) onDone onCont-        m_done a stream = runIter (f a) (const . flip onDone stream) f_cont-          where f_cont k Nothing = runIter (k stream) onDone onCont-                f_cont k e       = onCont k e-    in runIter m m_done (onCont . (flip bindIteratee f .))---instance NullPoint s => MonadTrans (Iteratee s) where-    lift m = Iteratee $ \onDone _ -> m >>= flip onDone (Chunk emptyP)--instance (MonadIO m, Nullable s, NullPoint s) => MonadIO (Iteratee s m) where-    liftIO = lift . liftIO--instance (MonadThrow m, Nullable s, NullPoint s) => MonadThrow (Iteratee s m) where-    throwM e    = lift $ CIO.throwM e--instance (MonadCatch m, Nullable s, NullPoint s) => MonadCatch (Iteratee s m) where-    m `catch` f = Iteratee $ \od oc -> runIter m od oc `CIO.catch` (\e -> runIter (f e) od oc)---- |Send 'EOF' to the @Iteratee@ and disregard the unconsumed part of the--- stream.  If the iteratee is in an exception state, that exception is--- thrown with 'Control.Exception.throw'.  Iteratees that do not terminate--- on @EOF@ will throw 'EofException'.-run :: Monad m => Iteratee s m a -> m a-run iter = runIter iter onDone onCont- where-   onDone  x _        = return x-   onCont  k Nothing  = runIter (k (EOF Nothing)) onDone onCont'-   onCont  _ (Just e) = E.throw e-   onCont' _ Nothing  = E.throw EofException-   onCont' _ (Just e) = E.throw e---- |Run an iteratee, returning either the result or the iteratee exception.--- Note that only internal iteratee exceptions will be returned; exceptions--- thrown with @Control.Exception.throw@ or @Control.Monad.CatchIO.throw@ will--- not be returned.------ See 'Data.Iteratee.Exception.IFException' for details.-tryRun :: (Exception e, Monad m) => Iteratee s m a -> m (Either e a)-tryRun iter = runIter iter onDone onCont-  where-    onDone  x _ = return $ Right x-    onCont  k Nothing  = runIter (k (EOF Nothing)) onDone onCont'-    onCont  _ (Just e) = return $ maybeExc e-    onCont' _ Nothing  = return $ maybeExc (toException EofException)-    onCont' _ (Just e) = return $ maybeExc e-    maybeExc e = maybe (Left (E.throw e)) Left (fromException e)---- | Lift a computation in the inner monad of an iteratee.------ A simple use would be to lift a logger iteratee to a monad stack.------ > logger :: Iteratee String IO ()--- > logger = mapChunksM_ putStrLn--- >--- > loggerG :: MonadIO m => Iteratee String m ()--- > loggerG = ilift liftIO logger------ A more complex example would involve lifting an iteratee to work with--- interleaved streams.  See the example at 'Data.Iteratee.ListLike.merge'.-ilift ::-  (Monad m, Monad n)-  => (forall r. m r -> n r)-  -> Iteratee s m a-  -> Iteratee s n a-ilift f i = Iteratee $  \od oc ->-  let onDone a str  = return $ Left (a,str)-      onCont k mErr = return $ Right (ilift f . k, mErr)-  in f (runIter i onDone onCont) >>= either (uncurry od) (uncurry oc)---- | Lift a computation in the inner monad of an iteratee, while threading--- through an accumulator.-ifold :: (Monad m, Monad n) => (forall r. m r -> acc -> n (r, acc))-      -> acc -> Iteratee s m a -> Iteratee s n (a, acc)-ifold f acc i = Iteratee $ \ od oc -> do-  (r, acc') <- flip f acc $-    runIter i (curry $ return . Left) (curry $ return . Right)-  either (uncurry (od . flip (,) acc'))-         (uncurry (oc . (ifold f acc .))) r
− src/Bio/Iteratee/Bgzf.hsc
@@ -1,491 +0,0 @@--- | Handling of BGZF files.  Right now, we have an Enumeratee each for--- input and output.  The input iteratee can optionally supply virtual--- file offsets, so that seeking is possible.--module Bio.Iteratee.Bgzf (-    Block(..), decompressBgzfBlocks', decompressBgzfBlocks,-    decompressBgzf, decompressPlain,-    maxBlockSize, bgzfEofMarker, liftBlock, getOffset,-    BgzfChunk(..), isBgzf, isGzip, parMapChunksIO,-    compressBgzf, compressBgzfLv, compressBgzf', CompressParams(..),-    compressChunk-                     ) where--import Bio.Iteratee-import Bio.Prelude-import Control.Concurrent.Async             ( async, wait )-import Foreign.C.String                     ( withCAString )-import Foreign.C.Types                      ( CInt(..), CChar(..), CUInt(..), CULong(..) )-import Foreign.Marshal.Alloc                ( mallocBytes, free, allocaBytes )--import qualified Data.ByteString            as S-import qualified Data.ByteString.Unsafe     as S--#include <zlib.h>---- | One BGZF block: virtual offset and contents.  Could also be a block--- of an uncompressed file, if we want to support indexing of--- uncompressed BAM or some silliness like that.-data Block = Block { block_offset   :: {-# UNPACK #-} !FileOffset-                   , block_contents :: {-# UNPACK #-} !Bytes }--instance NullPoint Block where emptyP = Block 0 S.empty-instance Nullable  Block where nullC  = S.null . block_contents--instance Monoid Block where-    mempty = Block 0 S.empty-    mappend = (<>)-    mconcat [] = Block 0 S.empty-    mconcat bs@(Block x _:_) = Block x $ S.concat [s|Block _ s <- bs]--instance Semigroup Block where-    Block x s <> Block _ t = Block x (s `S.append` t)-    sconcat (Block x b :| bs) = Block x . sconcat $ b :| map block_contents bs----- | "Decompresses" a plain file.  What's actually happening is that the--- offset in the input stream is tracked and added to the @Bytes@s--- giving @Block@s.  This results in the same interface as decompressing--- actual Bgzf.-decompressPlain :: MonadIO m => Enumeratee Bytes Block m a-decompressPlain = eneeCheckIfDone (liftI . step 0)-  where-    step !o it (Chunk s) = eneeCheckIfDone (liftI . step (o + fromIntegral (S.length s))) . it $ Chunk (Block o s)-    step  _ it (EOF  mx) = idone (liftI it) (EOF mx)---- | Decompress a BGZF stream into a stream of 'Bytes's.-decompressBgzf :: MonadIO m => Enumeratee Bytes Bytes m a-decompressBgzf = decompressBgzfBlocks ><> mapChunks block_contents--decompressBgzfBlocks :: MonadIO m => Enumeratee Bytes Block m a-decompressBgzfBlocks out =  do-    np <- liftIO $ getNumCapabilities-    decompressBgzfBlocks' np out---- | Decompress a BGZF stream into a stream of 'Block's, 'np' fold parallel.-decompressBgzfBlocks' :: MonadIO m => Int -> Enumeratee Bytes Block m a-decompressBgzfBlocks' np = eneeCheckIfDonePass (go 0 emptyQ)-  where-    -- check if the queue is full-    go !off !qq k (Just e) = handleSeek off qq k e-    go !off !qq k Nothing = case popQ qq of-        Just (a, qq') | lengthQ qq == np -> liftIO (wait a) >>= eneeCheckIfDonePass (go off qq') . k . Chunk-        _                                -> liftI $ go' off qq k--    -- we have room for input, so try and get a compressed block-    go' !_   !qq k (EOF  mx) = goE mx qq k Nothing-    go' !off !qq k (Chunk c)-        | S.null  c = liftI $ go' off qq k-        | otherwise = joinIM $ enumPure1Chunk c $ do-                                  (off', op) <- get_bgzf_block off-                                  a <- liftIO (async op)-                                  go off' (pushQ a qq) k Nothing--    -- input ended, empty the queue-    goE  _ !qq k (Just e) = handleSeek 0 qq k e-    goE mx !qq k Nothing = case popQ qq of-        Nothing      -> idone (liftI k) (EOF mx)-        Just (a,qq') -> liftIO (wait a) >>= eneeCheckIfDonePass (goE mx qq') . k . Chunk--    handleSeek !off !qq k e = case fromException e of-        Nothing                -> throwRecoverableErr e $ go' off qq k-        Just (SeekException o) -> do-            cancelAll qq-            seek $ o `shiftR` 16-            eneeCheckIfDonePass (go (o `shiftR` 16) emptyQ) $ do-                block'drop . fromIntegral $ o .&. 0xffff-                liftI k--    block'drop sz = liftI $ \s -> case s of-        EOF _ -> throwErr $ setEOF s-        Chunk (Block p c)-            | S.length c < sz -> block'drop (sz - S.length c)-            | otherwise       -> let b' = Block (p + fromIntegral sz) (S.drop sz c)-                                 in idone () (Chunk b')--get_bgzf_block :: MonadIO m => FileOffset -> Iteratee Bytes m (FileOffset, IO Block)-get_bgzf_block off = do !(csize,xlen) <- get_bgzf_header-                        !comp  <- get_block . fromIntegral $ csize - xlen - 19-                        !crc   <- endianRead4 LSB-                        !isize <- endianRead4 LSB--                        let !off' = off + fromIntegral csize + 1-                            op    = decompress1 (off `shiftL` 16) comp crc (fromIntegral isize)-                        return (off',op)-  where-    -- Get a block of a prescribed size.  Comes back as a list of chunks.-    get_block sz = liftI $ \s -> case s of-        EOF _ -> throwErr $ setEOF s-        Chunk c | S.length c < sz -> (:) c `liftM` get_block (sz - S.length c)-                | otherwise       -> idone [S.take sz c] (Chunk (S.drop sz c))----- | Decodes a BGZF block header and returns the block size if--- successful.-get_bgzf_header :: Monad m => Iteratee Bytes m (Word16, Word16)-get_bgzf_header = do x   <- headStreamBS-                     y   <- headStreamBS-                     _cm <- headStreamBS-                     flg <- headStreamBS-                     if flg `testBit` 2 then do-                         dropStreamBS 6-                         xlen <- endianRead2 LSB-                         it <- takeStreamBS (fromIntegral xlen) get_bsize >>= lift . tryRun-                         case it of Left e -> throwErr e-                                    Right s | x == 31 && y == 139 -> return (s,xlen)-                                    _ -> throwErr $ iterStrExc "No BGZF"-                      else throwErr $ iterStrExc "No BGZF"-  where-    get_bsize = do i1 <- headStreamBS-                   i2 <- headStreamBS-                   len <- endianRead2 LSB-                   if i1 == 66 && i2 == 67 && len == 2-                      then endianRead2 LSB-                      else dropStreamBS (fromIntegral len) >> get_bsize---- | Tests whether a stream is in BGZF format.  Does not consume any--- input.-isBgzf :: Monad m => Iteratee Bytes m Bool-isBgzf = liftM isRight $ checkErr $ iLookAhead $ get_bgzf_header---- | Tests whether a stream is in GZip format.  Also returns @True@ on a--- Bgzf stream, which is technically a special case of GZip.-isGzip :: Monad m => Iteratee Bytes m Bool-isGzip = liftM (either (const False) id) $ checkErr $ iLookAhead $ test-  where-    test = do x   <- headStreamBS-              y   <- headStreamBS-              dropStreamBS 24-              b <- isFinished-              return $ not b && x == 31 && y == 139---- ------------------------------------------------------------------------- Output---- | Maximum block size for Bgzf: 64k with some room for headers and--- uncompressible stuff-maxBlockSize :: Int-maxBlockSize = 65450----- | The EOF marker for BGZF files.--- This is just an empty string compressed as BGZF.  Appended to BAM--- files to indicate their end.-bgzfEofMarker :: Bytes-bgzfEofMarker = "\x1f\x8b\x8\x4\0\0\0\0\0\xff\x6\0\x42\x43\x2\0\x1b\0\x3\0\0\0\0\0\0\0\0\0"---- | Decompress a collection of strings into a single BGZF block.------ Ideally, we receive one decode chunk from a BGZF file, decompress it,--- and return it, in the process attaching the virtual address.  But we--- might actually get more than one chunk, depending on the internals of--- the @Iteratee@s used.  If so, we concatenate them; the first gets to--- assign the address.------ Now allocate space for uncompressed data, decompress the chunks we--- got, compute crc for each and check it, finally convert to 'Bytes'--- and emit.------ We could probably get away with @unsafePerformIO@'ing everything in--- here, but then again, we only do this when we're writing output--- anyway.  Hence, run in IO.---decompress1 :: FileOffset -> [Bytes] -> Word32 -> Int -> IO Block-decompress1 off ss crc usize =-    allocaBytes (#{const sizeof(z_stream)}) $ \stream -> do-    buf <- mallocBytes usize--    #{poke z_stream, msg}       stream nullPtr-    #{poke z_stream, zalloc}    stream nullPtr-    #{poke z_stream, zfree}     stream nullPtr-    #{poke z_stream, opaque}    stream nullPtr-    #{poke z_stream, next_in}   stream nullPtr-    #{poke z_stream, next_out}  stream buf-    #{poke z_stream, avail_in}  stream (0 :: CUInt)-    #{poke z_stream, avail_out} stream (fromIntegral usize :: CUInt)--    z_check "inflateInit2" =<< c_inflateInit2 stream (-15)--    -- loop over the fragments, forward order-    forM_ ss $ \s -> case fromIntegral $ S.length s of-            l | l > 0 -> S.unsafeUseAsCString s $ \p -> do-                #{poke z_stream, next_in} stream p-                #{poke z_stream, avail_in} stream (l :: CUInt)-                z_check "inflate" =<< c_inflate stream #{const Z_NO_FLUSH}-            _ -> return ()--    z_check "inflate" =<< c_inflate stream #{const Z_FINISH}-    z_check "inflateEnd" =<< c_inflateEnd stream--    pe <- #{peek z_stream, next_out} stream-    when (pe `minusPtr` buf /= usize) $ error "size mismatch after deflate()"--    crc0 <- c_crc32 0 nullPtr 0-    crc' <- c_crc32 crc0 buf (fromIntegral usize)-    when (fromIntegral crc /= crc') $ error "CRC error after deflate()"--    Block off `liftM` S.unsafePackCStringFinalizer (castPtr buf) usize (free buf)----- | Compress a collection of strings into a single BGZF block.------ Okay, performance was lacking... let's do it again, in a more direct--- style.  We build our block manually.  First check if the compressed--- data is going to fit---if not, that's a bug.  Then alloc a buffer,--- fill with a dummy header, alloc a ZStream, compress the pieces we--- were handed one at a time.  Calculate CRC32, finalize header,--- construct a byte string, return it.------ We could probably get away with @unsafePerformIO@'ing everything in--- here, but then again, we only do this when we're writing output--- anyway.  Hence, run in IO.--compress1 :: Int -> [Bytes] -> IO Bytes-compress1 _lv [] = return bgzfEofMarker-compress1 lv ss0 =-    allocaBytes (#{const sizeof(z_stream)}) $ \stream -> do--    let input_length = sum (map S.length ss0)-    when (input_length > maxBlockSize) $ error "Trying to create too big a BGZF block; this is a bug."-    buf <- mallocBytes 65536--    -- steal header from the EOF marker (length is wrong for now)-    S.unsafeUseAsCString bgzfEofMarker $ \eof ->-        forM_ [0,4..16] $ \o -> do x <- peekByteOff eof o-                                   pokeByteOff buf o (x::Word32)--    #{poke z_stream, msg}       stream nullPtr-    #{poke z_stream, zalloc}    stream nullPtr-    #{poke z_stream, zfree}     stream nullPtr-    #{poke z_stream, opaque}    stream nullPtr-    #{poke z_stream, next_in}   stream nullPtr-    #{poke z_stream, next_out}  stream (buf `plusPtr` 18)-    #{poke z_stream, avail_in}  stream (0 :: CUInt)-    #{poke z_stream, avail_out} stream (65536-18-8 :: CUInt)--    z_check "deflateInit2" =<< c_deflateInit2 stream (fromIntegral lv) #{const Z_DEFLATED}-                                              (-15) 8 #{const Z_DEFAULT_STRATEGY}--    -- loop over the fragments.  In reverse order!-    let go (s:ss) = do-            crc <- go ss-            S.unsafeUseAsCString s $ \p ->-              case fromIntegral $ S.length s of-                l | l > 0 -> do-                    #{poke z_stream, next_in} stream p-                    #{poke z_stream, avail_in} stream (l :: CUInt)-                    z_check "deflate" =<< c_deflate stream #{const Z_NO_FLUSH}-                    c_crc32 crc p l-                _ -> return crc-        go [] = c_crc32 0 nullPtr 0-    crc <- go ss0--    z_check "deflate" =<< c_deflate stream #{const Z_FINISH}-    z_check "deflateEnd" =<< c_deflateEnd stream--    compressed_length <- (+) (18+8) `fmap` #{peek z_stream, total_out} stream-    when (compressed_length > 65536) $ error "produced too big a block"--    -- set length in header-    pokeByteOff buf 16 (fromIntegral $ (compressed_length-1) .&. 0xff :: Word8)-    pokeByteOff buf 17 (fromIntegral $ (compressed_length-1) `shiftR` 8 :: Word8)--    pokeByteOff buf (compressed_length-8) (fromIntegral crc :: Word32)-    pokeByteOff buf (compressed_length-4) (fromIntegral input_length :: Word32)--    S.unsafePackCStringFinalizer buf compressed_length (free buf)---data ZStream--{-# INLINE z_check #-}-z_check :: String -> CInt -> IO ()-z_check msg c = when (c /= #{const Z_OK} && c /= #{const Z_STREAM_END}) $-                   error $ msg ++ " failed: " ++ show c---c_deflateInit2 :: Ptr ZStream -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt-c_deflateInit2 z a b c d e = withCAString #{const_str ZLIB_VERSION} $ \versionStr ->-    c_deflateInit2_ z a b c d e versionStr (#{const sizeof(z_stream)} :: CInt)--foreign import ccall unsafe "zlib.h deflateInit2_" c_deflateInit2_ ::-    Ptr ZStream -> CInt -> CInt -> CInt -> CInt -> CInt-                -> Ptr CChar -> CInt -> IO CInt--c_inflateInit2 :: Ptr ZStream -> CInt -> IO CInt-c_inflateInit2 z a = withCAString #{const_str ZLIB_VERSION} $ \versionStr ->-    c_inflateInit2_ z a versionStr (#{const sizeof(z_stream)} :: CInt)--foreign import ccall unsafe "zlib.h inflateInit2_" c_inflateInit2_ ::-    Ptr ZStream -> CInt -> Ptr CChar -> CInt -> IO CInt--foreign import ccall unsafe "zlib.h deflate" c_deflate ::-    Ptr ZStream -> CInt -> IO CInt--foreign import ccall unsafe "zlib.h inflate" c_inflate ::-    Ptr ZStream -> CInt -> IO CInt--foreign import ccall unsafe "zlib.h deflateEnd" c_deflateEnd ::-    Ptr ZStream -> IO CInt--foreign import ccall unsafe "zlib.h inflateEnd" c_inflateEnd ::-    Ptr ZStream -> IO CInt--foreign import ccall unsafe "zlib.h crc32" c_crc32 ::-    CULong -> Ptr CChar -> CUInt -> IO CULong---- ------------------------------------------------------------------------------------------------- utils---- | Get the current virtual offset.  The virtual address in a BGZF--- stream contains the offset of the current block in the upper 48 bits--- and the current offset into that block in the lower 16 bits.  This--- scheme is compatible with the way BAM files are indexed.-getOffset :: Iteratee Block m FileOffset-getOffset = liftI step-  where-    step s@(EOF _) = icont step (Just (setEOF s))-    step s@(Chunk (Block o _)) = idone o s---- | Runs an @Iteratee@ for @Bytes@s when decompressing BGZF.  Adds--- internal bookkeeping.-liftBlock :: Monad m => Iteratee Bytes m a -> Iteratee Block m a-liftBlock = liftI . step-  where-    step it (EOF ex) = joinI $ lift $ enumChunk (EOF ex) it--    step it (Chunk (Block !l !s)) = Iteratee $ \od oc ->-            enumPure1Chunk s it >>= \it' -> runIter it' (onDone od) (oc . step . liftI)-      where-        !sl = S.length s-        onDone od hdr (Chunk !rest) = od hdr . Chunk $! Block (l + fromIntegral (sl - S.length rest)) rest-        onDone od hdr (EOF      ex) = od hdr (EOF ex)----- | Compresses a stream of @Bytes@s into a stream of BGZF blocks,--- in parallel---- We accumulate an uncompressed block as long as adding a new chunk to--- it doesn't exceed the max. block size.  If we receive an empty chunk--- (used as a flush signal), or if we would exceed the block size, we--- write out a block.  Then we continue writing until we're below block--- size.  On EOF, we flush and write the end marker.--compressBgzf' :: MonadIO m => CompressParams -> Enumeratee BgzfChunk Bytes m a-compressBgzf' (CompressParams lv np) = bgzfBlocks ><> parMapChunksIO np (compress1 lv)--data BgzfChunk = SpecialChunk  !Bytes BgzfChunk-               | RecordChunk   !Bytes BgzfChunk-               | LeftoverChunk !Bytes BgzfChunk-               | NoChunk--instance NullPoint BgzfChunk where emptyP = NoChunk-instance Nullable BgzfChunk where-    nullC NoChunk = True-    nullC (SpecialChunk  s c) = S.null s && nullC c-    nullC (RecordChunk   s c) = S.null s && nullC c-    nullC (LeftoverChunk s c) = S.null s && nullC c---- | Breaks a stream into chunks suitable to be compressed individually.--- Each chunk on output is represented as a list of 'Bytes',--- each list must be reversed and concatenated to be compressed.--- ('compress1' does that.)--bgzfBlocks :: Monad m => Enumeratee BgzfChunk [Bytes] m a-bgzfBlocks = eneeCheckIfDone (liftI . to_blocks 0 [])-  where-    -- terminate by sending the last block and then an empty block,-    -- which becomes the EOF marker-    to_blocks _alen acc k (EOF mx) =-        lift (enumPure1Chunk [S.empty] (k $ Chunk acc)) >>= flip idone (EOF mx)--    -- \'Empty list\', in a sense.-    to_blocks  alen acc k (Chunk NoChunk) = liftI $ to_blocks alen acc k--    to_blocks  alen acc k (Chunk (SpecialChunk c cs))  -- special chunk, encode then flush-        -- If it fits, flush.-        | alen + S.length c < maxBlockSize  = eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk cs)) . k $ Chunk (c:acc)-        -- If nothing is pending, flush the biggest thing that does fit.-        | null acc                       = let (l,r) = S.splitAt maxBlockSize c-                                           in eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (SpecialChunk r cs))) . k $ Chunk [l]-        -- Otherwise, flush what's pending and think again.-        | otherwise                         = eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (SpecialChunk c cs))) . k $ Chunk acc--    to_blocks  alen acc k (Chunk (RecordChunk c cs))-        -- if it fits, we accumulate,  (needs to consider the length prefix!)-        | alen + S.length c + 4 < maxBlockSize  = to_blocks (alen + S.length c + 4) (c:encLength c:acc) k (Chunk cs)-        -- else if nothing's pending, we break the chunk,  (needs to consider the length prefix!)-        | null acc                       = let (l,r) = S.splitAt (maxBlockSize-4) c-                                           in eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (LeftoverChunk r cs))) . k $-                                                    Chunk [l, encLength l]-        -- else we flush the accumulator and think again.-        | otherwise                         = eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (RecordChunk c cs))) . k $ Chunk acc-      where-        encLength s = let !l = S.length s in S.pack [ fromIntegral (l `shiftR`  0 .&. 0xff)-                                                    , fromIntegral (l `shiftR`  8 .&. 0xff)-                                                    , fromIntegral (l `shiftR` 16 .&. 0xff)-                                                    , fromIntegral (l `shiftR` 24 .&. 0xff) ]--    to_blocks  alen acc k (Chunk (LeftoverChunk c cs))-        -- if it fits, we accumulate,-        | alen + S.length c < maxBlockSize  = to_blocks (alen + S.length c) (c:acc) k (Chunk cs)-        -- else if nothing's pending, we break the chunk,-        | null acc                       = let (l,r) = S.splitAt maxBlockSize c-                                           in eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (LeftoverChunk r cs))) . k $ Chunk [l]-        -- else we flush the accumulator and think again.-        | otherwise                         = eneeCheckIfDone (\k' -> to_blocks 0 [] k' (Chunk (LeftoverChunk c cs))) . k $ Chunk acc---- | Like 'compressBgzf'', with sensible defaults.-compressBgzf :: MonadIO m => Enumeratee BgzfChunk Bytes m a-compressBgzf = compressBgzfLv 6--compressBgzfLv :: MonadIO m => Int -> Enumeratee BgzfChunk Bytes m a-compressBgzfLv lv out =  do-    np <- liftIO $ getNumCapabilities-    compressBgzf' (CompressParams lv (np+2)) out--data CompressParams = CompressParams {-        compression_level :: Int,-        queue_depth :: Int }-    deriving Show--compressChunk :: Int -> Ptr Word8 -> CUInt -> IO Bytes-compressChunk lv ptr len =-    allocaBytes (#{const sizeof(z_stream)}) $ \stream -> do-    buf <- mallocBytes 65536--    -- steal header from the EOF marker (length is wrong for now)-    S.unsafeUseAsCString bgzfEofMarker $ \eof ->-        forM_ [0,4..16] $ \o -> do x <- peekByteOff eof o-                                   pokeByteOff buf o (x::Word32)--    -- set up ZStream-    #{poke z_stream, msg}       stream nullPtr-    #{poke z_stream, zalloc}    stream nullPtr-    #{poke z_stream, zfree}     stream nullPtr-    #{poke z_stream, opaque}    stream nullPtr-    #{poke z_stream, next_in}   stream ptr-    #{poke z_stream, next_out}  stream (buf `plusPtr` 18)-    #{poke z_stream, avail_in}  stream len-    #{poke z_stream, avail_out} stream (65536-18-8 :: CUInt)--    z_check "deflateInit2" =<< c_deflateInit2 stream (fromIntegral lv) #{const Z_DEFLATED}-                                              (-15) 8 #{const Z_DEFAULT_STRATEGY}-    -- z_check "deflate" =<< c_deflate stream #{const Z_NO_FLUSH}-    z_check "deflate" =<< c_deflate stream #{const Z_FINISH}-    z_check "deflateEnd" =<< c_deflateEnd stream--    crc0 <- c_crc32 0 nullPtr 0-    crc  <- c_crc32 crc0 (castPtr ptr) len--    compressed_length <- (+) (18+8) `fmap` #{peek z_stream, total_out} stream-    when (compressed_length > 65536) $ error "produced too big a block"--    -- set length in header-    pokeByteOff buf 16 (fromIntegral $ (compressed_length-1) .&. 0xff :: Word8)-    pokeByteOff buf 17 (fromIntegral $ (compressed_length-1) `shiftR` 8 :: Word8)--    pokeByteOff buf (compressed_length-8) (fromIntegral crc :: Word32)-    pokeByteOff buf (compressed_length-4) (fromIntegral len :: Word32)--    S.unsafePackCStringFinalizer buf compressed_length (free buf)-
− src/Bio/Iteratee/Builder.hs
@@ -1,262 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}--- | Buffer builder to assemble Bgzf blocks.  The idea is to serialize--- stuff (BAM and BCF) into a buffer, then bgzf chunks from the buffer.--- We use a large buffer, and we always make sure there is plenty of--- space in it (to avoid redundant checks).--module Bio.Iteratee.Builder (-    BB(..),-    newBuffer,-    fillBuffer,-    expandBuffer,-    encodeBgzf,-    BgzfTokens(..),-    BclArgs(..),-    BclSpecialType(..),-    int_loop,-    loop_bcl_special-                            ) where--import Bio.Iteratee-import Bio.Iteratee.Bgzf                   ( compressChunk, maxBlockSize, bgzfEofMarker )-import Bio.Prelude-import Foreign.Marshal.Utils               ( copyBytes )--import qualified Data.ByteString            as B-import qualified Data.ByteString.Unsafe     as B-import qualified Data.Vector.Storable       as V---- | We manage a large buffer (multiple megabytes), of which we fill an--- initial portion.  We remember the size, the used part, and two marks--- where we later fill in sizes for the length prefixed BAM or BCF--- records.  We move the buffer down when we yield a piece downstream,--- and when we run out of space, we simply move to a new buffer.--- Garbage collection should take care of the rest.  Unused 'mark' must--- be set to (maxBound::Int) so it doesn't interfere with flushing.--data BB = BB { buffer :: {-# UNPACK #-} !(ForeignPtr Word8)-             , size   :: {-# UNPACK #-} !Int            -- total size of buffer-             , off    :: {-# UNPACK #-} !Int            -- offset of active portion-             , used   :: {-# UNPACK #-} !Int            -- used portion (inactive & active)-             , mark   :: {-# UNPACK #-} !Int            -- offset of mark-             , mark2  :: {-# UNPACK #-} !Int }          -- offset of mark2---- | Things we are able to encode.  Taking inspiration from--- binary-serialise-cbor, we define these as a lazy list-like thing and--- consume it in a interpreter.--data BgzfTokens = TkWord32   {-# UNPACK #-} !Word32       BgzfTokens -- a 4-byte int-                | TkWord16   {-# UNPACK #-} !Word16       BgzfTokens -- a 2-byte int-                | TkWord8    {-# UNPACK #-} !Word8        BgzfTokens -- a byte-                | TkFloat    {-# UNPACK #-} !Float        BgzfTokens -- a float-                | TkDouble   {-# UNPACK #-} !Double       BgzfTokens -- a double-                | TkString   {-# UNPACK #-} !B.ByteString BgzfTokens -- a raw string-                | TkDecimal  {-# UNPACK #-} !Int          BgzfTokens -- roughly ':%d'-                -- lotsa stuff might be missing here--                | TkSetMark                               BgzfTokens -- sets the first mark-                | TkEndRecord                             BgzfTokens -- completes a BAM record-                | TkEndRecordPart1                        BgzfTokens -- completes part 1 of a BCF record-                | TkEndRecordPart2                        BgzfTokens -- completes part 2 of a BCF record-                | TkEnd                                              -- nothing more, for now--                -- specialties-                | TkBclSpecial !BclArgs                   BgzfTokens-                | TkLowLevel {-# UNPACK #-} !Int (BB -> IO BB) BgzfTokens--data BclSpecialType = BclNucsBin | BclNucsWide | BclNucsAsc | BclNucsAscRev | BclQualsBin | BclQualsAsc | BclQualsAscRev--data BclArgs = BclArgs BclSpecialType-                       {-# UNPACK #-} !(V.Vector Word8)   -- bcl matrix-                       {-# UNPACK #-} !Int                -- stride-                       {-# UNPACK #-} !Int                -- first cycle-                       {-# UNPACK #-} !Int                -- last cycle-                       {-# UNPACK #-} !Int                -- cluster index---- | Creates a buffer.-newBuffer :: Int -> IO BB-newBuffer sz = mallocForeignPtrBytes sz >>= \ar -> return $ BB ar sz 0 0 maxBound maxBound---- | Creates a new buffer, copying the active content from an old one,--- with higher capacity.  The size of the new buffer is twice the free--- space in the old buffer, but at least @minsz@.-expandBuffer :: Int -> BB -> IO BB-expandBuffer minsz b = do-    let sz' = max (2 * (size b - used b)) minsz-    arr1 <- mallocForeignPtrBytes sz'-    withForeignPtr arr1 $ \d ->-        withForeignPtr (buffer b) $ \s ->-             copyBytes d (plusPtr s (off b)) (used b - off b)-    return BB{ buffer = arr1-             , size   = sz'-             , off    = 0-             , used   = used b - off b-             , mark   = if mark  b == maxBound then maxBound else mark  b - off b-             , mark2  = if mark2 b == maxBound then maxBound else mark2 b - off b }--compressChunk' :: Int -> ForeignPtr Word8 -> Int -> Int -> IO B.ByteString-compressChunk' lv fptr off len =-    withForeignPtr fptr $ \ptr ->-    compressChunk lv (plusPtr ptr off) (fromIntegral len)--instance Nullable (Endo BgzfTokens) where-    nullC f = case appEndo f TkEnd of TkEnd -> True ; _ -> False---- | Expand a chain of tokens into a buffer, sending finished pieces--- downstream as soon as possible.-encodeBgzf :: MonadIO m => Int -> Enumeratee (Endo BgzfTokens) B.ByteString m b-encodeBgzf lv = (\out -> newBuffer (1024*1024) `ioBind` \bb -> eneeCheckIfDone (liftI . go bb) out)-                ><> parRunIO (2*numCapabilities)-  where-    go bb0 k (EOF  mx) = final_flush bb0 mx k-    go bb0 k (Chunk f)-        -- initially, we make sure we have reasonable space.  this may not be enough.-        | size bb0 - used bb0 < 1024 = expandBuffer (1024*1024) bb0 `ioBind` \bb' -> go' bb' k (appEndo f TkEnd)-        | otherwise                  =                                               go' bb0 k (appEndo f TkEnd)--    go' bb0 k tk = fillBuffer bb0 tk `ioBind` \(bb',tk') -> flush_blocks tk' bb' k--    -- We can flush anything that is between 'off' and the lower of 'mark'-    -- and 'used'.  When done, we bump 'off'.-    flush_blocks tk bb k-        | min (mark bb) (used bb) - off bb < maxBlockSize =-            case tk of TkEnd -> liftI $ go bb k-                       _     -> -- we arrive here because we ran out of buffer space, so we expand it.-                                expandBuffer (1024*1024) bb `ioBind` \bb' -> go' bb' k tk-        | otherwise =-            eneeCheckIfDone (flush_blocks tk bb { off = off bb + maxBlockSize }) $-                k $ Chunk [compressChunk' lv (buffer bb) (off bb) maxBlockSize]--    final_flush bb mx k-        | used bb > off bb =-            idone (k $ Chunk [ compressChunk' lv (buffer bb) (off bb) (used bb - off bb)-                             , return bgzfEofMarker ]) (EOF mx)-        | otherwise =-            idone (k $ Chunk [ return bgzfEofMarker ]) (EOF mx)---fillBuffer :: BB -> BgzfTokens -> IO (BB, BgzfTokens)-fillBuffer bb0 tk = withForeignPtr (buffer bb0) (\p -> go_slowish p bb0 tk)-  where-    go_slowish p bb = go_fast p bb (used bb)--    go_fast p bb use tk1 = case tk1 of-        -- no space?  not our job.-        _ | size bb - use < 1024 -> return (bb { used = use },tk1)--        -- the actual end.-        TkEnd                    -> return (bb { used = use },tk1)--        -- I'm cheating.  This stuff works only if the platform allows-        -- unaligned accesses, is little-endian and uses IEEE floats.-        -- It's true on i386 and ix86_64.-        TkWord32   x tk' -> do pokeByteOff p use x-                               go_fast p bb (use + 4) tk'--        TkWord16   x tk' -> do pokeByteOff p use x-                               go_fast p bb (use + 2) tk'--        TkWord8    x tk' -> do pokeByteOff p use x-                               go_fast p bb (use + 1) tk'--        TkFloat    x tk' -> do pokeByteOff p use x-                               go_fast p bb (use + 4) tk'--        TkDouble   x tk' -> do pokeByteOff p use x-                               go_fast p bb (use + 8) tk'--        TkString   s tk'-            -- Too big, can't handle.  By returning with unfinished-            -- business, we will get progressively bigger buffers and-            -- eventually handle it.-            | B.length s > size bb - use -> return (bb { used = use },tk1)--            | otherwise  -> do let ln = B.length s-                               B.unsafeUseAsCString s $ \q ->-                                    copyBytes (p `plusPtr` use) q ln-                               go_fast p bb (use + ln) tk'--        TkDecimal  x tk' -> do ln <- int_loop (p `plusPtr` use) x-                               go_fast p bb (use + ln) tk'--        TkSetMark        tk' ->    go_slowish p bb { used = use + 4, mark = use } tk'--        TkEndRecord      tk' -> do let !l = use - mark bb - 4-                                   pokeByteOff p (mark bb) (fromIntegral l :: Word32)-                                   go_slowish p bb { used = use, mark = maxBound } tk'--        TkEndRecordPart1 tk' -> do let !l = use - mark bb - 4-                                   pokeByteOff p (mark bb - 4) (fromIntegral l :: Word32)-                                   go_slowish p bb { used = use, mark2 = use } tk'--        TkEndRecordPart2 tk' -> do let !l = use - mark2 bb-                                   pokeByteOff p (mark bb) (fromIntegral l :: Word32)-                                   go_slowish p bb { used = use, mark = maxBound } tk'---        TkBclSpecial special_args tk' -> do-            l <- loop_bcl_special (p `plusPtr` use) special_args-            go_fast p bb (use + l) tk'--        TkLowLevel minsize proc tk'-            | size bb - use < minsize -> return (bb { used = use },tk1)-            | otherwise               -> do bb' <- proc bb { used = use }-                                            go_slowish p bb' tk'---loop_bcl_special :: Ptr Word8 -> BclArgs -> IO Int-loop_bcl_special p (BclArgs tp vec stride u v i) =--    V.unsafeWith vec $ \q -> case tp of-        BclNucsBin -> do-            nuc_loop p stride (plusPtr q i) u v-            return $ (v - u + 2) `div` 2--        BclNucsWide -> do-            nuc_loop_wide p stride (plusPtr q i) u v-            return $ v - u + 1--        BclNucsAsc -> do-            nuc_loop_asc p stride (plusPtr q i) u v-            return $ v - u + 1--        BclNucsAscRev -> do-            nuc_loop_asc_rev p stride (plusPtr q i) u v-            return $ v - u + 1--        BclQualsBin -> do-            qual_loop p stride (plusPtr q i) u v-            return $ v - u + 1--        BclQualsAsc -> do-            qual_loop_asc p stride (plusPtr q i) u v-            return $ v - u + 1--        BclQualsAscRev -> do-            qual_loop_asc_rev p stride (plusPtr q i) u v-            return $ v - u + 1--foreign import ccall unsafe "nuc_loop"-    nuc_loop :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "nuc_loop_wide"-    nuc_loop_wide :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "nuc_loop_asc"-    nuc_loop_asc :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "nuc_loop_asc_rev"-    nuc_loop_asc_rev :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "qual_loop"-    qual_loop :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "qual_loop_asc"-    qual_loop_asc :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "qual_loop_asc_rev"-    qual_loop_asc_rev :: Ptr Word8 -> Int -> Ptr Word8 -> Int -> Int -> IO ()--foreign import ccall unsafe "int_loop"-    int_loop :: Ptr Word8 -> Int -> IO Int-
− src/Bio/Iteratee/Bytes.hs
@@ -1,269 +0,0 @@-{-# LANGUAGE FlexibleContexts, BangPatterns #-}---- |Monadic Iteratees:--- incremental input parsers, processors, and transformers------ Iteratees for parsing binary data.--module Bio.Iteratee.Bytes (-  -- * Types-  Endian (..)-  -- * Endian multi-byte iteratees-  ,endianRead2-  ,endianRead3-  ,endianRead3i-  ,endianRead4-  ,endianRead8--  -- * Iteratees treating Bytes as list of Word8-  ,headStreamBS-  ,tryHeadBS-  ,peekStreamBS-  ,takeStreamBS-  ,dropStreamBS-  ,dropWhileStreamBS--  -- * Iteratees treating Bytes as list of Char-  ,enumLinesBS-  ,enumWordsBS-)-where--import Bio.Iteratee.Base-import Bio.Iteratee.Iteratee-import Bio.Prelude--import qualified Data.ByteString              as B-import qualified Data.ByteString.Char8        as C-import qualified Data.ByteString.Unsafe       as B---- --------------------------------------------------------------------------- Binary Random IO Iteratees---- Iteratees to read unsigned integers written in Big- or Little-endian ways---- | Indicate endian-ness.-data Endian = MSB -- ^ Most Significant Byte is first (big-endian)-  | LSB           -- ^ Least Significan Byte is first (little-endian)-  deriving (Eq, Ord, Show, Enum)--endianRead2 :: Endian -> Iteratee Bytes m Word16-endianRead2 e = endianReadN e 2 word16'-{-# INLINE endianRead2 #-}--endianRead3 :: Endian -> Iteratee Bytes m Word32-endianRead3 e = endianReadN e 3 (word32' . (0:))-{-# INLINE endianRead3 #-}---- |Read 3 bytes in an endian manner.  If the first bit is set (negative),--- set the entire first byte so the Int32 will be negative as--- well.-endianRead3i :: Monad m => Endian -> Iteratee Bytes m Int32-endianRead3i e = do-  c1 <- headStreamBS-  c2 <- headStreamBS-  c3 <- headStreamBS-  case e of-    MSB -> return $ (((fromIntegral c1-                        `shiftL` 8) .|. fromIntegral c2)-                        `shiftL` 8) .|. fromIntegral c3-    LSB ->-     let m :: Int32-         m = shiftR (shiftL (fromIntegral c3) 24) 8-     in return $ (((fromIntegral c3-                        `shiftL` 8) .|. fromIntegral c2)-                        `shiftL` 8) .|. fromIntegral m-{-# INLINE endianRead3i #-}--endianRead4 :: Endian -> Iteratee Bytes m Word32-endianRead4 e = endianReadN e 4 word32'-{-# INLINE endianRead4 #-}--endianRead8 :: Endian -> Iteratee Bytes m Word64-endianRead8 e = endianReadN e 8 word64'-{-# INLINE endianRead8 #-}---- This function does all the parsing work, depending upon provided arguments-endianReadN ::-  Endian-  -> Int-  -> ([Word8] -> b)-  -> Iteratee Bytes m b-endianReadN MSB n0 cnct = liftI (step n0 [])- where-  step !n acc (Chunk c)-    | B.null c        = liftI (step n acc)-    | B.length c >= n = let (this,next) = B.splitAt n c-                            !result     = cnct $ acc ++ B.unpack this-                        in idone result (Chunk next)-    | otherwise        = liftI (step (n - B.length c) (acc ++ B.unpack c))-  step !n acc (EOF Nothing)  = icont (step n acc) (Just $ toException EofException)-  step !n acc (EOF (Just e)) = icont (step n acc) (Just e)-endianReadN LSB n0 cnct = liftI (step n0 [])- where-  step !n acc (Chunk c)-    | B.null c        = liftI (step n acc)-    | B.length c >= n = let (this,next) = B.splitAt n c-                            !result = cnct $ B.unpack (B.reverse this) ++ acc-                        in idone result (Chunk next)-    | otherwise        = liftI (step (n - B.length c)-                                     (B.unpack (B.reverse c) ++ acc))-  step !n acc (EOF Nothing)  = icont (step n acc)-                                    (Just $ toException EofException)-  step !n acc (EOF (Just e)) = icont (step n acc) (Just e)-{-# INLINE endianReadN #-}---word16' :: [Word8] -> Word16-word16' [c1,c2] = word16 c1 c2-word16' _ = error "iteratee: internal error in word16'"--word16 :: Word8 -> Word8 -> Word16-word16 c1 c2 = (fromIntegral c1 `shiftL`  8) .|.  fromIntegral c2-{-# INLINE word16 #-}--word32' :: [Word8] -> Word32-word32' [c1,c2,c3,c4] = word32 c1 c2 c3 c4-word32' _ = error "iteratee: internal error in word32'"--word32 :: Word8 -> Word8 -> Word8 -> Word8 -> Word32-word32 c1 c2 c3 c4 =-  (fromIntegral c1 `shiftL` 24) .|.-  (fromIntegral c2 `shiftL` 16) .|.-  (fromIntegral c3 `shiftL`  8) .|.-   fromIntegral c4-{-# INLINE word32 #-}--word64' :: [Word8] -> Word64-word64' [c1,c2,c3,c4,c5,c6,c7,c8] = word64 c1 c2 c3 c4 c5 c6 c7 c8-word64' _ = error "iteratee: internal error in word64'"-{-# INLINE word64' #-}--word64-  :: Word8 -> Word8 -> Word8 -> Word8-  -> Word8 -> Word8 -> Word8 -> Word8-  -> Word64-word64 c1 c2 c3 c4 c5 c6 c7 c8 =-  (fromIntegral c1 `shiftL` 56) .|.-  (fromIntegral c2 `shiftL` 48) .|.-  (fromIntegral c3 `shiftL` 40) .|.-  (fromIntegral c4 `shiftL` 32) .|.-  (fromIntegral c5 `shiftL` 24) .|.-  (fromIntegral c6 `shiftL` 16) .|.-  (fromIntegral c7 `shiftL`  8) .|.-   fromIntegral c8-{-# INLINE word64 #-}--headStreamBS :: Iteratee Bytes m Word8-headStreamBS = liftI step-  where-  step (Chunk c)-    | B.null c = icont step Nothing-    | otherwise  = idone (B.unsafeHead c) (Chunk (B.unsafeTail c))-  step stream          = icont step (Just (setEOF stream))-{-# INLINE headStreamBS #-}--peekStreamBS :: Iteratee Bytes m (Maybe Word8)-peekStreamBS = liftI step-  where-    step s@(Chunk vec)-      | B.null vec = liftI step-      | otherwise   = idone (Just $ B.unsafeHead vec) s-    step stream     = idone Nothing stream-{-# INLINE peekStreamBS #-}--tryHeadBS :: Iteratee Bytes m (Maybe Word8)-tryHeadBS = liftI step-  where-  step (Chunk vec)-    | B.null vec = liftI step-    | otherwise  = idone (Just (B.unsafeHead vec)) (Chunk (B.unsafeTail vec))-  step stream          = idone Nothing stream-{-# INLINE tryHeadBS #-}--dropStreamBS :: Int -> Iteratee Bytes m ()-dropStreamBS 0  = idone () (Chunk emptyP)-dropStreamBS n' = liftI (step n')-  where-    step n (Chunk str)-      | B.length str < n = liftI (step (n - B.length str))-      | otherwise        = idone () (Chunk (B.drop n str))-    step _ stream        = idone () stream-{-# INLINE dropStreamBS #-}--dropWhileStreamBS :: (Word8 -> Bool) -> Iteratee Bytes m ()-dropWhileStreamBS p = liftI step-  where-    step (Chunk str)-      | B.null rest  = liftI step-      | otherwise    = idone () (Chunk rest)-      where-        rest = B.dropWhile p str-    step stream      = idone () stream-{-# INLINE dropWhileStreamBS #-}--takeStreamBS ::-  Monad m-  => Int   -- ^ number of elements to consume-  -> Enumeratee Bytes Bytes m a-takeStreamBS n' iter- | n' <= 0   = return iter- | otherwise = Iteratee $ \od oc -> runIter iter (on_done od oc) (on_cont od oc)-  where-    on_done od oc x _ = runIter (dropStreamBS n' >> return (return x)) od oc-    on_cont od oc k Nothing = if n' == 0 then od (liftI k) (Chunk mempty)-                                 else runIter (liftI (step n' k)) od oc-    on_cont od oc _ (Just e) = runIter (dropStreamBS n' >> throwErr e) od oc-    step n k (Chunk str)-      | B.null str        = liftI (step n k)-      | B.length str <= n = takeStreamBS (n - B.length str) $ k (Chunk str)-      | otherwise          = idone (k (Chunk s1)) (Chunk s2)-      where (s1, s2) = B.splitAt n str-    step _n k stream       = idone (liftI k) stream-{-# INLINE takeStreamBS #-}---- Like enumWords, but operates on ByteStrings.--- This is provided as a higher-performance alternative to enumWords, and--- is equivalent to treating the stream as a Data.ByteString.Char8.ByteString.-enumWordsBS :: Monad m => Enumeratee Bytes [Bytes] m a-enumWordsBS = convStream getter-  where-    getter = liftI step-    lChar = isSpace . C.last-    step (Chunk xs)-      | C.null xs  = getter-      | lChar xs   = idone (C.words xs) (Chunk C.empty)-      | otherwise  = icont (step' xs) Nothing-    step str       = idone mempty str-    step' xs (Chunk ys)-      | C.null ys  = icont (step' xs) Nothing-      | lChar ys   = idone (C.words . C.append xs $ ys) mempty-      | otherwise  = let w' = C.words . C.append xs $ ys-                         ws = init w'-                         ck = last w'-                     in idone ws (Chunk ck)-    step' xs str   = idone (C.words xs) str-{-# INLINE enumWordsBS #-}---- Like enumLines, but operates on ByteStrings.--- This is provided as a higher-performance alternative to enumLines, and--- is equivalent to treating the stream as a Data.ByteString.Char8.ByteString.-enumLinesBS :: Monad m => Enumeratee Bytes [Bytes] m a-enumLinesBS = convStream getter-  where-    getter = icont step Nothing-    lChar = (== '\n') . C.last-    step (Chunk xs)-      | C.null xs  = getter-      | lChar xs   = idone (C.lines xs) (Chunk C.empty)-      | otherwise  = icont (step' xs) Nothing-    step str       = idone mempty str-    step' xs (Chunk ys)-      | C.null ys  = icont (step' xs) Nothing-      | lChar ys   = idone (C.lines . C.append xs $ ys) mempty-      | otherwise  = let w' = C.lines $ C.append xs ys-                         ws = init w'-                         ck = last w'-                     in idone ws (Chunk ck)-    step' xs str   = idone (C.lines xs) str-{-# INLINE enumLinesBS #-}
− src/Bio/Iteratee/Exception.hs
@@ -1,212 +0,0 @@-{-# LANGUAGE ExistentialQuantification #-}---- |Monadic and General Iteratees:--- Messaging and exception handling.------ Iteratees use an internal exception handling mechanism that is parallel to--- that provided by 'Control.Exception'.  This allows the iteratee framework--- to handle its own exceptions outside @IO@.------ Iteratee exceptions are divided into two categories, 'IterException' and--- 'EnumException'.  @IterExceptions@ are exceptions within an iteratee, and--- @EnumExceptions@ are exceptions within an enumerator.------ Enumerators can be constructed to handle an 'IterException' with--- @Data.Iteratee.Iteratee.enumFromCallbackCatch@.  If the enumerator detects--- an @iteratee exception@, the enumerator calls the provided exception handler.--- The enumerator is then able to continue feeding data to the iteratee,--- provided the exception was successfully handled.  If the handler could--- not handle the exception, the 'IterException' is converted to an--- 'EnumException' and processing aborts.------ Exceptions can also be cleared by @Data.Iteratee.Iteratee.checkErr@,--- although in this case the iteratee continuation cannot be recovered.------ When viewed as Resumable Exceptions, iteratee exceptions provide a means--- for iteratees to send control messages to enumerators.  The @seek@--- implementation provides an example.  @Data.Iteratee.Iteratee.seek@ stores--- the current iteratee continuation and throws a 'SeekException', which--- inherits from 'IterException'.  @Data.Iteratee.IO.enumHandleRandom@ is--- constructed with @enumFromCallbackCatch@ and a handler that performs--- an @hSeek@.  Upon receiving the 'SeekException', @enumHandleRandom@ calls--- the handler, checks that it executed properly, and then continues with--- the stored continuation.------ As the exception hierarchy is open, users can extend it with custom--- exceptions and exception handlers to implement sophisticated messaging--- systems based upon resumable exceptions.---module Bio.Iteratee.Exception (-  -- * Exception types-  IFException (..)-  ,Exception (..)             -- from Control.Exception-  ,FileOffset                 -- from System.Posix.Types-  -- ** Enumerator exceptions-  ,EnumException (..)-  ,DivergentException (..)-  ,EnumStringException (..)-  ,EnumUnhandledIterException (..)-  -- ** Iteratee exceptions-  ,IException (..)-  ,IterException (..)-  ,SeekException (..)-  ,EofException (..)-  ,IterStringException (..)-  -- * Functions-  ,enStrExc-  ,iterStrExc-  ,wrapIterExc-  ,iterExceptionToException-  ,iterExceptionFromException-)-where--import Control.Exception-import Data.Data-import Prelude-import System.Posix.Types ( FileOffset )----- ------------------------------------------------- create exception type hierarchy---- |Root of the Iteratee exception hierarchy.  @IFException@ derives from--- @Control.Exception.SomeException@.  'EnumException', 'IterException',--- and all inheritants are descendents of 'IFException'.-data IFException = forall e . Exception e => IFException e-  deriving Typeable--instance Show IFException where-  show (IFException e) = show e--instance Exception IFException--ifExceptionToException :: Exception e => e -> SomeException-ifExceptionToException = toException . IFException--ifExceptionFromException :: Exception e => SomeException -> Maybe e-ifExceptionFromException x = do-  IFException a <- fromException x-  cast a---- Root of enumerator exceptions.-data EnumException = forall e . Exception e => EnumException e-  deriving Typeable--instance Show EnumException where-  show (EnumException e) = show e--instance Exception EnumException where-  toException   = ifExceptionToException-  fromException = ifExceptionFromException--enumExceptionToException :: Exception e => e -> SomeException-enumExceptionToException = toException . IterException--enumExceptionFromException :: Exception e => SomeException -> Maybe e-enumExceptionFromException x = do-  IterException a <- fromException x-  cast a---- |The @iteratee@ diverged upon receiving 'EOF'.-data DivergentException = DivergentException-  deriving (Show, Typeable)--instance Exception DivergentException where-  toException   = enumExceptionToException-  fromException = enumExceptionFromException---- |Create an enumerator exception from a @String@.-newtype EnumStringException = EnumStringException String-  deriving (Show, Typeable)--instance Exception EnumStringException where-  toException   = enumExceptionToException-  fromException = enumExceptionFromException---- |Create an 'EnumException' from a string.-enStrExc :: String -> EnumException-enStrExc = EnumException . EnumStringException---- |The enumerator received an 'IterException' it could not handle.-newtype EnumUnhandledIterException = EnumUnhandledIterException IterException-  deriving (Show, Typeable)--instance Exception EnumUnhandledIterException where-  toException   = enumExceptionToException-  fromException = enumExceptionFromException---- |Convert an 'IterException' to an 'EnumException'.  Meant to be used--- within an @Enumerator@ to signify that it could not handle the--- @IterException@.-wrapIterExc :: IterException -> EnumException-wrapIterExc = EnumException . EnumUnhandledIterException---- iteratee exceptions---- |A class for @iteratee exceptions@.  Only inheritants of @IterException@--- should be instances of this class.-class Exception e => IException e where-  toIterException   :: e -> IterException-  toIterException   = IterException-  fromIterException :: IterException -> Maybe e-  fromIterException = fromException . toException---- |Root of iteratee exceptions.-data IterException = forall e . Exception e => IterException e-  deriving Typeable--instance Show IterException where-  show (IterException e) = show e--instance Exception IterException where-  toException   = ifExceptionToException-  fromException = ifExceptionFromException--iterExceptionToException :: Exception e => e -> SomeException-iterExceptionToException = toException . IterException--iterExceptionFromException :: Exception e => SomeException -> Maybe e-iterExceptionFromException x = do-  IterException a <- fromException x-  cast a--instance IException IterException where-  toIterException   = id-  fromIterException = Just---- |A seek request within an @Iteratee@.-newtype SeekException = SeekException FileOffset-  deriving (Typeable, Show)--instance Exception SeekException where-  toException   = iterExceptionToException-  fromException = iterExceptionFromException--instance IException SeekException where---- |The @Iteratee@ needs more data but received @EOF@.-data EofException = EofException-  deriving (Typeable, Show)--instance Exception EofException where-  toException   = iterExceptionToException-  fromException = iterExceptionFromException--instance IException EofException where---- |An @Iteratee exception@ specified by a @String@.-newtype IterStringException = IterStringException String deriving (Typeable, Show)--instance Exception IterStringException where-  toException   = iterExceptionToException-  fromException = iterExceptionFromException--instance IException IterStringException where---- |Create an @iteratee exception@ from a string.--- This convenience function wraps 'IterStringException' and 'toException'.-iterStrExc :: String -> SomeException-iterStrExc= toException . IterStringException-
− src/Bio/Iteratee/IO.hs
@@ -1,91 +0,0 @@--- |Random and Binary IO with generic Iteratees, using File Descriptors for IO.--- when available, these are the preferred functions for performing IO as they--- run in constant space and function properly with sockets, pipes, etc.--module Bio.Iteratee.IO(-  -- * Data-  defaultBufSize-  -- * File enumerators-  ,enumFile-  ,enumFileRandom-  -- * FileDescriptor based enumerators for monadic iteratees-  ,enumFd-  ,enumFdRandom-)-where--import Bio.Iteratee.Iteratee-import Bio.Prelude       hiding ( bracket, loop )-import Control.Monad.IO.Class   ( MonadIO(..)   )-import System.IO                ( SeekMode(..)  )--import qualified Data.ByteString as B---- | Default buffer size in elements.  This was 1024 in "Data.Iteratee",--- which is obviously too small.  Since we often want to merge many--- files, a read should take more time than a seek.  This sets the--- sensible buffer size to somewhat more than one MB.-defaultBufSize :: Int-defaultBufSize = 2*1024*1024---- |The enumerator of a POSIX File Descriptor.  This version enumerates--- over the entire contents of a file, in order, unless stopped by--- the iteratee.  In particular, seeking is not supported.-enumFd :: MonadIO m => Int -> Fd -> Enumerator Bytes m a-enumFd bufsize fd = loop-  where-    loop iter = runIter iter idoneM onCont--    onCont k j@(Just _) = return (icont k j)-    onCont k   Nothing  = do-        s <- liftIO $ fdGet bufsize fd-        if B.null s then return $ liftI k-                    else loop . k $ Chunk s----- |The enumerator of a POSIX File Descriptor: a variation of @enumFd@ that--- supports RandomIO (seek requests).-enumFdRandom :: MonadIO m => Int -> Fd -> Enumerator Bytes m a-enumFdRandom bs fd = loop-  where-    loop iter = runIter iter idoneM onCont--    onCont k Nothing  = do-        s <- liftIO $ fdGet bs fd-        if B.null s then return $ liftI k-                    else loop . k $ Chunk s--    onCont k j@(Just e) = case fromException e of-        Just (SeekException off) -> do-            liftIO . void $ fdSeek fd AbsoluteSeek (fromIntegral off)-            onCont k Nothing--        Nothing -> return (icont k j)----enumFile' :: MonadBracketIO m =>-  (Int -> Fd -> Enumerator s m a)-  -> Int -- ^Buffer size-  -> FilePath-  -> Enumerator s m a-enumFile' enumf bufsize filepath iter = bracketIO-  (openFd filepath ReadOnly Nothing defaultFileFlags)-  (closeFd)-  (flip (enumf bufsize) iter)--enumFile ::-  MonadBracketIO m-  => Int                 -- ^Buffer size-  -> FilePath-  -> Enumerator Bytes m a-enumFile = enumFile' enumFd--enumFileRandom ::-  MonadBracketIO m-  => Int                 -- ^Buffer size-  -> FilePath-  -> Enumerator Bytes m a-enumFileRandom = enumFile' enumFdRandom--
− src/Bio/Iteratee/Iteratee.hs
@@ -1,601 +0,0 @@-{-# LANGUAGE Rank2Types, FlexibleContexts #-}---- |Monadic and General Iteratees:--- incremental input parsers, processors and transformers--module Bio.Iteratee.Iteratee (-  -- * Types-  EnumerateeHandler-  -- ** Error handling-  ,throwErr-  ,throwRecoverableErr-  ,checkErr-  -- ** Basic Iteratees-  ,skipToEof-  ,isStreamFinished-  -- ** Iteratee composition-  ,mBind-  ,mBind_-  ,ioBind-  ,ioBind_-  ,MonadBracketIO(..)-  -- ** Chunkwise Iteratees-  ,mapChunksM_-  ,foldChunksM-  ,getChunk-  ,getChunks-  -- ** Nested iteratee combinators-  ,mapChunks-  ,mapChunksM-  ,convStream-  ,unfoldConvStream-  ,unfoldConvStreamCheck-  ,joinI-  ,joinIM-  -- * Enumerators-  ,Enumerator-  ,Enumeratee-  -- ** Basic enumerators-  ,enumChunk-  ,enumEof-  ,enumErr-  ,enumPure1Chunk-  ,enumList-  ,enumCheckIfDone-  ,enumFromCallback-  ,enumFromCallbackCatch-  -- ** Enumerator Combinators-  ,eneeCheckIfDone-  ,eneeCheckIfDoneHandle-  ,eneeCheckIfDoneIgnore-  ,eneeCheckIfDonePass-  ,mergeEnums-  -- ** Enumeratee Combinators-  ,($=)-  ,(=$)-  ,(><>)-  ,(<><)-  -- * Misc.-  ,seek-  -- * Classes-  ,module Bio.Iteratee.Base-)-where--import Bio.Iteratee.Base-import Bio.Prelude hiding (loop)-import Control.Monad.Catch as CIO-import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad.Trans.Class (MonadTrans(..))---- exception helpers-excDivergent :: SomeException-excDivergent = toException DivergentException---- --------------------------------------------------------------------------- Primitive iteratees---- |Report and propagate an unrecoverable error.---  Disregard the input first and then propagate the error.  This error--- cannot be handled by 'enumFromCallbackCatch', although it can be cleared--- by 'checkErr'.-throwErr :: SomeException -> Iteratee s m a-throwErr e = icont (const (throwErr e)) (Just e)---- |Report and propagate a recoverable error.  This error can be handled by--- both 'enumFromCallbackCatch' and 'checkErr'.-throwRecoverableErr ::-  SomeException-  -> (Stream s -> Iteratee s m a)-  -> Iteratee s m a-throwRecoverableErr e i = icont i (Just e)----- |Check if an iteratee produces an error.--- Returns @Right a@ if it completes without errors, otherwise--- @Left SomeException@. 'checkErr' is useful for iteratees that may not--- terminate, such as @Data.Iteratee.head@ with an empty stream.-checkErr ::- (NullPoint s) =>-  Iteratee s m a-  -> Iteratee s m (Either SomeException a)-checkErr iter = Iteratee $ \onDone onCont ->-  let od            = onDone . Right-      oc k Nothing  = onCont (checkErr . k) Nothing-      oc _ (Just e) = onDone (Left e) (Chunk emptyP)-  in runIter iter od oc---- --------------------------------------------------------------------------- Parser combinators---- |Get the stream status of an iteratee.-isStreamFinished :: (Nullable s) => Iteratee s m (Maybe SomeException)-isStreamFinished = liftI check-  where-    check s@(Chunk xs)-      | nullC xs  = isStreamFinished-      | otherwise = idone Nothing s-    check s@(EOF e) = idone (Just $ fromMaybe (toException EofException) e) s-{-# INLINE isStreamFinished #-}----- |Skip the rest of the stream-skipToEof :: Iteratee s m ()-skipToEof = icont check Nothing-  where-    check (Chunk _) = skipToEof-    check s         = idone () s----- |Seek to a position in the stream-seek :: Nullable s => FileOffset -> Iteratee s m ()-seek o = throwRecoverableErr (toException $ SeekException o) (idone ())----- | Map a monadic function over the chunks of the stream and ignore the--- result.  Useful for creating efficient monadic iteratee consumers, e.g.------ >  logger = mapChunksM_ (liftIO . putStrLn)------ these can be efficiently run in parallel with other iteratees via--- @Data.Iteratee.ListLike.zip@.-mapChunksM_ :: (Monad m, Nullable s) => (s -> m b) -> Iteratee s m ()-mapChunksM_ f = liftI step-  where-    step (Chunk xs)-      | nullC xs   = liftI step-      | otherwise  = lift (f xs) >> liftI step-    step s@(EOF _) = idone () s-{-# INLINE mapChunksM_ #-}---- | A fold over chunks-foldChunksM :: (Monad m, Nullable s) => (a -> s -> m a) -> a -> Iteratee s m a-foldChunksM f = liftI . go-  where-    go a (Chunk c) = lift (f a c) >>= liftI . go-    go a e = idone a e-{-# INLINE foldChunksM #-}---- | Get the current chunk from the stream.-getChunk :: Nullable s => Iteratee s m s-getChunk = liftI step- where-  step (Chunk xs)-    | nullC xs  = liftI step-    | otherwise = idone xs $ Chunk emptyP-  step (EOF Nothing)  = throwErr $ toException EofException-  step (EOF (Just e)) = throwErr e-{-# INLINE getChunk #-}---- | Get a list of all chunks from the stream.-getChunks :: (Nullable s) => Iteratee s m [s]-getChunks = liftI (step id)- where-  step acc (Chunk xs)-    | nullC xs    = liftI (step acc)-    | otherwise   = liftI (step $ acc . (xs:))-  step acc stream = idone (acc []) stream-{-# INLINE getChunks #-}---- ------------------------------------------------------ The converters show a different way of composing two iteratees:--- `vertical' rather than `horizontal'--type Enumeratee sFrom sTo m a =-  Iteratee sTo m a-  -> Iteratee sFrom m (Iteratee sTo m a)---- The following pattern appears often in Enumeratee code-{-# INLINE eneeCheckIfDone #-}---- | Utility function for creating enumeratees.  Typical usage is demonstrated--- by the @breakE@ definition.------ > breakE--- >   :: (Monad m, LL.ListLike s el, NullPoint s)--- >   => (el -> Bool)--- >   -> Enumeratee s s m a--- > breakE cpred = eneeCheckIfDone (liftI . step)--- >  where--- >   step k (Chunk s)--- >       | LL.null s  = liftI (step k)--- >       | otherwise  = case LL.break cpred s of--- >         (str', tail')--- >           | LL.null tail' -> eneeCheckIfDone (liftI . step) . k $ Chunk str'--- >           | otherwise     -> idone (k $ Chunk str') (Chunk tail')--- >   step k stream           =  idone (k stream) stream----eneeCheckIfDone ::- (Monad m, NullPoint elo) =>-  ((Stream eli -> Iteratee eli m a) -> Iteratee elo m (Iteratee eli m a))-  -> Enumeratee elo eli m a-eneeCheckIfDone f = eneeCheckIfDonePass f'- where-  f' k Nothing  = f k-  f' k (Just e) = throwRecoverableErr e (\s -> joinIM $ enumChunk s $ eneeCheckIfDone f (liftI k))--type EnumerateeHandler eli elo m a =-  (Stream eli -> Iteratee eli m a)-  -> SomeException-  -> Iteratee elo m (Iteratee eli m a)---- | The same as eneeCheckIfDonePass, with one extra argument:--- a handler which is used--- to process any exceptions in a separate method.-eneeCheckIfDoneHandle-  :: (NullPoint elo)-  => EnumerateeHandler eli elo m a-  -> ((Stream eli -> Iteratee eli m a)-      -> Maybe SomeException-      -> Iteratee elo m (Iteratee eli m a)-     )-  -> Enumeratee elo eli m a-eneeCheckIfDoneHandle h f inner = Iteratee $ \od oc ->-  let onDone x s = od (idone x s) (Chunk emptyP)-      onCont k Nothing  = runIter (f k Nothing) od oc-      onCont k (Just e) = runIter (h k e)       od oc-  in runIter inner onDone onCont-{-# INLINABLE eneeCheckIfDoneHandle #-}--eneeCheckIfDonePass-  :: (NullPoint elo)-  => ((Stream eli -> Iteratee eli m a)-      -> Maybe SomeException-      -> Iteratee elo m (Iteratee eli m a)-     )-  -> Enumeratee elo eli m a-eneeCheckIfDonePass f = eneeCheckIfDoneHandle (\k e -> f k (Just e)) f-{-# INLINABLE eneeCheckIfDonePass #-}--eneeCheckIfDoneIgnore-  :: (NullPoint elo)-  => ((Stream eli -> Iteratee eli m a)-      -> Maybe SomeException-      -> Iteratee elo m (Iteratee eli m a)-     )-  -> Enumeratee elo eli m a-eneeCheckIfDoneIgnore f = eneeCheckIfDoneHandle (\k _ -> f k Nothing) f--{-# INLINE mBind #-}--- | Lifts a monadic action and combines it with a continuation.--- @mBind m f@ is the same as @lift m >>= f@, but does not require a--- 'Nullable' constraint on the stream type.-infixl 1 `mBind`-mBind :: Monad m => m a -> (a -> Iteratee s m b) -> Iteratee s m b-mBind m f = Iteratee $ \onDone onCont -> m >>= \a -> runIter (f a) onDone onCont--{-# INLINE mBind_ #-}--- | Lifts a monadic action, ignored the result and combines it with a--- continuation.  @mBind_ m f@ is the same as @lift m >>= f@, but does--- not require a 'Nullable' constraint on the stream type.-infixl 1 `mBind_`-mBind_ :: Monad m => m a -> Iteratee s m b -> Iteratee s m b-mBind_ m b = Iteratee $ \onDone onCont -> m >> runIter b onDone onCont--{-# INLINE ioBind #-}--- | Lifts an IO action and combines it with a continuation.--- @ioBind m f@ is the same as @liftIO m >>= f@, but does not require a--- 'Nullable' constraint on the stream type.-infixl 1 `ioBind`-ioBind :: MonadIO m => IO a -> (a -> Iteratee s m b) -> Iteratee s m b-ioBind m f = Iteratee $ \onDone onCont -> liftIO m >>= \a -> runIter (f a) onDone onCont--{-# INLINE ioBind_ #-}--- | Lifts an IO action, ignores its result, and combines it with a--- continuation.  @ioBind_ m f@ is the same as @liftIO m >> f@, but does--- not require a 'Nullable' constraint on the stream type.-infixl 1 `ioBind_`-ioBind_ :: MonadIO m => IO a -> Iteratee s m b -> Iteratee s m b-ioBind_ m b = Iteratee $ \onDone onCont -> liftIO m >> runIter b onDone onCont--class (MonadCatch m, MonadIO m) => MonadBracketIO m where-    altmask :: ((forall a. m a -> m a) -> m b) -> m b--    -- | Runs an 'Iteratee' in between an 'IO' action to acquire a-    -- resource and one to release it.  'Iteratee' can't be an instance-    -- of 'CIO.MonadMask', so 'CIO.bracket' isn't defined for it.-    -- However, if we restrict the acquire/release actions to 'IO',-    -- which is the most important use case anyway, we can directly-    -- implement this weaker version.-    bracketIO :: IO a -> (a -> IO b) -> (a -> m c) -> m c--instance MonadBracketIO IO where-    {-# INLINE altmask #-}-    altmask = CIO.mask--    {-# INLINE bracketIO #-}-    bracketIO = CIO.bracket--instance (MonadBracketIO m, Nullable s) => MonadBracketIO (Iteratee s m) where-    {-# INLINE altmask #-}-    altmask q = Iteratee $ \od oc -> altmask $ \u -> runIter (q $ ilift u) od oc--    {-# INLINE bracketIO #-}-    bracketIO acquire release use =-      Iteratee $ \od oc -> altmask $ \u ->-          runIter (acquire `ioBind` \resource ->-                   ilift u (use resource) `CIO.onException` liftIO (release resource) >>= \result ->-                   release resource `ioBind_` return result) od oc----- | Convert one stream into another with the supplied mapping function.--- This function operates on whole chunks at a time, contrasting to--- @mapStream@ which operates on single elements.------ > unpacker :: Enumeratee B.ByteString [Word8] m a--- > unpacker = mapChunks B.unpack----mapChunks :: (NullPoint s) => (s -> s') -> Enumeratee s s' m a-mapChunks f = eneeCheckIfDonePass (icont . step)- where-  step k (Chunk xs)     = eneeCheckIfDonePass (icont . step) . k . Chunk $ f xs-  step k str@(EOF mErr) = idone (k $ EOF mErr) str-{-# INLINE mapChunks #-}---- | Convert a stream of @s@ to a stream of @s'@ using the supplied function.-mapChunksM :: (Monad m, NullPoint s) => (s -> m s') -> Enumeratee s s' m a-mapChunksM f = eneeCheckIfDonePass (icont . step)- where-  step k (Chunk xs) = f xs `mBind` eneeCheckIfDonePass (icont . step) . k . Chunk-  step k str        = idone (liftI k) str-{-# INLINE mapChunksM #-}---- |Convert one stream into another, not necessarily in lockstep.------ The transformer mapStream maps one element of the outer stream--- to one element of the nested stream.  The transformer below is more--- general: it may take several elements of the outer stream to produce--- one element of the inner stream, or the other way around.--- The transformation from one stream to the other is specified as--- Iteratee s m s'.-convStream ::- (Monad m, Nullable s) =>-  Iteratee s m s'-  -> Enumeratee s s' m a-convStream fi = eneeCheckIfDonePass check-  where-    check k (Just e) = throwRecoverableErr e (idone ()) >> check k Nothing-    check k _ = isStreamFinished >>= maybe (step k) (idone (liftI k) . EOF . Just)-    step k = fi >>= eneeCheckIfDonePass check . k . Chunk-{-# INLINABLE convStream #-}---- |The most general stream converter.  Given a function to produce iteratee--- transformers and an initial state, convert the stream using iteratees--- generated by the function while continually updating the internal state.-unfoldConvStream ::- (Monad m, Nullable s) =>-  (acc -> Iteratee s m (acc, s'))-  -> acc-  -> Enumeratee s s' m a-unfoldConvStream f acc0 = eneeCheckIfDonePass (check acc0)-  where-    check acc k (Just e) = throwRecoverableErr e (idone ()) >> check acc k Nothing-    check acc k _ = isStreamFinished >>=-                    maybe (step acc k) (idone (liftI k) . EOF . Just)-    step acc k = f acc >>= \(acc', s') ->-                    eneeCheckIfDonePass (check acc') . k . Chunk $ s'-{-# INLINABLE unfoldConvStream #-}--unfoldConvStreamCheck-  :: (Monad m, Nullable elo)-  => (((Stream eli -> Iteratee eli m a)-        -> Maybe SomeException-        -> Iteratee elo m (Iteratee eli m a)-      )-      -> Enumeratee elo eli m a-     )-  -> (acc -> Iteratee elo m (acc, eli))-  -> acc-  -> Enumeratee elo eli m a-unfoldConvStreamCheck checkDone f acc0 = checkDone (check acc0)-  where-    check acc k mX = isStreamFinished >>=-                   maybe (step acc k mX) (idone (icont k mX) . EOF . Just)-    step acc k Nothing = f acc >>= \(acc', s') ->-                  (checkDone (check acc') . k $ Chunk s')-    step acc k (Just ex) = throwRecoverableErr ex $ \str' ->-      let i = f acc >>= \(acc', s') ->-                           (checkDone (check acc') . k $ Chunk s')-      in joinIM $ enumChunk str' i-{-# INLINABLE unfoldConvStreamCheck #-}---- | Collapse a nested iteratee.  The inner iteratee is terminated by @EOF@.---   Errors are propagated through the result.------  The stream resumes from the point of the outer iteratee; any remaining---  input in the inner iteratee will be lost.---  Differs from 'Control.Monad.join' in that the inner iteratee is terminated,---  and may have a different stream type than the result.-joinI ::- (Monad m, Nullable s) =>-  Iteratee s m (Iteratee s' m a)-  -> Iteratee s m a-joinI = (>>=-  \inner -> Iteratee $ \od oc ->-  let onDone  x _        = od x (Chunk emptyP)-      onCont  k Nothing  = runIter (k (EOF Nothing)) onDone onCont'-      onCont  _ (Just e) = runIter (throwErr e) od oc-      onCont' _ e        = runIter (throwErr (fromMaybe excDivergent e)) od oc-  in runIter inner onDone onCont)-{-# INLINE joinI #-}---- | Lift an iteratee inside a monad to an iteratee.-joinIM :: (Monad m) => m (Iteratee s m a) -> Iteratee s m a-joinIM mIter = Iteratee $ \od oc -> mIter >>= \iter -> runIter iter od oc----- --------------------------------------------------------------------------- Enumerators--- | Each enumerator takes an iteratee and returns an iteratee------ an Enumerator is an iteratee transformer.--- The enumerator normally stops when the stream is terminated--- or when the iteratee moves to the done state, whichever comes first.--- When to stop is of course up to the enumerator...--type Enumerator s m a = Iteratee s m a -> m (Iteratee s m a)---- |Applies the iteratee to the given stream.  This wraps 'enumEof',--- 'enumErr', and 'enumPure1Chunk', calling the appropriate enumerator--- based upon 'Stream'.-enumChunk :: (Monad m) => Stream s -> Enumerator s m a-enumChunk (Chunk xs)     = enumPure1Chunk xs-enumChunk (EOF Nothing)  = enumEof-enumChunk (EOF (Just e)) = enumErr e---- |The most primitive enumerator: applies the iteratee to the terminated--- stream. The result is the iteratee in the Done state.  It is an error--- if the iteratee does not terminate on EOF.-enumEof :: (Monad m) => Enumerator s m a-enumEof iter = runIter iter onDone onCont-  where-    onDone  x _str    = return $ idone x (EOF Nothing)-    onCont  k Nothing = runIter (k (EOF Nothing)) onDone onCont'-    onCont  k e       = return $ icont k e-    onCont' _ Nothing = return $ throwErr excDivergent-    onCont' k e       = return $ icont k e---- |Another primitive enumerator: tell the Iteratee the stream terminated--- with an error.-enumErr :: (Exception e, Monad m) => e -> Enumerator s m a-enumErr e iter = runIter iter onDone onCont-  where-    onDone  x _       = return $ idone x (EOF . Just $ toException e)-    onCont  k Nothing = runIter (k (EOF (Just (toException e)))) onDone onCont'-    onCont  k e'      = return $ icont k e'-    onCont' _ Nothing = return $ throwErr excDivergent-    onCont' k e'      = return $ icont k e'---infixr 0 =$---- | Combines an Enumeratee from @s@ to @s'@ and an Iteratee that---  consumes @s'@ into an Iteratee which consumes @s@-(=$)-  :: (Nullable s, Monad m)-  => Enumeratee s s' m a-  -> Iteratee s' m a-  -> Iteratee s m a-(=$) = (.) joinI--infixl 1 $=---- | Combines Enumerator which produces stream of @s@ and @Enumeratee@---  which transforms stream of @s@ to stream---  of @s'@ to into Enumerator which produces stream of @s'@-($=)-  :: Monad m-  => (forall a. Enumerator s m a)-  -> Enumeratee s s' m b-  -> Enumerator s' m b-($=) enum enee iter = enum (enee iter) >>= run----- | Enumeratee composition--- Run the second enumeratee within the first.  In this example, stream2list--- is run within the 'takeStream 10', which is itself run within 'takeStream 15', resulting--- in 15 elements being consumed------ >>> run =<< enumPure1Chunk [1..1000 :: Int] (joinI $ (I.takeStream 15 ><> I.takeStream 10) I.stream2list)--- [1,2,3,4,5,6,7,8,9,10]----(><>) ::- (Nullable s1, Monad m)-  => (forall x . Enumeratee s1 s2 m x)-  -> Enumeratee s2 s3 m a-  -> Enumeratee s1 s3 m a-f ><> g = joinI . f . g---- | enumeratee composition with the arguments flipped, see '><>'-(<><) ::- (Nullable s1, Monad m)-  => Enumeratee s2 s3 m a-  -> (forall x. Enumeratee s1 s2 m x)-  -> Enumeratee s1 s3 m a-f <>< g = joinI . g . f---- | Combine enumeration over two streams.  The merging enumeratee would--- typically be the result of 'Data.Iteratee.ListLike.merge' or--- 'Data.Iteratee.ListLike.mergeByChunks' (see @merge@ for example).-mergeEnums ::-  (Nullable s2, Nullable s1, Monad m)-  => Enumerator s1 m a                   -- ^ inner enumerator-  -> Enumerator s2 (Iteratee s1 m) a     -- ^ outer enumerator-  -> Enumeratee s2 s1 (Iteratee s1 m) a  -- ^ merging enumeratee-  -> Enumerator s1 m a-mergeEnums e1 e2 etee i = e1 $ e2 (joinI . etee $ ilift lift i) >>= run-{-# INLINE mergeEnums #-}---- | The pure 1-chunk enumerator------ It passes a given list of elements to the iteratee in one chunk--- This enumerator does no IO and is useful for testing of base parsing-enumPure1Chunk :: (Monad m) => s -> Enumerator s m a-enumPure1Chunk str iter = runIter iter idoneM onCont-  where-    onCont k Nothing = return $ k $ Chunk str-    onCont k e       = return $ icont k e---- | Enumerate chunks from a list----enumList :: (Monad m) => [s] -> Enumerator s m a-enumList = go- where-  go [] i = return i-  go xs' i = runIter i idoneM (onCont xs')-   where-    onCont (x:xs) k Nothing = go xs . k $ Chunk x-    onCont _ _ (Just e) = return $ throwErr e-    onCont _ k Nothing  = return $ icont k Nothing-{-# INLINABLE enumList #-}---- | Checks if an iteratee has finished.------ This enumerator runs the iteratee, performing any monadic actions.--- If the result is True, the returned iteratee is done.-enumCheckIfDone :: (Monad m) => Iteratee s m a -> m (Bool, Iteratee s m a)-enumCheckIfDone iter = runIter iter onDone onCont-  where-    onDone x str = return (True, idone x str)-    onCont k e   = return (False, icont k e)-{-# INLINE enumCheckIfDone #-}----- |Create an enumerator from a callback function-enumFromCallback ::- (Monad m, NullPoint s) =>-  (st -> m (Either SomeException ((Bool, st), s)))-  -> st-  -> Enumerator s m a-enumFromCallback c = loop-  where-    loop st iter = runIter iter idoneM (onCont st)-    check k (True,  st') = loop st' . k . Chunk-    check k (False,_st') = return . k . Chunk-    onCont st k Nothing  = c st >>=-        either (return . k . EOF . Just) (uncurry (check k))-    onCont _st k j = return (icont k j)---- |Create an enumerator from a callback function with an exception handler.--- The exception handler is called if an iteratee reports an exception.-enumFromCallbackCatch-  :: (IException e, Monad m, NullPoint s)-  => (st -> m (Either SomeException ((Bool, st), s)))-  -> (e -> m (Maybe EnumException))-  -> st-  -> Enumerator s m a-enumFromCallbackCatch c handler = loop-  where-    loop st iter = runIter iter idoneM (onCont st)-    check k (True,  st') = loop st' . k . Chunk-    check k (False,_st') = return . k . Chunk-    onCont st k Nothing  = c st >>=-        either (return . k . EOF . Just) (uncurry (check k))-    onCont st k j@(Just e) = case fromException e of-      Just e' -> handler e' >>=-                   maybe (loop st . k $ Chunk emptyP)-                         (return . icont k . Just) . fmap toException-      Nothing -> return (icont k j)-{-# INLINE enumFromCallbackCatch #-}--
− src/Bio/Iteratee/List.hs
@@ -1,810 +0,0 @@--- |Monadic Iteratees:--- incremental input parsers, processors and transformers------ This module provides many basic iteratees from which more complicated--- iteratees can be built.  In general these iteratees parallel those in--- @Data.List@, with some additions.--module Bio.Iteratee.List (-  -- * Iteratees-  -- ** Iteratee Utilities-  isFinished-  ,stream2list-  ,stream2stream-  -- ** Basic Iteratees-  ,dropWhileStream-  ,dropStream-  ,headStream-  ,tryHead-  ,lastStream-  ,heads-  ,peekStream-  ,roll-  ,lengthStream-  ,chunkLength-  ,takeFromChunk-  -- ** Nested iteratee combinators-  ,breakStream-  ,breakE-  ,takeStream-  ,takeUpTo-  ,takeWhileE-  ,mapStream-  ,concatMapStream-  ,concatMapStreamM-  ,mapMaybeStream-  ,filterStream-  ,filterStreamM-  ,groupStreamBy-  ,groupStreamOn-  ,mergeStreams-  ,mergeByChunks-  -- ** Folds-  ,foldStream-  -- * Enumerators-  -- ** Basic enumerators-  ,enumPureNChunk-  -- ** Enumerator Combinators-  ,enumWith-  ,zipStreams-  ,zipStreams3-  ,zipStreams4-  ,zipStreams5-  ,sequenceStreams_-  ,countConsumed-  -- ** Monadic functions-  ,mapStreamM-  ,mapStreamM_-  ,foldStreamM-  -- * Re-exported modules-  ,module Bio.Iteratee.Iteratee-)-where--import Bio.Iteratee.Iteratee-import Bio.Prelude-import Control.Monad.Trans.Class (MonadTrans(..))---- import qualified Data.ByteString          as B----- Useful combinators for implementing iteratees and enumerators---- | Check if a stream has received 'EOF'.-isFinished :: Nullable s => Iteratee s m Bool-isFinished = liftI check-  where-  check c@(Chunk xs)-    | nullC xs    = liftI check-    | otherwise   = idone False c-  check s@(EOF _) = idone True s-{-# INLINE isFinished #-}---- --------------------------------------------------------------------------- Primitive iteratees---- |Read a stream to the end and return all of its elements as a list.--- This iteratee returns all data from the stream *strictly*.-stream2list :: Monad m => Iteratee [el] m [el]-stream2list = liftM concat getChunks-{-# INLINE stream2list #-}---- |Read a stream to the end and return all of its elements as a stream.--- This iteratee returns all data from the stream *strictly*.-stream2stream :: (Monad m, Nullable s, Monoid s) => Iteratee s m s-stream2stream = liftM mconcat getChunks-{-# INLINE stream2stream #-}----- --------------------------------------------------------------------------- Parser combinators---- |Attempt to read the next element of the stream and return it--- Raise a (recoverable) error if the stream is terminated.------ The analogue of @List.head@------ Because @head@ can raise an error, it shouldn't be used when constructing--- iteratees for @convStream@.  Use @tryHead@ instead.-headStream :: Iteratee [el] m el-headStream = liftI step-  where-  step (Chunk [     ]) = icont step Nothing-  step (Chunk (hd:tl)) = idone hd (Chunk tl)-  step stream          = icont step (Just (setEOF stream))-{-# INLINE headStream #-}---- | Similar to @headStream@, except it returns @Nothing@ if the stream--- is terminated.-tryHead :: Iteratee [el] m (Maybe el)-tryHead = liftI step-  where-  step (Chunk [     ]) = liftI step-  step (Chunk (hd:tl)) = idone (Just hd) (Chunk tl)-  step stream          = idone Nothing stream-{-# INLINE tryHead #-}---- |Attempt to read the last element of the stream and return it--- Raise a (recoverable) error if the stream is terminated------ The analogue of @List.last@-lastStream :: Iteratee [el] m el-lastStream = liftI (step Nothing)-  where-  step l (Chunk xs)-    | nullC xs     = liftI (step l)-    | otherwise    = liftI $ step (Just $ last xs)-  step l s@(EOF _) = case l of-    Nothing -> icont (step l) . Just . setEOF $ s-    Just x  -> idone x s-{-# INLINE lastStream #-}----- |Given a sequence of characters, attempt to match them against--- the characters on the stream.  Return the count of how many--- characters matched.  The matched characters are removed from the--- stream.--- For example, if the stream contains 'abd', then (heads 'abc')--- will remove the characters 'ab' and return 2.-heads :: (Monad m, Eq el) => [el] -> Iteratee [el] m Int-heads st | nullC st = return 0-heads st = loopE 0 st-  where-  loopE cnt xs-    | nullC xs  = return cnt-    | otherwise = liftI (step cnt xs)-  step cnt str (Chunk [])          = liftI (step cnt str)-  step cnt [ ] stream              = idone cnt stream-  step cnt (y:ys) s@(Chunk (x:xs))-    | y == x    = step (succ cnt) ys (Chunk xs)-    | otherwise = idone cnt s-  step cnt _ stream         = idone cnt stream-{-# INLINE heads #-}----- |Look ahead at the next element of the stream, without removing--- it from the stream.--- Return @Just c@ if successful, return @Nothing@ if the stream is--- terminated by 'EOF'.-peekStream :: Iteratee [el] m (Maybe el)-peekStream = liftI step-  where-    step   (Chunk [   ]) = liftI step-    step s@(Chunk (x:_)) = idone (Just x) s-    step stream          = idone Nothing stream-{-# INLINE peekStream #-}---- | Return a chunk of @t@ elements length while consuming @d@ elements---   from the stream.  Useful for creating a 'rolling average' with---  'convStream'.-roll-  :: Monad m-  => Int  -- ^ length of chunk (t)-  -> Int  -- ^ amount to consume (d)-  -> Iteratee [el] m [[el]]-roll t d | t > d  = liftI step-  where-    step (Chunk vec)-      | length vec >= t =-          idone [take t vec] (Chunk $ drop d vec)-      | null vec        = liftI step-      | otherwise          = liftI (step' vec)-    step stream            = idone empty stream-    step' v1 (Chunk vec)   = step . Chunk $ v1 `mappend` vec-    step' v1 stream        = idone [v1] stream-roll t d = do r <- joinI (takeStream t stream2stream)-              dropStream (d-t)-              return [r]-  -- d is >= t, so this version works-{-# INLINE roll #-}----- |Drop n elements of the stream, if there are that many.------ The analogue of @List.drop@-dropStream :: Int -> Iteratee [el] m ()-dropStream 0  = idone () (Chunk emptyP)-dropStream n' = liftI (step n')-  where-    step n (Chunk str)-      | length str < n = liftI (step (n - length str))-      | otherwise         = idone () (Chunk (drop n str))-    step _ stream         = idone () stream-{-# INLINE dropStream #-}---- |Skip all elements while the predicate is true.------ The analogue of @List.dropWhile@-dropWhileStream :: (el -> Bool) -> Iteratee [el] m ()-dropWhileStream p = liftI step-  where-    step (Chunk str)-      | null rest = liftI step-      | otherwise    = idone () (Chunk rest)-      where-        rest = dropWhile p str-    step stream      = idone () stream-{-# INLINE dropWhileStream #-}----- | Return the total length of the remaining part of the stream.------ This forces evaluation of the entire stream.------ The analogue of @List.length@-lengthStream :: Num a => Iteratee [el] m a-lengthStream = liftI (step 0)-  where-    step !i (Chunk xs) = liftI (step $ i + fromIntegral (length xs))-    step !i stream     = idone i stream-{-# INLINE lengthStream #-}---- | Get the length of the current chunk, or @Nothing@ if 'EOF'.------ This function consumes no input.-chunkLength :: Iteratee [el] m (Maybe Int)-chunkLength = liftI step- where-  step s@(Chunk xs) = idone (Just $ length xs) s-  step stream       = idone Nothing stream-{-# INLINE chunkLength #-}---- | Take @n@ elements from the current chunk, or the whole chunk if--- @n@ is greater.-takeFromChunk :: Int -> Iteratee [el] m [el]-takeFromChunk n | n <= 0 = idone emptyP (Chunk emptyP)-takeFromChunk n = liftI step- where-  step (Chunk xs) = let (h,t) = splitAt n xs in idone h $ Chunk t-  step stream     = idone emptyP stream-{-# INLINE takeFromChunk #-}---- |Takes an element predicate and returns the (possibly empty) prefix of--- the stream.  None of the characters in the string satisfy the character--- predicate.--- If the stream is not terminated, the first character of the remaining stream--- satisfies the predicate.------ N.B. 'breakE' should be used in preference to @breakStream@.--- @breakStream@ will retain all data until the predicate is met, which may--- result in a space leak.------ The analogue of @List.break@--breakStream :: (el -> Bool) -> Iteratee [el] m [el]-breakStream cpred = icont (step mempty) Nothing-  where-    step bfr (Chunk str)-      | null str          =  icont (step bfr) Nothing-      | otherwise         =  case break cpred str of-        (str', tail')-          | null tail'    -> icont (step (bfr `mappend` str)) Nothing-          | otherwise     -> idone (bfr `mappend` str') (Chunk tail')-    step bfr stream       =  idone bfr stream-{-# INLINE breakStream #-}---- ------------------------------------------------------ The converters show a different way of composing two iteratees:--- `vertical' rather than `horizontal'---- |Takes an element predicate and an iteratee, running the iteratee--- on all elements of the stream until the predicate is met.------ the following rule relates @break@ to @breakE@--- @break@ pred === @joinI@ (@breakE@ pred stream2stream)------ @breakE@ should be used in preference to @break@ whenever possible.-breakE :: (el -> Bool) -> Enumeratee [el] [el] m a-breakE cpred = eneeCheckIfDonePass (icont . step)- where-  step k (Chunk s)-      | null s  = liftI (step k)-      | otherwise  = case break cpred s of-        (str', tail')-          | null tail'    -> eneeCheckIfDonePass (icont . step) . k $ Chunk str'-          | otherwise     -> idone (k $ Chunk str') (Chunk tail')-  step k stream           =  idone (liftI k) stream-{-# INLINE breakE #-}---- |Read n elements from a stream and apply the given iteratee to the--- stream of the read elements. Unless the stream is terminated early, we--- read exactly n elements, even if the iteratee has accepted fewer.------ The analogue of @List.take@-takeStream ::-  Monad m-  => Int   -- ^ number of elements to consume-  -> Enumeratee [el] [el] m a-takeStream n' iter- | n' <= 0   = return iter- | otherwise = Iteratee $ \od oc -> runIter iter (on_done od oc) (on_cont od oc)-  where-    on_done od oc x _ = runIter (dropStream n' >> return (return x)) od oc-    on_cont od oc k Nothing = if n' == 0 then od (liftI k) (Chunk mempty)-                                 else runIter (liftI (step n' k)) od oc-    on_cont od oc _ (Just e) = runIter (dropStream n' >> throwErr e) od oc-    step n k (Chunk str)-      | null str           = liftI (step n k)-      | length str <= n    = takeStream (n - length str) $ k (Chunk str)-      | otherwise          = idone (k (Chunk s1)) (Chunk s2)-      where (s1, s2) = splitAt n str-    step _n k stream       = idone (liftI k) stream-{-# INLINE takeStream #-}---- |Read n elements from a stream and apply the given iteratee to the--- stream of the read elements. If the given iteratee accepted fewer--- elements, we stop.--- This is the variation of 'takeStream' with the early termination--- of processing of the outer stream once the processing of the inner stream--- finished early.------ Iteratees composed with 'takeUpTo' will consume only enough elements to--- reach a done state.  Any remaining data will be available in the outer--- stream.------ > > let iter = do--- > h <- joinI $ takeUpTo 5 I.head--- > t <- stream2list--- > return (h,t)--- >--- > > enumPureNChunk [1..10::Int] 3 iter >>= run >>= print--- > (1,[2,3,4,5,6,7,8,9,10])--- >--- > > enumPureNChunk [1..10::Int] 7 iter >>= run >>= print--- > (1,[2,3,4,5,6,7,8,9,10])------ in each case, @I.head@ consumes only one element, returning the remaining--- 4 elements to the outer stream-takeUpTo :: Monad m => Int -> Enumeratee [el] [el] m a-takeUpTo i iter- | i <= 0    = idone iter (Chunk emptyP)- | otherwise = Iteratee $ \od oc ->-    runIter iter (onDone od oc) (onCont od oc)-  where-    onDone od oc x str      = runIter (idone (return x) str) od oc-    onCont od oc k Nothing  = if i == 0 then od (liftI k) (Chunk mempty)-                                 else runIter (liftI (step i k)) od oc-    onCont od oc _ (Just e) = runIter (throwErr e) od oc-    step n k (Chunk str)-      | null str       = liftI (step n k)-      | length str < n = takeUpTo (n - length str) $ k (Chunk str)-      | otherwise      =-         -- check to see if the inner iteratee has completed, and if so,-         -- grab any remaining stream to put it in the outer iteratee.-         -- the outer iteratee is always complete at this stage, although-         -- the inner may not be.-         let (s1, s2) = splitAt n str-         in Iteratee $ \od' _ -> do-              res <- runIter (k (Chunk s1)) (\a s  -> return $ Left  (a, s))-                                            (\k' e -> return $ Right (k',e))-              case res of-                Left (a,Chunk s1') -> od' (return a)-                                          (Chunk $ s1' ++ s2)-                Left  (a,s')       -> od' (idone a s') (Chunk s2)-                Right (k',e)       -> od' (icont k' e) (Chunk s2)-    step _ k stream       = idone (liftI k) stream-{-# INLINE takeUpTo #-}----- |Takes an element predicate and an iteratee, running the iteratee--- on all elements of the stream while the predicate is met.------ This is preferred to @takeWhile@.-takeWhileE :: (el -> Bool) -> Enumeratee [el] [el] m a-takeWhileE = breakE . (not .)-{-# INLINEABLE takeWhileE #-}---- | Map a function over an 'Iteratee'.--- This one is reimplemented and differs from the the one in--- "Data.Iteratee.ListLike" in so far that it doesn't pass on an 'EOF'--- received in the input, which is the expected behavior.-mapStream :: (el -> el') -> Enumeratee [el] [el'] m a-mapStream = mapChunks . map-{-# INLINE mapStream #-}---- | Apply a function to the elements of a stream, concatenate the--- results into a stream.  No giant intermediate list is produced.-concatMapStream :: Monoid t => (a -> t) -> Enumeratee [a] t m r-concatMapStream = mapChunks . foldMap-{-# INLINE concatMapStream #-}---- | Apply a monadic function to the elements of a stream, concatenate--- the results into a stream.  No giant intermediate list is produced.-concatMapStreamM :: Monad m => (a -> m t) -> Enumeratee [a] t m r-concatMapStreamM f = eneeCheckIfDone (liftI . go)-  where-    go k (EOF   mx)              = idone (liftI k) (EOF mx)-    go k (Chunk xs) | null xs    = liftI (go k)-                    | otherwise  = f (head xs) `mBind`-                                   eneeCheckIfDone (flip go (Chunk (tail xs))) . k . Chunk-{-# INLINE concatMapStreamM #-}--mapMaybeStream :: (a -> Maybe b) -> Enumeratee [a] [b] m r-mapMaybeStream = mapChunks . mapMaybe-{-# INLINE mapMaybeStream #-}---- |Creates an 'enumeratee' with only elements from the stream that--- satisfy the predicate function.  The outer stream is completely consumed.------ The analogue of @List.filter@-filterStream :: (el -> Bool) -> Enumeratee [el] [el] m a-filterStream p = mapChunks (filter p)-{-# INLINE filterStream #-}---- | Apply a monadic filter predicate to an 'Iteratee'.-filterStreamM :: Monad m => (a -> m Bool) -> Enumeratee [a] [a] m r-filterStreamM k = mapChunksM (go id)-  where-    go acc [   ] = return $! acc empty-    go acc (h:t) = do p <- k h-                      let acc' = if p then (:) h . acc else acc-                      go acc' t-{-# INLINE filterStreamM #-}---- | Grouping on 'Iteratee's.  @groupStreamOn proj inner outer@ executes--- @inner (proj e)@, where @e@ is the first input element, to obtain an--- 'Iteratee' @i@, then passes elements @e@ to @i@ as long as @proj e@--- produces the same result.  If @proj e@ changes or the input ends, the--- pair of @proj e@ and the result of @run i@ is passed to @outer@.  At--- end of input, the resulting @outer@ is returned.-groupStreamOn :: (Monad m, Eq t1)-              => (e -> t1)-              -> (t1 -> m (Iteratee [e] m t2))-              -> Enumeratee [e] [(t1, t2)] m a-groupStreamOn proj inner = eneeCheckIfDonePass (icont . step)-  where-    step outer   (EOF      mx) = idone (liftI outer) $ EOF mx-    step outer   (Chunk [   ]) = liftI $ step outer-    step outer c@(Chunk (h:_)) = let x = proj h-                                 in lift (inner x) >>= \i -> step' x i outer c--    -- We want to feed a 'Chunk' to the inner 'Iteratee', which might be-    -- finished.  In that case, we would want to abort, but we cannot,-    -- since the outer iteration is still going on.  So instead we-    -- discard data we would have fed to the inner 'Iteratee'.  (Use of-    -- 'enumPure1Chunk' is not appropriate, it would accumulate the-    -- data, just to have it discarded by the 'run' that eventually-    -- happens.--    step' c it outer (Chunk as)-        | null as = liftI $ step' c it outer-        | (l,r) <- span ((==) c . proj) as, not (null l) =-            let od a    _str = idoneM a $ EOF Nothing-                oc k Nothing = return $ k (Chunk l)-                oc k       m = icontM k m-            in lift (runIter it od oc) >>= \it' -> step' c it' outer (Chunk r)--    step' c it outer str =-        lift (run it) >>= \b -> eneeCheckIfDone (`step` str) . outer $ Chunk [(c,b)]----- | Grouping on 'Iteratee's.  @groupStreamBy cmp inner outer@ executes--- @inner@ to obtain an 'Iteratee' @i@, then passes elements @e@ to @i@--- as long as @cmp e0 e@, where @e0@ is some preceeding element, is--- true.  Else, the result of @run i@ is passed to @outer@ and--- 'groupStreamBy' restarts.  At end of input, the resulting @outer@ is--- returned.-groupStreamBy :: Monad m-              => (t -> t -> Bool)-              -> m (Iteratee [t] m t2)-              -> Enumeratee [t] [t2] m a-groupStreamBy cmp inner = eneeCheckIfDonePass (icont . step)-  where-    step outer   (EOF      mx) = idone (liftI outer) $ EOF mx-    step outer   (Chunk [   ]) = liftI $ step outer-    step outer c@(Chunk (h:_)) = lift inner >>= \i -> step' h i outer c--    step' c it outer (Chunk as)-        | null as = liftI $ step' c it outer-        | (l,r) <- span (cmp c) as, not (null l) =-            let od a    _str = idoneM a $ EOF Nothing-                oc k Nothing = return $ k (Chunk l)-                oc k       m = icontM k m-            in lift (runIter it od oc) >>= \it' -> step' (head l) it' outer (Chunk r)--    step' _ it outer str =-        lift (run it) >>= \b -> eneeCheckIfDone (`step` str) . outer $ Chunk [b]----- | @mergeStreams@ offers another way to nest iteratees: as a monad stack.--- This allows for the possibility of interleaving data from multiple--- streams.------ > -- print each element from a stream of lines.--- > logger :: (MonadIO m) => Iteratee [ByteString] m ()--- > logger = mapStreamM_ (liftIO . putStrLn . B.unpack)--- >--- > -- combine alternating lines from two sources--- > -- To see how this was derived, follow the types from--- > -- 'ileaveLines logger' and work outwards.--- > run =<< enumFile 10 "file1" (joinI $ enumLinesBS $--- >           ( enumFile 10 "file2" . joinI . enumLinesBS $ joinI--- >                 (ileaveLines logger)) >>= run)--- >--- > ileaveLines :: (Functor m, Monad m)--- >   => Enumeratee [ByteString] [ByteString] (Iteratee [ByteString] m)--- >        [ByteString]--- > ileaveLines = mergeStreams (\l1 l2 ->--- >    [B.pack "f1:\n\t" ,l1 ,B.pack "f2:\n\t" ,l2 ]--- >--- >----mergeStreams :: Monad m => (el1 -> el2 -> b) -> Enumeratee [el2] b (Iteratee [el1] m) a-mergeStreams f = convStream $ liftM2 f (lift headStream) headStream-{-# INLINE mergeStreams #-}---- | A version of mergeStreams which operates on chunks instead of--- elements.------ mergeByChunks offers more control than 'mergeStreams'.--- 'mergeStreams' terminates when the first stream terminates, however--- mergeByChunks will continue until both streams are exhausted.------ 'mergeByChunks' guarantees that both chunks passed to the merge--- function will have the same number of elements, although that number--- may vary between calls.-mergeByChunks ::-  Monad m-  => ([el1] -> [el2] -> c3)  -- ^ merge function-  -> ([el1] -> c3)-  -> ([el2] -> c3)-  -> Enumeratee [el2] c3 (Iteratee [el1] m) a-mergeByChunks f f1 f2 = unfoldConvStream iter (0 :: Int)- where-  iter 1 = (\x -> (1,f1 x)) `liftM` lift getChunk-  iter 2 = (\x -> (2,f2 x)) `liftM` getChunk-  iter _ = do-    ml1 <- lift chunkLength-    ml2 <- chunkLength-    case (ml1, ml2) of-      (Just l1, Just l2) -> do-        let tval = min l1 l2-        c1 <- lift $ takeFromChunk tval-        c2 <- takeFromChunk tval-        return (0, f c1 c2)-      (Just _, Nothing) -> iter 1-      (Nothing, _)      -> iter 2-{-# INLINE mergeByChunks #-}---- --------------------------------------------------------------------------- Folds---- | Left-associative fold that is strict in the accumulator.--- This function should be used in preference to 'foldl' whenever possible.------ The analogue of @List.foldl'@.-foldStream :: (a -> el -> a) -> a -> Iteratee [el] m a-foldStream f i = liftI (step i)-  where-    step acc (Chunk xs)-      | null xs = liftI (step acc)-      | otherwise  = liftI (step $! foldl' f acc xs)-    step acc stream = idone acc stream-{-# INLINE foldStream #-}---- --------------------------------------------------------------------------- Zips---- |Enumerate two iteratees over a single stream simultaneously.------ Compare to @List.zip@.-zipStreams-  :: Monad m-  => Iteratee [el] m a-  -> Iteratee [el] m b-  -> Iteratee [el] m (a, b)-zipStreams x0 y0 = do-    -- need to check if both iteratees are initially finished.  If so,-    -- we don't want to push a chunk which will be dropped-    (a', x') <- lift $ runIter x0 od oc-    (b', y') <- lift $ runIter y0 od oc-    case checkDone a' b' of-      Just (Right (a,b,s))  -> idone (a,b) s  -- 's' may be EOF, needs to stay-      Just (Left (Left a))  -> liftM ((,) a) y'-      Just (Left (Right b)) -> liftM (flip (,) b) x'-      Nothing               -> liftI (step x' y')-  where-    step x y (Chunk xs) | nullC xs = liftI (step x y)-    step x y (Chunk xs) = do-      (a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x-      (b', y') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs y-      case checkDone a' b' of-        Just (Right (a,b,s))  -> idone (a,b) s-        Just (Left (Left a))  -> liftM ((,) a) y'-        Just (Left (Right b)) -> liftM (flip (,) b) x'-        Nothing               -> liftI (step x' y')-    step x y (EOF err) = joinIM $ case err of-      Nothing -> (liftM2.liftM2) (,) (enumEof   x) (enumEof   y)-      Just e  -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)--    od a s = return (Just (a, s), idone a s)-    oc k e = return (Nothing    , icont k e)--    checkDone r1 r2 = case (r1, r2) of-      (Just (a, s1), Just (b,s2)) -> Just $ Right (a, b, shorter s1 s2)-      (Just (a, _), Nothing)      -> Just . Left $ Left a-      (Nothing, Just (b, _))      -> Just . Left $ Right b-      (Nothing, Nothing)          -> Nothing--    shorter c1@(Chunk xs) c2@(Chunk ys)-      | length xs < length ys = c1-      | otherwise                   = c2-    shorter e@(EOF _)  _         = e-    shorter _          e@(EOF _) = e-{-# INLINE zipStreams #-}--zipStreams3-  :: Monad m-  => Iteratee [el] m a -> Iteratee [el] m b-  -> Iteratee [el] m c -> Iteratee [el] m (a, b, c)-zipStreams3 a b c = zipStreams a (zipStreams b c) >>=-  \(r1, (r2, r3)) -> return (r1, r2, r3)-{-# INLINE zipStreams3 #-}--zipStreams4-  :: Monad m-  => Iteratee [el] m a -> Iteratee [el] m b-  -> Iteratee [el] m c -> Iteratee [el] m d-  -> Iteratee [el] m (a, b, c, d)-zipStreams4 a b c d = zipStreams a (zipStreams3 b c d) >>=-  \(r1, (r2, r3, r4)) -> return (r1, r2, r3, r4)-{-# INLINE zipStreams4 #-}--zipStreams5-  :: Monad m-  => Iteratee [el] m a -> Iteratee [el] m b-  -> Iteratee [el] m c -> Iteratee [el] m d-  -> Iteratee [el] m e -> Iteratee [el] m (a, b, c, d, e)-zipStreams5 a b c d e = zipStreams a (zipStreams4 b c d e) >>=-  \(r1, (r2, r3, r4, r5)) -> return (r1, r2, r3, r4, r5)-{-# INLINE zipStreams5 #-}---- | Enumerate over two iteratees in parallel as long as the first iteratee--- is still consuming input.  The second iteratee will be terminated with EOF--- when the first iteratee has completed.  An example use is to determine--- how many elements an iteratee has consumed:------ > snd <$> enumWith (dropWhile (<5)) length------ Compare to @zipStreams@-enumWith-  :: Monad m-  => Iteratee [el] m a-  -> Iteratee [el] m b-  -> Iteratee [el] m (a, b)-enumWith i1 i2 = do-    -- as with zipStreams, first check to see if the initial iteratee is complete,-    -- otherwise data would be dropped.-    -- running the second iteratee as well to prevent a monadic effect mismatch-    -- although I think that would be highly unlikely to happen in common-    -- code-    (a', x') <- lift $ runIter i1 od oc-    (_,  y') <- lift $ runIter i2 od oc-    case a' of-      Just (a, s) -> flip idone s =<< lift (liftM ((,) a) $ run i2)-      Nothing     -> go x' y'-  where-    od a s = return (Just (a, s), idone a s)-    oc k e = return (Nothing    , icont k e)--    getUsed xs (Chunk ys) = take (length xs - length ys) xs-    getUsed xs (EOF _)    = xs--    go x y = liftI step-      where-        step (Chunk xs) | nullC xs = liftI step-        step (Chunk xs) = do-          (a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x-          case a' of-            Just (a, s) -> do-              b <- lift $ run =<< enumPure1Chunk (getUsed xs s) y-              idone (a, b) s-            Nothing        -> lift (enumPure1Chunk xs y) >>= go x'-        step (EOF err) = joinIM $ case err of-          Nothing -> (liftM2.liftM2) (,) (enumEof   x) (enumEof   y)-          Just e  -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)-{-# INLINE enumWith #-}---- |Enumerate a list of iteratees over a single stream simultaneously--- and discard the results. This is a different behavior than Prelude's--- sequence_ which runs iteratees in the list one after the other.------ Compare to @Prelude.sequence_@.-sequenceStreams_-  :: Monad m-  => [Iteratee [el] m a]-  -> Iteratee [el] m ()-sequenceStreams_ = self-  where-    self is = liftI step-      where-        step (Chunk xs) | null xs = liftI step-        step s@(Chunk _) = do-          -- give a chunk to each iteratee-          is'  <- lift $ mapM (enumChunk s) is-          -- filter done iteratees-          (done, notDone) <- lift $ partition fst `liftM` mapM enumCheckIfDone is'-          if null notDone-            then idone () <=< remainingStream $ map snd done-            else self $ map snd notDone-        step s@(EOF _) = do-          s' <- remainingStream <=< lift $ mapM (enumChunk s) is-          case s' of-            EOF (Just e) -> throwErr e-            _            -> idone () s'--    -- returns the unconsumed part of the stream; "sequenceStreams_ is" consumes as-    -- much of the stream as the iteratee in is that consumes the most; e.g.-    -- sequenceStreams_ [I.head, I.last] consumes whole stream-    remainingStream :: Monad m => [Iteratee [el] m a] -> Iteratee [el] m (Stream [el])-    remainingStream is = lift $-      return . foldl1 shorter <=< mapM (\i -> runIter i od oc) $ is-      where-        od _ s = return s-        oc _ e = return $ case e of-          Nothing -> mempty-          _       -> EOF e--    -- return the shorter one of two streams; errors are propagated with the-    -- priority given to the "left"-    shorter c1@(Chunk xs) c2@(Chunk ys)-      | length xs < length ys = c1-      | otherwise                   = c2-    shorter (EOF e1 ) (EOF e2 ) = EOF (e1 `mplus` e2)-    shorter e@(EOF _) _         = e-    shorter _         e@(EOF _) = e---- |Transform an iteratee into one that keeps track of how much data it--- consumes.-countConsumed :: (Monad m, Integral n) => Iteratee [el] m a -> Iteratee [el] m (a, n)-countConsumed i = go 0 (const i) (Chunk emptyP)-  where-    go !n f str@(EOF _) = flip (,) n `liftM` f str-    go !n f str@(Chunk c) = Iteratee rI-      where-        newLen = n + fromIntegral (length c)-        rI od oc = runIter (f str) onDone onCont-          where-            onDone a str'@(Chunk c') =-                od (a, newLen - fromIntegral (length c')) str'-            onDone a str'@(EOF _) = od (a, n) str'-            onCont f' = oc (go newLen f')-{-# INLINE countConsumed #-}---- --------------------------------------------------------------------------- Enumerators---- |The pure n-chunk enumerator--- It passes a given stream of elements to the iteratee in @n@-sized chunks.-enumPureNChunk :: Monad m => [el] -> Int -> Enumerator [el] m a-enumPureNChunk str n iter-  | null str = return iter-  | n > 0       = enum' str iter-  | otherwise   = error $ "enumPureNChunk called with n==" ++ show n-  where-    enum' str' iter'-      | null str' = return iter'-      | otherwise    = let (s1, s2) = splitAt n str'-                           on_cont k Nothing = enum' s2 . k $ Chunk s1-                           on_cont k e = return $ icont k e-                       in runIter iter' idoneM on_cont-{-# INLINE enumPureNChunk #-}---- --------------------------------------------------------------------------- Monadic functions---- | Maps a monadic function over the elements of the stream and ignores--- the result.-mapStreamM_ :: Monad m => (el -> m b) -> Iteratee [el] m ()-mapStreamM_ = mapChunksM_ . mapM_-{-# INLINE mapStreamM_ #-}---- | Maps a monadic function over an 'Iteratee'.-mapStreamM :: Monad m => (el -> m el') -> Enumeratee [el] [el'] m a-mapStreamM = mapChunksM . mapM-{-# INLINE mapStreamM #-}---- | Folds a monadic function over an 'Iteratee'.-foldStreamM :: Monad m => (b -> a -> m b) -> b -> Iteratee [a] m b-foldStreamM = foldChunksM . foldM-{-# INLINE foldStreamM #-}
− src/Bio/Iteratee/ZLib.hsc
@@ -1,741 +0,0 @@--- Stolen from iteratee-compress module, which doesn't work due to--- dependency problems.  Modified for proper early-out behaviour.-module Bio.Iteratee.ZLib-  (-    -- * Enumeratees-    enumInflate,-    enumInflateAny,-    enumDeflate,-    -- * Exceptions-    ZLibParamsException(..),-    ZLibException(..),-    -- * Parameters-    CompressParams(..),-    defaultCompressParams,-    DecompressParams(..),-    defaultDecompressParams,-    Format(..),-    CompressionLevel(..),-    Method(..),-    WindowBits(..),-    MemoryLevel(..),-    CompressionStrategy(..),-    enumSyncFlush,-    enumFullFlush,-    enumBlockFlush,-  )-where-#include <zlib.h>--import Bio.Iteratee-import Control.Applicative-import Control.Exception-import Control.Monad ( liftM, liftM2 )-import Data.ByteString as BS-import Data.ByteString.Internal-import Data.Foldable-import Data.Typeable-import Foreign-import Foreign.C-import Prelude-#ifdef DEBUG-import qualified Foreign.Concurrent as C-import System.IO (stderr)-import qualified System.IO as IO-#endif---- | Denotes error is user-supplied parameter-data ZLibParamsException-    = IncorrectCompressionLevel !Int-    -- ^ Incorrect compression level was chosen-    | IncorrectWindowBits !Int-    -- ^ Incorrect number of window bits was chosen-    | IncorrectMemoryLevel !Int-    -- ^ Incorrect memory level was chosen-    deriving (Eq,Typeable)---- | Denotes error in compression and decompression-data ZLibException-    = NeedDictionary-    -- ^ Decompression requires user-supplied dictionary (not supported)-    | BufferError-    -- ^ Buffer error - denotes a library error---    | File Error-    | StreamError-    -- ^ State of steam inconsistent-    | DataError-    -- ^ Input data corrupted-    | MemoryError-    -- ^ Not enough memory-    | VersionError-    -- ^ Version error-    | Unexpected !CInt-    -- ^ Unexpected or unknown error - please report as bug-    | IncorrectState-    -- ^ Incorrect state - denotes error in library-    deriving (Eq,Typeable)---- | Denotes the flush that can be sent to stream-data ZlibFlush-    = SyncFlush-    -- ^ All pending output is flushed and all input that is available is sent-    -- to inner Iteratee.-    | FullFlush-    -- ^ Flush all pending output and reset the compression state. It allows to-    -- restart from this point if compression was damaged but it can seriously-    -- affect the compression rate.-    ---    -- It may be only used during compression.-    | Block-    -- ^ If the iteratee is compressing it requests to stop when next block is-    -- emmited. On the beginning it skips only header if and only if it exists.-    deriving (Eq,Typeable)--instance Show ZlibFlush where-    show SyncFlush = "zlib: flush requested"-    show FullFlush = "zlib: full flush requested"-    show Block = "zlib: block flush requested"--instance Exception ZlibFlush--fromFlush :: ZlibFlush -> CInt-fromFlush SyncFlush = #{const Z_SYNC_FLUSH}-fromFlush FullFlush = #{const Z_FULL_FLUSH}-fromFlush Block = #{const Z_BLOCK}--instance Show ZLibParamsException where-    show (IncorrectCompressionLevel lvl)-        = "zlib: incorrect compression level " ++ show lvl-    show (IncorrectWindowBits lvl)-        = "zlib: incorrect window bits " ++ show lvl-    show (IncorrectMemoryLevel lvl)-        = "zlib: incorrect memory level " ++ show lvl--instance Show ZLibException where-    show NeedDictionary = "zlib: needs dictionary"-    show BufferError = "zlib: no progress is possible (internal error)"---    show FileError = "zlib: file I/O error"-    show StreamError = "zlib: stream error"-    show DataError = "zlib: data error"-    show MemoryError = "zlib: memory error"-    show VersionError = "zlib: version error"-    show (Unexpected lvl) = "zlib: unknown error " ++ show lvl-    show IncorrectState = "zlib: incorrect state"--instance Exception ZLibParamsException-instance Exception ZLibException--newtype ZStream = ZStream (ForeignPtr ZStream)-withZStream :: ZStream -> (Ptr ZStream -> IO a) -> IO a-withZStream (ZStream fptr) = withForeignPtr fptr----- Following code is copied from Duncan Coutts zlib haskell library version--- 0.5.2.0 ((c) 2006-2008 Duncan Coutts, published on BSD licence) and adapted---- | Set of parameters for compression. For sane defaults use--- 'defaultCompressParams'-data CompressParams = CompressParams {-      compressLevel :: !CompressionLevel,-      compressMethod :: !Method,-      compressWindowBits :: !WindowBits,-      compressMemoryLevel :: !MemoryLevel,-      compressStrategy :: !CompressionStrategy,-      -- | The size of output buffer. That is the size of 'Chunk's that will be-      -- emitted to inner iterator (except the last 'Chunk').-      compressBufferSize :: !Int,-      compressDictionary :: !(Maybe ByteString)-    }--defaultCompressParams :: CompressParams-defaultCompressParams-    = CompressParams DefaultCompression Deflated DefaultWindowBits-                     DefaultMemoryLevel DefaultStrategy (8*1024) Nothing---- | Set of parameters for decompression. For sane defaults see--- 'defaultDecompressParams'.-data DecompressParams = DecompressParams {-      -- | Window size - it have to be at least the size of-      -- 'compressWindowBits' the stream was compressed with.-      ---      -- Default in 'defaultDecompressParams' is the maximum window size --      -- please do not touch it unless you know what you are doing.-      decompressWindowBits :: !WindowBits,-      -- | The size of output buffer. That is the size of 'Chunk's that will be-      -- emitted to inner iterator (except the last 'Chunk').-      decompressBufferSize :: !Int,-      decompressDictionary :: !(Maybe ByteString)-    }--defaultDecompressParams :: DecompressParams-defaultDecompressParams = DecompressParams DefaultWindowBits (8*1024) Nothing---- | Specify the format for compression and decompression-data Format-    = GZip-    -- ^ The gzip format is widely used and uses a header with checksum and-    -- some optional metadata about the compress file.-    ---    -- It is intended primarily for compressing individual files but is also-    -- used for network protocols such as HTTP.-    ---    -- The format is described in RFC 1952-    -- <http://www.ietf.org/rfc/rfc1952.txt>.-    | Zlib-    -- ^ The zlib format uses a minimal header with a checksum but no other-    -- metadata. It is designed for use in network protocols.-    ---    -- The format is described in RFC 1950-    -- <http://www.ietf.org/rfc/rfc1950.txt>-    | Raw-    -- ^ The \'raw\' format is just the DEFLATE compressed data stream without-    -- and additionl headers.-    ---    -- Thr format is described in RFC 1951-    -- <http://www.ietf.org/rfc/rfc1951.txt>-    | GZipOrZlib-    -- ^ "Format" for decompressing a 'Zlib' or 'GZip' stream.-    deriving (Eq)---- | The compression level specify the tradeoff between speed and compression.-data CompressionLevel-    = DefaultCompression-    -- ^ Default compression level set at 6.-    | NoCompression-    -- ^ No compression, just a block copy.-    | BestSpeed-    -- ^ The fastest compression method (however less compression)-    | BestCompression-    -- ^ The best compression method (however slowest)-    | CompressionLevel Int-    -- ^ Compression level set by number from 1 to 9---- | Specify the compression method.-data Method-    = Deflated-    -- ^ \'Deflate\' is so far the only method supported.---- | This specify the size of compression level. Larger values result in better--- compression at the expense of highier memory usage.------ The compression window size is 2 to the power of the value of the window--- bits.------ The total memory used depends on windows bits and 'MemoryLevel'.-data WindowBits-    = WindowBits Int-    -- ^ The size of window bits. It have to be between @8@ (which corresponds-    -- to 256b i.e. 32B) and @15@ (which corresponds to 32 kib i.e. 4kiB).-    | DefaultWindowBits-    -- ^ The default window size which is 4kiB---- | The 'MemoryLevel' specifies how much memory should be allocated for the--- internal state. It is a tradeoff between memory usage, speed and--- compression.--- Using more memory allows faster and better compression.------ The memory used for interal state, excluding 'WindowBits', is 512 bits times--- 2 to power of memory level.------ The total amount of memory use depends on the 'WindowBits' and--- 'MemoryLevel'.-data MemoryLevel-    = DefaultMemoryLevel-    -- ^ Default memory level set to 8.-    | MinMemoryLevel-    -- ^ Use the small amount of memory (equivalent to memory level 1) - i.e.-    -- 1024b or 256 B.-    -- It slow and reduces the compresion ratio.-    | MaxMemoryLevel-    -- ^ Maximum memory level for optimal compression speed (equivalent to-    -- memory level 9).-    -- The internal state is 256kib or 32kiB.-    | MemoryLevel Int-    -- ^ A specific level. It have to be between 1 and 9.---- | Tunes the compress algorithm but does not affact the correctness.-data CompressionStrategy-    = DefaultStrategy-    -- ^ Default strategy-    | Filtered-    -- ^ Use the filtered compression strategy for data produced by a filter-    -- (or predictor). Filtered data consists mostly of small values with a-    -- somewhat random distribution. In this case, the compression algorithm-    -- is tuned to compress them better. The effect of this strategy is to-    -- force more Huffman coding and less string matching; it is somewhat-    -- intermediate between 'DefaultStrategy' and 'HuffmanOnly'.-    | HuffmanOnly-    -- ^ Use the Huffman-only compression strategy to force Huffman encoding-    -- only (no string match).--fromMethod :: Method -> CInt-fromMethod Deflated = #{const Z_DEFLATED}--fromCompressionLevel :: CompressionLevel -> Either ZLibParamsException CInt-fromCompressionLevel DefaultCompression = Right $! -1-fromCompressionLevel NoCompression = Right $! 0-fromCompressionLevel BestSpeed = Right $! 1-fromCompressionLevel BestCompression = Right $! 9-fromCompressionLevel (CompressionLevel n)-    | n >= 0 && n <= 9 = Right $! fromIntegral $! n-    | otherwise = Left $! IncorrectCompressionLevel n--fromWindowBits :: Format -> WindowBits -> Either ZLibParamsException CInt-fromWindowBits format bits-    = formatModifier format <$> checkWindowBits bits-    where checkWindowBits DefaultWindowBits = Right $! 15-          checkWindowBits (WindowBits n)-              | n >= 8 && n <= 15 = Right $! fromIntegral $! n-              | otherwise = Left $! IncorrectWindowBits $! n-          formatModifier Zlib       = id-          formatModifier GZip       = (+16)-          formatModifier GZipOrZlib = (+32)-          formatModifier Raw        = negate--fromMemoryLevel :: MemoryLevel -> Either ZLibParamsException CInt-fromMemoryLevel DefaultMemoryLevel = Right $! 8-fromMemoryLevel MinMemoryLevel     = Right $! 1-fromMemoryLevel MaxMemoryLevel     = Right $! 9-fromMemoryLevel (MemoryLevel n)-         | n >= 1 && n <= 9 = Right $! fromIntegral n-         | otherwise        = Left $! IncorrectMemoryLevel $! fromIntegral n--fromCompressionStrategy :: CompressionStrategy -> CInt-fromCompressionStrategy DefaultStrategy = #{const Z_DEFAULT_STRATEGY}-fromCompressionStrategy Filtered        = #{const Z_FILTERED}-fromCompressionStrategy HuffmanOnly     = #{const Z_HUFFMAN_ONLY}--fromErrno :: CInt -> Either ZLibException Bool-fromErrno (#{const Z_OK}) = Right $! True-fromErrno (#{const Z_STREAM_END}) = Right $! False-fromErrno (#{const Z_NEED_DICT}) = Left $! NeedDictionary-fromErrno (#{const Z_BUF_ERROR}) = Left $! BufferError---fromErrno (#{const Z_ERRNO}) = Left $! FileError-fromErrno (#{const Z_STREAM_ERROR}) = Left $! StreamError-fromErrno (#{const Z_DATA_ERROR}) = Left $! DataError-fromErrno (#{const Z_MEM_ERROR}) = Left $! MemoryError-fromErrno (#{const Z_VERSION_ERROR}) = Left $! VersionError-fromErrno n = Left $! Unexpected n---- Helper function-convParam :: Format-          -> CompressParams-          -> Either ZLibParamsException (CInt, CInt, CInt, CInt, CInt)-convParam f (CompressParams c m w l s _ _)-    = let c' = fromCompressionLevel c-          m' = fromMethod m-          b' = fromWindowBits f w-          l' = fromMemoryLevel l-          s' = fromCompressionStrategy s-          eit = either Left-          r = Right-      in eit (\c_ -> eit (\b_ -> eit (\l_ -> r (c_, m', b_, l_, s')) l') b') c'---- In following code we go through 7 states. Some of the operations are--- 'deterministic' like 'insertOut' and some of them depends on input ('fill')--- or library call.------                                                  (Finished)---                                                     ^---                                                     |---                                                     |---                                                     | finish---                                                     |---              insertOut                fill[1]       |----  (Initial) -------------> (EmptyIn) -----------> (Finishing)---         ^                    ^ | ^ |---         |             run[2] | | | \------------------\---         |                    | | |                    |---         |                    | | \------------------\ |---         |    run[1]          | |        flush[0]    | |---         \------------------\ | | fill[0]            | | fill[3]---                            | | |                    | |---                            | | |                    | |---               swapOut      | | v       flush[1]     | v---  (FullOut) -------------> (Invalid) <----------- (Flushing)------ Initial: Initial state, both buffers are empty--- EmptyIn: Empty in buffer, out waits untill filled--- FullOut: Out was filled and sent. In was not entirely read--- Invalid[1]: Both buffers non-empty--- Finishing: There is no more in data and in buffer is empty. Waits till---    all outs was sent.--- Finished: Operation finished--- Flushing: Flush requested------ Please note that the decompressing can finish also on flush and finish.------ [1] Named for 'historical' reasons--newtype Initial = Initial ZStream-data EmptyIn = EmptyIn !ZStream !ByteString-data FullOut = FullOut !ZStream !ByteString-data Invalid = Invalid !ZStream !ByteString !ByteString-data Finishing = Finishing !ZStream !ByteString-data Flushing = Flushing !ZStream !ZlibFlush !ByteString--withByteString :: ByteString -> (Ptr Word8 -> Int -> IO a) -> IO a-withByteString (PS ptr off len) f-    = withForeignPtr ptr (\ptr' -> f (ptr' `plusPtr` off) len)--#ifdef DEBUG-mkByteString :: MonadIO m => Int -> m ByteString-mkByteString s = liftIO $ do-    base <- mallocForeignPtrArray s-    withForeignPtr base $ \ptr ->  C.addForeignPtrFinalizer base $ do-        IO.hPutStrLn stderr $ "Freed buffer " ++ show ptr-    IO.hPutStrLn stderr $ "Allocated buffer " ++ show base-    return $! PS base 0 s--dumpZStream :: ZStream -> IO ()-dumpZStream zstr = withZStream zstr $ \zptr -> do-    IO.hPutStr stderr $ "<<ZStream@"-    IO.hPutStr stderr $ (show zptr)-    IO.hPutStr stderr . (" next_in=" ++) . show =<<-        (#{peek z_stream, next_in} zptr :: IO (Ptr ()))-    IO.hPutStr stderr . (" avail_in=" ++) . show =<<-        (#{peek z_stream, avail_in} zptr :: IO CUInt)-    IO.hPutStr stderr . (" total_in=" ++) . show =<<-        (#{peek z_stream, total_in} zptr :: IO CULong)-    IO.hPutStr stderr . (" next_out=" ++) . show =<<-        (#{peek z_stream, next_out} zptr :: IO (Ptr ()))-    IO.hPutStr stderr . (" avail_out=" ++) . show =<<-        (#{peek z_stream, avail_out} zptr :: IO CUInt)-    IO.hPutStr stderr .  (" total_out=" ++) . show =<<-        (#{peek z_stream, total_out} zptr :: IO CULong)---    IO.hPutStr stderr . (" msg=" ++) =<< peekCString =<<---        (#{peek z_stream, msg} zptr)-    IO.hPutStrLn stderr ">>"-#else-mkByteString :: MonadIO m => Int -> m ByteString-mkByteString s = liftIO $ create s (\_ -> return ())-#endif--putOutBuffer :: Int -> ZStream -> IO ByteString-putOutBuffer size zstr = do-    _out <- mkByteString size-    withByteString _out $ \ptr len -> withZStream zstr $ \zptr -> do-        #{poke z_stream, next_out} zptr ptr-        #{poke z_stream, avail_out} zptr len-    return _out--putInBuffer :: ZStream -> ByteString -> IO ()-putInBuffer zstr _in-    = withByteString _in $ \ptr len -> withZStream zstr $ \zptr -> do-        #{poke z_stream, next_in} zptr ptr-        #{poke z_stream, avail_in} zptr len--pullOutBuffer :: ZStream -> ByteString -> IO ByteString-pullOutBuffer zstr _out = withByteString _out $ \ptr _ -> do-    next_out <- withZStream zstr $ \zptr -> #{peek z_stream, next_out} zptr-    return $! BS.take (next_out `minusPtr` ptr) _out--pullInBuffer :: ZStream -> ByteString -> IO ByteString-pullInBuffer zstr _in = withByteString _in $ \ptr _ -> do-    next_in <- withZStream zstr $ \zptr -> #{peek z_stream, next_in} zptr-    return $! BS.drop (next_in `minusPtr` ptr) _in--type EnumerateeS eli elo m a = (Stream eli -> Iteratee eli m a) -> Iteratee elo m (Iteratee eli m a)--eneeErr :: (Monad m, Exception err, Nullable elo)-        => (Stream eli -> Iteratee eli m a) -> err -> Iteratee elo m ()-eneeErr iter = liftM (const ()) . lift . run . iter . EOF . Just . toException--insertOut :: MonadIO m-          => Int-          -> (ZStream -> CInt -> IO CInt)-          -> Initial-          -> Enumeratee ByteString ByteString m a-insertOut size runf (Initial zstr) iter = do-    _out <- liftIO $ putOutBuffer size zstr-#ifdef DEBUG-    liftIO $ IO.hPutStrLn stderr $ "Inserted out buffer of size " ++ show size-#endif-    eneeCheckIfDone (fill size runf (EmptyIn zstr _out)) iter--fill :: MonadIO m-     => Int-     -> (ZStream -> CInt -> IO CInt)-     -> EmptyIn-     -> EnumerateeS ByteString ByteString m a-fill size run' (EmptyIn zstr _out) iter-    = let fill' (Chunk _in)-              | not (BS.null _in) = do-                  liftIO $ putInBuffer zstr _in-#ifdef DEBUG-                  liftIO $ IO.hPutStrLn stderr $-                      "Inserted in buffer of size " ++ show (BS.length _in)-#endif-                  doRun size run' (Invalid zstr _in _out) iter-              | otherwise = fillI-          fill' (EOF Nothing) = do-              out <- liftIO $ pullOutBuffer zstr _out-              eneeCheckIfDone (finish size run' (Finishing zstr BS.empty)) $ iter (Chunk out)-          fill' (EOF (Just err))-              = case fromException err of-                  Just err' -> flush size run' (Flushing zstr err' _out) iter-                  Nothing -> throwRecoverableErr err fill'-#ifdef DEBUG-          fillI = do-              liftIO $ IO.hPutStrLn stderr $ "About to insert in buffer"-              liftI fill'-#else-          fillI = liftI fill'-#endif-      in fillI--swapOut :: MonadIO m-        => Int-        -> (ZStream -> CInt -> IO CInt)-        -> FullOut-        -> Enumeratee ByteString ByteString m a-swapOut size run' (FullOut zstr _in) iter = do-    _out <- liftIO $ putOutBuffer size zstr-#ifdef DEBUG-    liftIO $ IO.hPutStrLn stderr $ "Swapped out buffer of size " ++ show size-#endif-    eneeCheckIfDone (doRun size run' (Invalid zstr _in _out)) iter--doRun :: MonadIO m-      => Int-      -> (ZStream -> CInt -> IO CInt)-      -> Invalid-      -> EnumerateeS ByteString ByteString m a-doRun size run' (Invalid zstr _in _out) iter = do-#ifdef DEBUG-    liftIO $ IO.hPutStrLn stderr $ "About to run"-    liftIO $ dumpZStream zstr-#endif-    status <- liftIO $ run' zstr #{const Z_NO_FLUSH}-#ifdef DEBUG-    liftIO $ IO.hPutStrLn stderr $ "Runned"-#endif-    case fromErrno status of-        Left err -> do-            eneeErr iter err-            throwErr (toException err)-        Right False -> do -- End of stream-            remaining <- liftIO $ pullInBuffer zstr _in-            out <- liftIO $ pullOutBuffer zstr _out-            idone (iter (Chunk out)) (Chunk remaining)-        Right True -> do -- Continue-            (avail_in, avail_out) <- liftIO $ withZStream zstr $ \zptr -> do-                avail_in <- liftIO $ #{peek z_stream, avail_in} zptr-                avail_out <- liftIO $ #{peek z_stream, avail_out} zptr-                return (avail_in, avail_out) :: IO (CInt, CInt)-            case avail_out of-                0 -> do-                    out <- liftIO $ pullOutBuffer zstr _out-                    case avail_in of-                        0 -> insertOut size run' (Initial zstr) $ iter (Chunk out)-                        _ -> swapOut size run' (FullOut zstr _in) $ iter (Chunk out)-                _ -> case avail_in of-                    0 -> fill size run' (EmptyIn zstr _out) iter-                    _ -> do-                        eneeErr iter IncorrectState-                        throwErr (toException IncorrectState)--flush :: MonadIO m-      => Int-      -> (ZStream -> CInt -> IO CInt)-      -> Flushing-      -> EnumerateeS ByteString ByteString m a-flush size run' (Flushing zstr _flush _out) iter = do-    status <- liftIO $ run' zstr (fromFlush _flush)-    case fromErrno status of-        Left err -> do-            eneeErr iter err-            throwErr (toException err)-        Right False -> do -- Finished-            out <- liftIO $ pullOutBuffer zstr _out-            idone (iter (Chunk out)) (Chunk BS.empty)-        Right True -> do-            avail_out <- liftIO $ withZStream zstr #{peek z_stream, avail_out}-            case avail_out :: CInt of-                0 -> do-                    out <- liftIO $ pullOutBuffer zstr _out-                    out' <- liftIO $ putOutBuffer size zstr-                    eneeCheckIfDone (flush size run' (Flushing zstr _flush out')) $ iter (Chunk out)-                _ -> insertOut size run' (Initial zstr) (liftI iter)--finish :: MonadIO m-       => Int-       -> (ZStream -> CInt -> IO CInt)-       -> Finishing-       -> EnumerateeS ByteString ByteString m a-finish size run' fin@(Finishing zstr _in) iter = do-#ifdef DEBUG-    liftIO $ IO.hPutStrLn stderr $-        "Finishing with out buffer of size " ++ show size-#endif-    _out <- liftIO $ putOutBuffer size zstr-    status <- liftIO $ run' zstr #{const Z_FINISH}-    case fromErrno status of-        Left err -> do-            eneeErr iter err-            throwErr (toException err)-        Right False -> do -- Finished-            remaining <- liftIO $ pullInBuffer zstr _in-            out <- liftIO $ pullOutBuffer zstr _out-            idone (iter (Chunk out)) (Chunk remaining)-        Right True -> do-            avail_out <- liftIO $ withZStream zstr #{peek z_stream, avail_out}-            case avail_out :: CInt of-                0 -> do-                    out <- liftIO $ pullOutBuffer zstr _out-                    eneeCheckIfDone (finish size run' fin) $ iter (Chunk out)-                _ -> do-                    eneeErr iter IncorrectState-                    throwErr $! toException IncorrectState--foreign import ccall unsafe deflateInit2_ :: Ptr ZStream -> CInt -> CInt-                                          -> CInt -> CInt -> CInt-                                          -> CString -> CInt -> IO CInt-foreign import ccall unsafe inflateInit2_ :: Ptr ZStream -> CInt-                                          -> CString -> CInt -> IO CInt-foreign import ccall unsafe inflate :: Ptr ZStream -> CInt -> IO CInt-foreign import ccall unsafe deflate :: Ptr ZStream -> CInt -> IO CInt-foreign import ccall unsafe "&deflateEnd"-                              deflateEnd :: FunPtr (Ptr ZStream -> IO ())-foreign import ccall unsafe "&inflateEnd"-                              inflateEnd :: FunPtr (Ptr ZStream -> IO ())-foreign import ccall unsafe deflateSetDictionary :: Ptr ZStream -> Ptr Word8-                                                 -> CUInt -> IO CInt-foreign import ccall unsafe inflateSetDictionary :: Ptr ZStream -> Ptr Word8-                                                 -> CUInt -> IO CInt--deflateInit2 :: Ptr ZStream -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt-deflateInit2 s l m wB mL s'-    = withCString #{const_str ZLIB_VERSION} $ \v ->-        deflateInit2_ s l m wB mL s' v #{size z_stream}--inflateInit2 :: Ptr ZStream -> CInt -> IO CInt-inflateInit2 s wB-    = withCString #{const_str ZLIB_VERSION} $ \v ->-        inflateInit2_ s wB v #{size z_stream}--#ifdef DEBUG-deflate' :: ZStream -> CInt -> IO CInt-deflate' z f = withZStream z $ \p -> do-    IO.hPutStrLn stderr "About to run deflate"-    deflate p f--inflate' :: ZStream -> CInt -> IO CInt-inflate' z f = withZStream z $ \p -> do-    IO.hPutStrLn stderr "About to run inflate"-    inflate p f-#else-deflate' :: ZStream -> CInt -> IO CInt-deflate' z f = withZStream z $ \p -> deflate p f--inflate' :: ZStream -> CInt -> IO CInt-inflate' z f = withZStream z $ \p -> inflate p f-#endif--mkCompress :: Format -> CompressParams-           -> IO (Either ZLibParamsException Initial)-mkCompress frm cp-    = case convParam frm cp of-        Left err -> return $! Left err-        Right (c, m, b, l, s) -> do-            zstr <- mallocForeignPtrBytes #{size z_stream}-            withForeignPtr zstr $ \zptr -> do-                _ <- memset (castPtr zptr) 0 #{size z_stream}-                _ <- deflateInit2 zptr c m b l s `finally`-                        addForeignPtrFinalizer deflateEnd zstr-                for_ (compressDictionary cp) $ \(PS fp off len) ->-                    withForeignPtr fp $ \ptr ->-                        deflateSetDictionary zptr (ptr `plusPtr` off)-                                                  (fromIntegral len)-            return $! Right $! Initial $ ZStream zstr--mkDecompress :: Format -> DecompressParams-             -> IO (Either ZLibParamsException (Initial, Maybe ByteString))-mkDecompress frm (DecompressParams w _ md)-    = case fromWindowBits frm w of-        Left err -> return $! Left err-        Right wB' -> do-            zstr <- mallocForeignPtrBytes #{size z_stream}-            v <- withForeignPtr zstr $ \zptr -> do-                _ <- memset (castPtr zptr) 0 #{size z_stream}-                _ <- inflateInit2 zptr wB' `finally`-                        addForeignPtrFinalizer inflateEnd zstr-                case (md, frm) of-                    (Just (PS fp off len), Raw) -> do-                        _ <- withForeignPtr fp $ \ptr ->-                                inflateSetDictionary zptr (ptr `plusPtr` off)-                                                          (fromIntegral len)-                        return Nothing-                    (Nothing, _) -> return $! Nothing-                    (Just bs, _) -> return $! (Just bs)-            return $! Right $! (Initial $ ZStream zstr, v)---- User-related code---- | Compress the input and send to inner iteratee.-enumDeflate :: MonadIO m-            => Format -- ^ Format of input-            -> CompressParams -- ^ Parameters of compression-            -> Enumeratee ByteString ByteString m a-enumDeflate f cp@(CompressParams _ _ _ _ _ size _) iter = do-    cmp <- liftIO $ mkCompress f cp-    case cmp of-        Left err -> do-            _ <- lift $ enumErr err iter-            throwErr (toException err)-        Right init' -> insertOut size deflate' init' iter---- | Decompress the input and send to inner iteratee. If there is data--- after the end of zlib stream, it is left unprocessed.-enumInflate :: MonadIO m-            => Format-            -> DecompressParams-            -> Enumeratee ByteString ByteString m a-enumInflate f dp@(DecompressParams _ size _md) iter = do-    dcmp <- liftIO $ mkDecompress f dp-    case dcmp of-        Left err -> do-            _ <- lift $ enumErr err iter-            throwErr (toException err)-        Right (init', Nothing) -> insertOut size inflate' init' iter-        Right (init', (Just (PS fp off len))) ->-            let inflate'' zstr param = do-                  ret <- inflate' zstr param-                  case fromErrno ret of-                      Left NeedDictionary -> do-                          _ <- withForeignPtr fp $ \ptr ->-                                  withZStream zstr $ \zptr ->-                                      inflateSetDictionary zptr (ptr `plusPtr` off)-                                                                (fromIntegral len)-                          inflate' zstr param-                      _ -> return ret-            in insertOut size inflate'' init' iter---- | Inflate if Gzip format is recognized, otherwise pass through.-enumInflateAny :: MonadIO m => Enumeratee ByteString ByteString m a-enumInflateAny it = do magic <- iLookAhead $ liftM2 (,) tryHeadBS tryHeadBS-                       case magic of-                           (Just 0x1f, Just 0x8b) ->-                               enumInflate GZip defaultDecompressParams it-                               >>= enumInflateAny-                           _ -> mapChunks id it--enumSyncFlush :: Monad m => Enumerator ByteString m a--- ^ Enumerate synchronise flush. It cause the all pending output to be flushed--- and all available input is sent to inner Iteratee.-enumSyncFlush = enumErr SyncFlush--enumFullFlush :: Monad m => Enumerator ByteString m a--- ^ Enumerate full flush. It flushes all pending output and reset the--- compression. It allows to restart from this point if compressed data was--- corrupted but it can affect the compression rate.------ It may be only used during compression.-enumFullFlush = enumErr FullFlush--enumBlockFlush :: Monad m => Enumerator ByteString m a--- ^ Enumerate block flush. If the enumerator is compressing it allows to--- finish current block. If the enumerator is decompressing it forces to stop--- on next block boundary.-enumBlockFlush = enumErr Block-
− src/Bio/Prelude.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE CPP #-}-module Bio.Prelude (-    module Bio.Base,-    module BasePrelude,-    module Data.List.NonEmpty,-    module Data.Semigroup,-    module System.IO,-    module System.Posix.Files,-    module System.Posix.IO,-    module System.Posix.Types,--    Bytes, LazyBytes,-    HashMap,-    HashSet,-    IntMap,-    IntSet,-    Text, LazyText,-    Pair(..),--#if !MIN_VERSION_base(4,8,0)-    first,-    second,-#endif--    decodeBytes,-    encodeBytes,--    Hashable(..),-    Unpack(..),-    fdGet,-    fdPut,-    fdPutLazy,-    withFd-                   ) where--import BasePrelude-#if MIN_VERSION_base(4,9,0)-                    hiding ( (<>), EOF, log1p, log1pexp, log1mexp, expm1 )-#else-                    hiding ( (<>), EOF )-#endif--#if !MIN_VERSION_base(4,8,0)--- Not as nice as Data.Bifunctor, but still useful.-import Control.Arrow       ( first, second )-#endif--import Bio.Base-import Data.ByteString     ( ByteString )-import Data.ByteString.Internal ( createAndTrim )-import Data.List.NonEmpty  ( NonEmpty(..) )-import Data.Semigroup      ( Semigroup(..) )-import Data.Text           ( Text )-import Data.Hashable       ( Hashable(..) )-import Data.HashMap.Strict ( HashMap )-import Data.HashSet        ( HashSet )-import Data.IntMap         ( IntMap )-import Data.IntSet         ( IntSet )-import Data.Text.Encoding  ( encodeUtf8, decodeUtf8With )-import Foreign.C.Error     ( throwErrnoIf_ )-import System.IO           ( hPrint, hPutStr, hPutStrLn, stderr, stdout, stdin )-import System.Posix.Files-import System.Posix.IO-import System.Posix.Types--import qualified Data.ByteString.Unsafe as B-import qualified Data.ByteString.Lazy   as BL-import qualified Data.ByteString.Char8  as S-import qualified Data.Text              as T-import qualified Data.Text.Lazy         as TL--type Bytes     =    ByteString-type LazyBytes = BL.ByteString-type LazyText  = TL.Text--infixl 2 :!:---- | A strict pair.-data Pair a b = !a :!: !b deriving(Eq, Ord, Show, Read, Bounded, Ix)---- | Class of things that can be unpacked into 'String's.  Kind of the--- opposite of 'IsString'.-class Unpack s where unpack :: s -> String--instance Unpack ByteString where unpack = S.unpack-instance Unpack Text       where unpack = T.unpack-instance Unpack String     where unpack = id---- | @fdGet bs fd@ reads up to @bs@ 'Bytes' from file descriptor @Fd@.--- Returns an empty 'Bytes' at end of file.-fdGet :: Int -> Fd -> IO Bytes-fdGet bs fd =-    createAndTrim bs $ \p ->-        fromIntegral <$> fdReadBuf fd (castPtr p) (fromIntegral bs)--fdPut :: Fd -> Bytes -> IO ()-fdPut fd s = B.unsafeUseAsCStringLen s $ \(p,l) ->-             throwErrnoIf_ (/= fromIntegral l) "fdPut" $-             fdWriteBuf fd (castPtr p) (fromIntegral l)--fdPutLazy :: Fd -> LazyBytes -> IO ()-fdPutLazy fd = mapM_ (fdPut fd) . BL.toChunks--withFd :: FilePath -> OpenMode -> Maybe FileMode -> OpenFileFlags-       -> (Fd -> IO a) -> IO a-withFd fp om fm ff = bracket (openFd fp om fm ff) closeFd---- | Converts 'Bytes' into 'Text'.  This uses UTF8, but if there is an--- error, it pretends it was Latin1.  Evil as this is, it tends to Just--- Work on files where nobody ever wasted a thought on encodings.-decodeBytes :: Bytes -> Text-decodeBytes = decodeUtf8With (const $ fmap w2c)---- | Converts 'Text' into 'Bytes'.  This uses UTF8.-encodeBytes :: Text -> Bytes-encodeBytes = encodeUtf8-
− src/Bio/TwoBit.hs
@@ -1,296 +0,0 @@--- | Would you believe it?  The 2bit format stores blocks of Ns in a table at--- the beginning of a sequence, then packs four bases into a byte.  So it--- is neither possible nor necessary to store Ns in the main sequence, and--- you would think they aren't stored there, right?  And they aren't.--- Instead Ts are stored which the reader has to replace with Ns.------ The sensible way to treat these is probably to just say there are two--- kinds of implied annotation (repeats and large gaps for a typical--- genome), which can be interpreted in whatever way fits.  And that's why--- we have 'Mask' and 'getSubseqWith'.--module Bio.TwoBit (-        TwoBitFile(..),-        TwoBitSequence(..),-        openTwoBit,--        getFwdSubseqWith,-        getSubseq,-        getSubseqWith,-        getSubseqAscii,-        getSubseqMasked,-        getLazySubseq,-        getFragment,-        getFwdSubseqV,-        getSeqnames,-        lookupSequence,-        getSeqLength,-        clampPosition,-        getRandomSeq,--        takeOverlap,-        mergeBlocks,-        Mask(..)-    ) where--import           Bio.Prelude hiding ( left, right, chr )-import           Bio.Util.MMap-import           Bio.Util.Storable-import           Control.Monad.Trans.State ( StateT(..), get, evalStateT )-import qualified Data.ByteString                as B-import qualified Data.ByteString.Unsafe         as B-import qualified Data.IntMap.Strict             as I-import qualified Data.HashMap.Lazy              as M-import qualified Data.Vector.Unboxed            as U-import           Foreign.C.Types ( CChar )--data TwoBitFile = TBF {-    tbf_raw :: B.ByteString,-    -- This map is intentionally lazy.  May or may not be important.-    tbf_seqs :: !(M.HashMap Seqid TwoBitSequence)-}--data TwoBitSequence = TBS { tbs_n_blocks   :: !(I.IntMap Int)-                          , tbs_m_blocks   :: !(I.IntMap Int)-                          , tbs_dna_offset :: {-# UNPACK #-} !Int-                          , tbs_dna_size   :: {-# UNPACK #-} !Int }---- | Brings a 2bit file into memory.  The file is mmap'ed, so it will--- not work on streams that are not actual files.  It's also unsafe if--- the file is modified in any way.-openTwoBit :: FilePath -> IO TwoBitFile-openTwoBit fp = do-        raw <- unsafeMMapFile fp-        B.unsafeUseAsCString raw $ \praw ->-        -- return $ flip runGet (L.fromChunks [raw]) $ do-            flip evalStateT praw $ do-                    sig <- getWord32be-                    getWord32 <- case sig :: Word32 of-                            0x1A412743 -> return getWord32be-                            0x4327411A -> return getWord32le-                            _          -> fail $ "invalid .2bit signature " ++ showHex sig []--                    version <- getWord32-                    unless (version == 0) $ fail $ "wrong .2bit version " ++ show version--                    nseqs <- getWord32-                    _reserved <- getWord32--                    TBF raw <$> foldM (\ix _ -> do !key <- getWord8 >>= getByteString-                                                   !off <- getWord32-                                                   return $! M.insert key (mkBlockIndex raw getWord32 off) ix-                                      ) M.empty [1..nseqs]--type Get = StateT (Ptr CChar) IO--getWord8, getWord32be, getWord32le :: Num a => Get a-getWord8    = StateT $ \p -> peekUnalnWord32BE  p >>= \w -> return (fromIntegral w, plusPtr p 1)-getWord32be = StateT $ \p -> peekUnalnWord32BE  p >>= \w -> return (fromIntegral w, plusPtr p 4)-getWord32le = StateT $ \p -> peekUnalnWord32LE  p >>= \w -> return (fromIntegral w, plusPtr p 4)--getByteString :: Int -> Get Bytes-getByteString l = StateT $ \p -> B.packCStringLen (p,l) >>= \s -> return (s, plusPtr p l)--mkBlockIndex :: B.ByteString -> Get Int -> Int -> TwoBitSequence-mkBlockIndex raw getWord32 ofs =-    unsafePerformIO $-    B.unsafeUseAsCString raw $ \praw ->-    evalStateT getBlock (plusPtr praw ofs)-  where-    getBlock = do p0 <- get-                  ds <- getWord32-                  nb <- readBlockList-                  mb <- readBlockList-                  _  <- getWord32-                  p1 <- get-                  return $! TBS (I.fromList nb) (I.fromList mb) (ofs + minusPtr p1 p0) ds--    readBlockList = getWord32 >>= \n -> liftM2 zip (repM n getWord32) (repM n getWord32)---- | Repeat monadic action @n@ times.  Returns result in reverse(!)--- order, but doesn't build a huge list of thunks in memory.-repM :: Monad m => Int -> m a -> m [a]-repM n0 m = go [] n0-  where-    go acc 0 = return acc-    go acc n = m >>= \x -> x `seq` go (x:acc) (n-1)--takeOverlap :: Int -> I.IntMap Int -> [(Int,Int)]-takeOverlap k m = dropWhile far_left $-                  maybe id (\(kv,_) -> (:) kv) (I.maxViewWithKey left) $-                  maybe id (\v -> (:) (k,v)) middle $-                  I.toAscList right-  where-    (left, middle, right) = I.splitLookup k m-    far_left (s,l) = s+l <= k--data Mask = None | Soft | Hard | Both deriving (Eq, Ord, Enum, Show)--getFwdSubseqWith :: TwoBitFile -> TwoBitSequence                -- raw data, sequence-                 -> (Word8 -> Mask -> a)                        -- mask function-                 -> Int -> [a]                                  -- start, lazy result-getFwdSubseqWith TBF{..} TBS{..} nt start =-    do_mask (takeOverlap start tbs_n_blocks `mergeBlocks` takeOverlap start tbs_m_blocks) start .-    drop (start .&. 3) .-    B.foldr toDNA [] .-    B.drop (fromIntegral $ tbs_dna_offset + (start `shiftR` 2)) $ tbf_raw-  where-    toDNA b = (++) [ 3 .&. (b `shiftR` x) | x <- [6,4,2,0] ]--    do_mask            _ _ [] = []-    do_mask [          ] _ ws = map (`nt` None) ws-    do_mask ((s,l,m):is) p ws-        | p < s     = map (`nt` None) (take  (s-p)  ws) ++ do_mask ((s,l,m):is)  s   (drop  (s-p)  ws)-        | otherwise = map (`nt`    m) (take (s+l-p) ws) ++ do_mask          is (s+l) (drop (s+l-p) ws)---- | Merge blocks of Ns and blocks of Ms into single list of blocks with--- masking annotation.  Gaps remain.  Used internally only.-mergeBlocks :: [(Int,Int)] -> [(Int,Int)] -> [(Int,Int,Mask)]-mergeBlocks ((_,0):nbs) mbs = mergeBlocks nbs mbs-mergeBlocks nbs ((_,0):mbs) = mergeBlocks nbs mbs--mergeBlocks ((ns,nl):nbs) ((ms,ml):mbs)-    | ns < ms   = let l = min (ms-ns) nl in (ns,l, Hard) : mergeBlocks ((ns+l,nl-l):nbs) ((ms,ml):mbs)-    | ms < ns   = let l = min (ns-ms) ml in (ms,l, Soft) : mergeBlocks ((ns,nl):nbs) ((ms+l,ml-l):mbs)-    | otherwise = let l = min nl ml in (ns,l, Both) : mergeBlocks ((ns+l,nl-l):nbs) ((ms+l,ml-l):mbs)--mergeBlocks ((ns,nl):nbs) [] = (ns,nl, Hard) : mergeBlocks nbs []-mergeBlocks [] ((ms,ml):mbs) = (ms,ml, Soft) : mergeBlocks [] mbs--mergeBlocks [     ] [     ] = []----- | Extract a subsequence and apply masking.  TwoBit file can represent--- two kinds of masking (hard and soft), where hard masking is usually--- realized by replacing everything by Ns and soft masking is done by--- lowercasing.  Here, we take a user supplied function to apply--- masking.-getSubseqWith :: (Nucleotide -> Mask -> a) -> TwoBitFile -> Range -> [a]-getSubseqWith maskf tbf Range{ r_pos = Pos { p_seq = chr, p_start = start }, r_length = len } = do-    let sq1 = fromMaybe (error $ unpack chr ++ " doesn't exist") $ M.lookup chr (tbf_seqs tbf)-    let go = getFwdSubseqWith tbf sq1-    if start < 0-        then reverse $ take len $ go (maskf . cmp_nt) (-start-len)-        else           take len $ go (maskf . fwd_nt)   start-  where-    fwd_nt = (!!) [nucT, nucC, nucA, nucG] . fromIntegral-    cmp_nt = (!!) [nucA, nucG, nucT, nucC] . fromIntegral---- | Works only in forward direction.-getLazySubseq :: TwoBitFile -> Position -> [Nucleotide]-getLazySubseq tbf Pos{ p_seq = chr, p_start = start } = do-    let sq1 = fromMaybe (error $ unpack chr ++ " doesn't exist") $ M.lookup chr (tbf_seqs tbf)-    let go  = getFwdSubseqWith tbf sq1-    if start < 0-        then error "sorry, can't go backwards"-        -- then reverse $ take len $ go (maskf . cmp_nt) (-start-len)-        else go fwd_nt start-  where-    fwd_nt n _ = [nucT, nucC, nucA, nucG] !! fromIntegral n----- | Extract a subsequence without masking.-getSubseq :: TwoBitFile -> Range -> [Nucleotide]-getSubseq = getSubseqWith const---- | Extract a subsequence with typical masking:  soft masking is--- ignored, hard masked regions are replaced with Ns.-getSubseqMasked :: TwoBitFile -> Range -> [Nucleotides]-getSubseqMasked = getSubseqWith mymask-  where-    mymask n None = nucToNucs n-    mymask n Soft = nucToNucs n-    mymask _ Hard = nucsN-    mymask _ Both = nucsN---- | Extract a subsequence with masking for biologists:  soft masking is--- done by lowercasing, hard masking by printing an N.-getSubseqAscii :: TwoBitFile -> Range -> String-getSubseqAscii = getSubseqWith mymask-  where-    mymask n None = showNucleotide n-    mymask n Soft = toLower (showNucleotide n)-    mymask _ Hard = 'N'-    mymask _ Both = 'N'---getSeqnames :: TwoBitFile -> [Seqid]-getSeqnames = M.keys . tbf_seqs--lookupSequence :: TwoBitFile -> Seqid -> Maybe TwoBitSequence-lookupSequence tbf sq = M.lookup sq . tbf_seqs $ tbf--getSeqLength :: TwoBitFile -> Seqid -> Int-getSeqLength tbf chr =-    maybe (error $ shows chr " doesn't exist") tbs_dna_size $-    M.lookup chr (tbf_seqs tbf)---- | limits a range to a position within the actual sequence-clampPosition :: TwoBitFile -> Range -> Range-clampPosition tbf (Range (Pos n start) len) = Range (Pos n start') (end' - start')-  where-    size   = getSeqLength tbf n-    start' = if start < 0 then max start (-size) else start-    end'   = min (start + len) $ if start < 0 then 0 else size----- | Sample a piece of random sequence uniformly from the genome.--- Only pieces that are not hard masked are sampled, soft masking is--- allowed, but not reported.--- On a 32bit platform, this will fail for genomes larger than 1G bases.--- However, if you're running this code on a 32bit platform, you have--- bigger problems to worry about.-getRandomSeq :: TwoBitFile                   -- ^ 2bit file-             -> Int                          -- ^ desired length-             -> (Int -> g -> (Int, g))       -- ^ draw random int below limit-             -> g                            -- ^ RNG-             -> ((Range, [Nucleotide]), g)   -- ^ position, sequence, new RNG-getRandomSeq tbf len rndInt = draw-  where-    names = getSeqnames tbf-    lengths = map (getSeqLength tbf) names-    total = sum lengths-    frags = I.fromList $ zip (scanl (+) 0 lengths) names--    draw g0 | good      = ((r', sq), gn)-            | otherwise = draw gn-      where-        (p0, gn) = rndInt (2*total) g0-        p = p0 `shiftR` 1-        Just ((o,s),_) = I.maxViewWithKey $ fst $ I.split (p+1) frags-        r' = (if odd p0 then id else reverseRange) $ clampPosition tbf $ Range (Pos s (p-o)) len-        sq = catMaybes $ getSubseqWith mask2maybe tbf r'-        good = r_length r' == len && length sq == len--        mask2maybe n None = Just n-        mask2maybe n Soft = Just n-        mask2maybe _ Hard = Nothing-        mask2maybe _ Both = Nothing---- | Gets a fragment from a 2bit file.  The result always has the--- desired length; if necessary, it is padded with Ns.  Be careful about--- the unconventional encoding: 0..4 == TCAGN-getFragment :: TwoBitFile -> Seqid -> Int -> Int -> U.Vector Word8-getFragment tbf chr p l =-    case lookupSequence tbf chr of-        Nothing  -> U.replicate l 4-        Just tbs -> getFwdSubseqV tbf tbs p l---- Careful about weird encoding: 0..4 == TCAGN-getFwdSubseqV :: TwoBitFile -> TwoBitSequence -> Int -> Int -> U.Vector Word8-getFwdSubseqV TBF{..} TBS{..} start len = U.unfoldrN len step ini-  where-    ini = (start, takeOverlap start tbs_n_blocks)--    step (off, nbs)-        | off < 0                   = Just (4, (succ off, nbs))-        | off >= tbs_dna_size       = Just (4, (succ off, nbs))-        | otherwise = case nbs of-            [        ]             -> Just (y, (succ off, [ ]))-            (s,l):nbs' | off < s   -> Just (y, (succ off, nbs))-                       | off < s+l -> Just (4, (succ off, nbs))-                       | otherwise -> Just (y, (succ off, nbs'))-      where-        x = B.index tbf_raw (tbs_dna_offset + off `shiftR` 2)-        y = x `shiftR` (6 - 2 * (off .&. 3)) .&. 3     -- T,C,A,G-
− src/Bio/Util/MMap.hs
@@ -1,26 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}-module Bio.Util.MMap where--import Bio.Prelude-import Data.ByteString.Internal ( fromForeignPtr )-import Foreign.C.Types--unsafeMMapFile :: FilePath -> IO Bytes-unsafeMMapFile fp =-    bracket (openFd fp ReadOnly Nothing defaultFileFlags) closeFd $ \fd -> do-        stat <- getFdStatus fd-        let size = fromIntegral (fileSize stat)-        if size <= 0-            then return mempty-            else do-                ptr <- c_mmap size (fromIntegral fd)-                if ptr == nullPtr-                    then error "unable to mmap file"-                    else do-                          fptr <- newForeignPtrEnv c_munmap (intPtrToPtr $ fromIntegral size) ptr-                          return $ fromForeignPtr fptr 0 (fromIntegral size)--foreign import ccall unsafe  "my_mmap"   c_mmap   :: CSize -> CInt -> IO (Ptr Word8)-foreign import ccall unsafe "&my_munmap" c_munmap :: FunPtr (Ptr () -> Ptr Word8 -> IO ())--
− src/Bio/Util/Numeric.hs
@@ -1,213 +0,0 @@--- | Random useful stuff I didn't know where to put.--module Bio.Util.Numeric (-    wilson, invnormcdf, choose,-    estimateComplexity, showNum, showOOM,-    log1p, expm1, (<#>),-    log1mexp, log1pexp,-    lsum, llerp-                ) where--import Data.List ( foldl1' )-import Data.Char ( intToDigit )-import Prelude---- | Calculates the Wilson Score interval.--- If @(l,m,h) = wilson c x n@, then @m@ is the binary proportion and--- @(l,h)@ it's @c@-confidence interval for @x@ positive examples out of--- @n@ observations.  @c@ is typically something like 0.05.--wilson :: Double -> Int -> Int -> (Double, Double, Double)-wilson c x n = ( (m - h) / d, p, (m + h) / d )-  where-    nn = fromIntegral n-    p  = fromIntegral x / nn--    z = invnormcdf (1-c*0.5)-    h = z * sqrt (( p * (1-p) + 0.25*z*z / nn ) / nn)-    m = p + 0.5 * z * z / nn-    d = 1 + z * z / nn--showNum :: Show a => a -> String-showNum = triplets [] . reverse . show-  where-    triplets acc [] = acc-    triplets acc [a] = a:acc-    triplets acc [a,b] = b:a:acc-    triplets acc [a,b,c] = c:b:a:acc-    triplets acc (a:b:c:s) = triplets (',':c:b:a:acc) s--showOOM :: Double -> String-showOOM x | x < 0 = '-' : showOOM (negate x)-          | otherwise = findSuffix (x*10) ".kMGTPEZY"-  where-    findSuffix _ [] = "many"-    findSuffix y (s:ss) | y < 100  = intToDigit (round y `div` 10) : case (round y `mod` 10, s) of-                                            (0,'.') -> [] ; (0,_) -> [s] ; (d,_) -> [s, intToDigit d]-                        | y < 1000 = intToDigit (round y `div` 100) : intToDigit ((round y `mod` 100) `div` 10) :-                                            if s == '.' then [] else [s]-                        | y < 10000 = intToDigit (round y `div` 1000) : intToDigit ((round y `mod` 1000) `div` 100) :-                                            '0' : if s == '.' then [] else [s]-                        | otherwise = findSuffix (y*0.001) ss---- Stolen from Lennart Augustsson's erf package, who in turn took it from--- <http://home.online.no/~pjacklam/notes/invnorm/> Accurate to about 1e-9.-invnormcdf :: (Ord a, Floating a) => a -> a-invnormcdf p =-    let a1 = -3.969683028665376e+01-        a2 =  2.209460984245205e+02-        a3 = -2.759285104469687e+02-        a4 =  1.383577518672690e+02-        a5 = -3.066479806614716e+01-        a6 =  2.506628277459239e+00--        b1 = -5.447609879822406e+01-        b2 =  1.615858368580409e+02-        b3 = -1.556989798598866e+02-        b4 =  6.680131188771972e+01-        b5 = -1.328068155288572e+01--        c1 = -7.784894002430293e-03-        c2 = -3.223964580411365e-01-        c3 = -2.400758277161838e+00-        c4 = -2.549732539343734e+00-        c5 =  4.374664141464968e+00-        c6 =  2.938163982698783e+00--        d1 =  7.784695709041462e-03-        d2 =  3.224671290700398e-01-        d3 =  2.445134137142996e+00-        d4 =  3.754408661907416e+00--        pLow = 0.02425--        nan = 0/0--    in  if p < 0 then-            nan-        else if p == 0 then-            -1/0-        else if p < pLow then-            let q = sqrt(-2 * log p)-            in  (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) /-                 ((((d1*q+d2)*q+d3)*q+d4)*q+1)-        else if p < 1 - pLow then-            let q = p - 0.5-                r = q*q-            in  (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q /-                (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1)-        else if p <= 1 then-            - invnormcdf (1 - p)-        else-            nan----- | Try to estimate complexity of a whole from a sample.  Suppose we--- sampled @total@ things and among those @singles@ occured only once.--- How many different things are there?------ Let the total number be @m@.  The copy number follows a Poisson--- distribution with paramter @\lambda@.  Let \( z := e^{\lambda} \), then--- we have:------ \[---   P( 0 ) = e^{-\lambda} = \frac{1}{z}                    \\---   P( 1 ) = \lambda e^{-\lambda} = \frac{\ln z}{z}                   \\---   P(\ge 1) = 1 - e^{-\lambda} = 1 - \frac{1}{z}                    \\--- \]--- \[---   \mbox{singles} = m \frac{\ln z}{z}                   \\---   \mbox{total}   = m \left( 1 - \frac{1}{z} \right)                  \\--- \]--- \[---   D := \frac{\mbox{total}}{\mbox{singles}} = (1 - \frac{1}{z}) * \frac{z}{\ln z}                  \\---   f := z - 1 - D \ln z = 0--- \]------ To get @z@, we solve using Newton iteration and then substitute to--- get @m@:------ \[---   df/dz = 1 - D/z                                    \\---   z' = z - \frac{ z (z - 1 - D \ln z) }{ z - D }     \\---   m = \mbox{singles} * \frac{z}{\ln z}--- \]------ It converges as long as the initial @z@ is large enough, and @10D@--- (in the line for @zz@ below) appears to work well.--estimateComplexity :: (Integral a, Floating b, Ord b) => a -> a -> Maybe b-estimateComplexity total singles | total   <= singles = Nothing-                                 | singles <= 0       = Nothing-                                 | otherwise          = Just m-  where-    d = fromIntegral total / fromIntegral singles-    step z = z * (z - 1 - d * log z) / (z - d)-    iter z = case step z of zd | abs zd < 1e-12 -> z-                               | otherwise -> iter $! z-zd-    zz = iter $! 10*d-    m = fromIntegral singles * zz / log zz----- | Computes \( \ln \left( e^x + e^y \right) \) without leaving the log domain and--- hence without losing precision.-infixl 5 <#>-{-# INLINE (<#>) #-}-(<#>) :: (Floating a, Ord a) => a -> a -> a-x <#> y = if x >= y then x + log1pexp (y-x) else y + log1pexp (x-y)---- | Computes @log (1+x)@ to a relative precision of @10^-8@ even for--- very small @x@.  Stolen from <http://www.johndcook.com/cpp_log_one_plus_x.html>-{-# INLINE log1p #-}-log1p :: (Floating a, Ord a) => a -> a-log1p x | x < -1 = error "log1p: argument must be greater than -1"-        -- x is large enough that the obvious evaluation is OK:-        | x > 0.0001 || x < -0.0001 = log $ 1 + x-        -- Use Taylor approx. log(1 + x) = x - x^2/2 with error roughly x^3/3-        -- Since |x| < 10^-4, |x|^3 < 10^-12, relative error less than 10^-8:-        | otherwise = (1 - 0.5*x) * x----- | Computes \( e^x - 1 \) to a relative precision of @10^-10@ even for--- very small @x@.  Stolen from <http://www.johndcook.com/cpp_expm1.html>-{-# INLINE expm1 #-}-expm1 :: (Floating a, Ord a) => a -> a-expm1 x | x > -0.00001 && x < 0.00001 = (1 + 0.5 * x) * x       -- Taylor approx-        | otherwise                   = exp x - 1               -- direct eval---- | Computes \( \ln (1 - e^x) \), following Martin Mächler.-{-# INLINE log1mexp #-}-log1mexp :: (Floating a, Ord a) => a -> a-log1mexp x | x > - log 2 = log (- expm1 x)-           | otherwise   = log1p (- exp x)---- | Computes \( \ln (1 + e^x) \), following Martin Mächler.-{-# INLINE log1pexp #-}-log1pexp :: (Floating a, Ord a) => a -> a-log1pexp x | x <=  -37 = exp x-           | x <=   18 = log1p $ exp x-           | x <= 33.3 = x + exp (-x)-           | otherwise = x----- | Computes \( \ln ( \sum_i e^{x_i} ) \) sensibly.  The list must be--- sorted in descending(!) order.-{-# INLINE lsum #-}-lsum :: (Floating a, Ord a) => [a] -> a-lsum = foldl1' (\x y -> if x >= y then x + log1pexp (y-x) else err)-    where err = error "lsum: argument list must be in descending order"---- | Computes \( \ln \left( c e^x + (1-c) e^y \right) \).-{-# INLINE llerp #-}-llerp :: (Floating a, Ord a) => a -> a -> a -> a-llerp c x y | c <= 0.0  = y-            | c >= 1.0  = x-            | x >= y    = log     c  + x + log1p ( (1-c)/c * exp (y-x) )        -- Hmm.-            | otherwise = log1p (-c) + y + log1p ( c/(1-c) * exp (x-y) )        -- Hmm.---- | Binomial coefficient: \( \mbox{choose n k} = \frac{n!}{(n-k)! k!} \)-{-# INLINE choose #-}-choose :: Integral a => a -> a -> a-choose n k = product [n-k+1 .. n] `div` product [2..k]--
− src/Bio/Util/Storable.hs
@@ -1,97 +0,0 @@-{-# LANGUAGE CPP #-}--- | Utilities to read multibyte quantities from arbitrary positions.-module Bio.Util.Storable-    ( peekWord8-    , peekUnalnWord16LE-    , peekUnalnWord16BE-    , peekUnalnWord32LE-    , peekUnalnWord32BE-    , pokeUnalnWord32LE-    ) where--#if __GLASGOW_HASKELL__ >= 710-#define HAVE_BYTESWAP_PRIMOPS-#endif--#if i386_HOST_ARCH || x86_64_HOST_ARCH-#define MEM_UNALIGNED_OPS-#endif--import Bio.Prelude--peekWord8 :: Ptr a -> IO Word8-peekWord8 = peek . castPtr--#if defined(MEM_UNALIGNED_OPS) && defined(WORDS_BIGENDIAN) && defined(HAVE_BYTESWAP_PRIMOPS)-peekUnalnWord16LE :: Ptr a -> IO Word16-peekUnalnWord16LE = fmap byteSwap16 . peek . castPtr--peekUnalnWord32LE :: Ptr a -> IO Word32-peekUnalnWord32LE = fmap byteSwap32 . peek . castPtr--pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()-pokeUnalnWord32LE p w = poke (castPtr p) (byteSwap32 w)--#elif defined(MEM_UNALIGNED_OPS) && !defined(WORDS_BIGENDIAN)-peekUnalnWord16LE :: Ptr a -> IO Word16-peekUnalnWord16LE = peek . castPtr--peekUnalnWord32LE :: Ptr a -> IO Word32-peekUnalnWord32LE = peek . castPtr--pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()-pokeUnalnWord32LE p w = poke (castPtr p) w--#else-peekUnalnWord16LE :: Ptr a -> IO Word16-peekUnalnWord16LE p = do-    x <- fromIntegral <$> peekWord8 (plusPtr p 0)-    y <- fromIntegral <$> peekWord8 (plusPtr p 1)-    return $! x .|. unsafeShiftL y 8--peekUnalnWord32LE :: Ptr a -> IO Word32-peekUnalnWord32LE p = do-    x <- fromIntegral <$> peekWord8 (plusPtr p 0)-    y <- fromIntegral <$> peekWord8 (plusPtr p 1)-    z <- fromIntegral <$> peekWord8 (plusPtr p 2)-    w <- fromIntegral <$> peekWord8 (plusPtr p 3)-    return $! x .|. unsafeShiftL y 8 .|. unsafeShiftL z 16 .|. unsafeShiftL w 24--pokeUnalnWord32LE :: Ptr a -> Word32 -> IO ()-pokeUnalnWord32LE p w = do pokeByteOff p 0 (fromIntegral $ shiftR w  0 :: Word8)-                           pokeByteOff p 1 (fromIntegral $ shiftR w  8 :: Word8)-                           pokeByteOff p 2 (fromIntegral $ shiftR w 16 :: Word8)-                           pokeByteOff p 3 (fromIntegral $ shiftR w 24 :: Word8)-#endif---#if defined(MEM_UNALIGNED_OPS) && !defined(WORDS_BIGENDIAN) && defined(HAVE_BYTESWAP_PRIMOPS)-peekUnalnWord16BE :: Ptr a -> IO Word16-peekUnalnWord16BE = fmap byteSwap16 . peek . castPtr--peekUnalnWord32BE :: Ptr a -> IO Word32-peekUnalnWord32BE = fmap byteSwap32 . peek . castPtr--#elif defined(MEM_UNALIGNED_OPS) && defined(WORDS_BIGENDIAN)-peekUnalnWord16BE :: Ptr a -> IO Word16-peekUnalnWord16BE = peek . castPtr--peekUnalnWord32BE :: Ptr a -> IO Word32-peekUnalnWord32BE = peek . castPtr--#else-peekUnalnWord16BE :: Ptr a -> IO Word16-peekUnalnWord16BE p = do-    x <- fromIntegral <$> peekWord8 (plusPtr p 0)-    y <- fromIntegral <$> peekWord8 (plusPtr p 1)-    return $! y .|. unsafeShiftL x 8--peekUnalnWord32BE :: Ptr a -> IO Word32-peekUnalnWord32BE p = do-    x <- fromIntegral <$> peekWord8 (plusPtr p 0)-    y <- fromIntegral <$> peekWord8 (plusPtr p 1)-    z <- fromIntegral <$> peekWord8 (plusPtr p 2)-    w <- fromIntegral <$> peekWord8 (plusPtr p 3)-    return $! w .|. unsafeShiftL z 8 .|. unsafeShiftL y 16 .|. unsafeShiftL x 24-#endif-
− src/Bio/Util/Zlib.hs
@@ -1,23 +0,0 @@-{-# LANGUAGE CPP #-}-module Bio.Util.Zlib ( decompressGzip ) where--import Prelude--import qualified Data.ByteString.Lazy            as L-import qualified Data.ByteString.Lazy.Internal   as L ( ByteString(..) )-import qualified Codec.Compression.Zlib.Internal as Z----- | Decompresses Gzip or Bgzf and passes everything else on.  In--- reality, it simply decompresses Gzip, and when done, looks for--- another Gzip stream.  Since there is a small chance to attempt--- decompression of an uncompressed stream, the original data is--- returned in case of an error.-decompressGzip :: L.ByteString -> L.ByteString-decompressGzip s = case L.uncons s of-    Just (31, s') -> case L.uncons s' of-        Just (139,_) -> Z.foldDecompressStreamWithInput L.Chunk decompressGzip (const s)-                        (Z.decompressST Z.gzipOrZlibFormat Z.defaultDecompressParams) s-        _            -> s-    _                -> s-
− src/cbits/loops.c
@@ -1,113 +0,0 @@-void nuc_loop( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;--    while( u < v ) {-        char a = q[ u ] ;-        char b = q[ u + stride ] ;-        char a1 = a ? 0x10 << (a&3) : 0xf0 ;-        char b1 = b ? 0x1  << (b&3) : 0xf  ;-        *p++ = a1 | b1 ;-        u += stride+stride ;-    }-    if( u == v ) {-        char a = q[ u ] ;-        char a1 = a ? 0x10 << (a&3) : 0xf0 ;-        *p = a1 ;-    }-}--void nuc_loop_wide( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;--    while( u < v ) {-        char a = q[ u ] ;-        *p++ = a ? 0x1 << (a&3) : 0xf  ;-        u += stride ;-    }-}--void nuc_loop_asc( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;--    while( u <= v ) {-        char a = q[ u ] ;-        *p++ = a == 0 ? 'N' : (a&3) == 0 ? 'A' : (a&3) == 1 ? 'C' : (a&3) == 2 ? 'G' : 'T' ;-        u += stride ;-    }-    *p = 0 ;-}--void nuc_loop_asc_rev( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;--    while( u <= v ) {-        char a = q[ v ] ;-        *p++ = a == 0 ? 'N' : (a&3) == 0 ? 'T' : (a&3) == 1 ? 'G' : (a&3) == 2 ? 'C' : 'A' ;-        v -= stride ;-    }-    *p = 0 ;-}--void qual_loop( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;-    while( u <= v ) {-        *p++ = (q[u] >> 2) & 0x3f ;-        u += stride ;-    }-}--void qual_loop_asc( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;-    while( u <= v ) {-        *p++ = 33 + ((q[u] >> 2) & 0x3f) ;-        u += stride ;-    }-    *p = 0 ;-}--void qual_loop_asc_rev( char* p, int stride, char* q, int u, int v )-{-    u *= stride ;-    v *= stride ;-    while( u <= v ) {-        *p++ = 33 + ((q[v] >> 2) & 0x3f) ;-        v -= stride ;-    }-    *p = 0 ;-}--int int_loop( char* p, int x )-{-    *p++ = ':' ;-    if( x == 0 ) {-        *p = '0' ;-        return 2 ;-    }-    char *q = p ;-    while( x > 0 ) {-        *q++ = '0' + x % 10 ;-        x /= 10 ;-    }-    int r = q-p ;-    --q ;-    while( p < q ) {-        char c = *p ;-        *p++ = *q ;-        *q-- = c ;-    }-    return r+1 ;-}--
− src/cbits/mmap.c
@@ -1,10 +0,0 @@-#include <sys/mman.h>--unsigned char *my_mmap(size_t len, int fd) {-        void *result = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);-        return (unsigned char*)( result == MAP_FAILED ? 0 : result );-}--void my_munmap(void *len, unsigned char *p) {-        munmap( p, (size_t)len ) ; -}
− src/cbits/myers_align.c
@@ -1,102 +0,0 @@-#include "myers_align.h"--#include <limits.h>-#include <stdlib.h>-#include <string.h>--// [*blech*, this looks and feels like FORTRAN.]-unsigned myers_diff(-        const char *seq_a, int len_a, enum myers_align_mode mode, -        const char* seq_b, int len_b, int maxd,-        char *bt_a, char *bt_b ) -{-	// int len_a = strlen( seq_a ), len_b = strlen( seq_b ) ;-	if( maxd > len_a + len_b ) maxd = len_a + len_b ;--	// in vee[d][k], d runs from 0 to maxd; k runs from -d to +d-	int **vee = calloc( maxd, sizeof(int*) ) ;--	int d, dd, k, x, y, r = UINT_MAX ;-	int *v_d_1 = 0, *v_d = 0 ; 															// "array slice" vee[.][d-1]-	for( d = 0 ; d != maxd ; ++d, v_d_1 = v_d )									// D-paths in order of increasing D-	{-		v_d = d + (vee[d] = malloc( (2 * d + 1) * sizeof( int ) )) ; 		// "array slice" vee[.][d]--		for( k = max(-d,-len_a) ; k <= min(d,len_b) ; ++k ) 					// diagonals-		{-			if( d == 0 )         x = 0 ;-			else if(d==1&&k==0)  x =                       v_d_1[ k ]+1 ;-			else if( k == -d   ) x =                                     v_d_1[ k+1 ] ;-			else if( k ==  d   ) x =       v_d_1[ k-1 ]+1 ;									// argh, need to check for d first, b/c -d+2 could be equal to d-			else if( k == -d+1 ) x = max(                  v_d_1[ k ]+1, v_d_1[ k+1 ] ) ;-			else if( k ==  d-1 ) x = max(  v_d_1[ k-1 ]+1, v_d_1[ k ]+1 ) ;-			else                 x = max3( v_d_1[ k-1 ]+1, v_d_1[ k ]+1, v_d_1[ k+1 ] ) ;--			y = x-k ;-			while( x < len_b && y < len_a && match( seq_b[x], seq_a[y] ) ) ++x, ++y ;-			v_d[ k ] = x ;--			if(-                    bt_a && bt_b &&-					(mode == myers_align_is_prefix || y == len_a) &&-					(mode == myers_align_has_prefix || x == len_b) )-			{-				char *out_a = bt_a + len_a + d +2 ;-				char *out_b = bt_b + len_b + d +2 ;-				*--out_a = 0 ;-				*--out_b = 0 ;-				for( dd = d ; dd != 0 ; )-				{-					if( k != -dd && k != dd && x == vee[ dd-1 ][ k + dd-1 ]+1 )-					{-						--dd ;-						--x ;-						--y ;-						*--out_b = seq_b[x] ;-						*--out_a = seq_a[y] ;-					}-					else if( k > -dd+1 && x == vee[ dd-1 ][ k-1 + dd-1 ]+1 )-					{-						--x ;-						--k ;-						--dd ;-						*--out_b = seq_b[x] ;-						*--out_a = '-' ;-					}-					else if( k < dd-1 && x == vee[ dd-1 ][ k+1 + dd-1 ] )-					{-						++k ;-						--y ;-						--dd ;-						*--out_b = '-' ;-						*--out_a = seq_a[y] ;-					}-					else // this better had been a match...-					{-						--x ;-						--y ;-						*--out_b = seq_b[x] ;-						*--out_a = seq_a[y] ;-					}-				}-				while( x > 0 )-				{-					--x ;-					*--out_b = seq_b[x] ;-					*--out_a = seq_a[x] ;-				}-				memmove( bt_a, out_a, bt_a + len_a + d + 2 - out_a ) ;-				memmove( bt_b, out_b, bt_b + len_b + d + 2 - out_b ) ;-				r = d ;-				goto cleanup ;-			}-		}-	}--cleanup:-	for( dd = maxd ; dd != 0 ; --dd )-		free( vee[dd-1] ) ;-	free( vee ) ;-	return r ;-}-
− src/cbits/myers_align.h
@@ -1,76 +0,0 @@-#ifndef INCLUDED_MYERS_ALIGN-#define INCLUDED_MYERS_ALIGN--enum myers_align_mode {-    myers_align_globally = 0,-    myers_align_is_prefix = 1,-    myers_align_has_prefix = 2 } ;--//! \brief aligns two sequences in O(nd) time-//! This alignment algorithm following Eugene W. Myers: "An O(ND)-//! Difference Algorithm and Its Variations".-//! Both input sequences are ASCIIZ-encoded with IUPAC-IUB ambiguity-//! codes.  By definition, if ambiguity codes overlap, that's a match,-//! else a mismatch.  Mismatches and gaps count a unit penalty.  If mode-//! is myers_align_globally, both sequences must align completely.  If-//! mode is myers_align_is_prefix, seq_a must align completely as prefix-//! of seq_b.  If mode is myers_align_has_prefix, seq_b must align-//! completely as prefix of seq_a.  -//!-//! Note that the calculation time is O(nd) where n is the length of the-//! best alignment and d the number of differences in it, memory-//! consumption is O(maxd^2).-//!-//! \param seq_a First input sequence.-//! \param mode How to align (i.e. what gaps to count).-//! \param seq_b Second input sequence.-//! \param maxd Maximum penalty to consider.-//! \param bt_a Space to backtrace seq_a into, must have room for-//!             (strlen(seq_a)+maxd+1) characters.-//! \param bt_b Space to backtrace seq_b into, must have room for-//!             (strlen(seq_b)+maxd+1) characters.-//! \return The actual edit distance or UINT_MAX if the edit distance-//!         would be greater than maxd.-//!-unsigned myers_diff(-        const char *seq_a, int len_a, enum myers_align_mode mode,-        const char* seq_b, int len_b, int maxd,-        char *bt_a, char *bt_b ) ;--//! \brief converts an IUPAC-IUB ambiguity code to a bitmap Each base is-//! represented by a bit, makes checking for matches easier.-static inline int char_to_bitmap( char x ) -{-    switch( x & ~32 )-    {-        case 'A': return 1 ;-        case 'C': return 2 ;-        case 'G': return 4 ;-        case 'T': return 8 ;-        case 'U': return 8 ;--        case 'S': return 6 ;-        case 'W': return 9 ;-        case 'R': return 5 ;-        case 'Y': return 10 ;-        case 'K': return 12 ;-        case 'M': return 3 ;--        case 'B': return 14 ;-        case 'D': return 13 ;-        case 'H': return 11 ;-        case 'V': return 7 ;--        case 'N': return 15 ;-        default: return 0 ;-    }-}--static inline int compatible( char x, char y ) { return (char_to_bitmap(x) & char_to_bitmap(y)) != 0 ; }-static inline int match( char a, char b ) { return (char_to_bitmap(a) & char_to_bitmap(b)) != 0 ; }--static inline int min( int a, int b ) { return a < b ? a : b ; }-static inline int max( int a, int b ) { return a < b ? b : a ; }-static inline int max3( int a, int b, int c ) { return a < b ? max( b, c ) : max( a, c ) ; }--#endif
− src/cbits/trim.c
@@ -1,46 +0,0 @@-#include <stdint.h>--static const uint8_t compls[] =-    { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 } ;--int prim_match_reads( int i1-                    , int i2-                    , int r-                    , const uint8_t *rd1-                    , const uint8_t *qs1-                    , const uint8_t *rd2-                    , const uint8_t *qs2 )-{-    int acc = 0 ;-    while( r != 0 )-    {-        --i2 ;-        uint8_t n1 = rd1[ i1 ] ;-        uint8_t n2 = rd2[ i2 ] ;-        uint8_t q1 = qs1[ i1 ] ;-        uint8_t q2 = qs2[ i2 ] ;--        acc += (n1 & 0xF) == compls[ n2 & 0xF ] ? 0 : 5 + (q1 < q2 ? q1 : q2) ;--        ++i1 ;-        --r ;-    }-    return acc ;-}--int prim_match_ad( int off-                 , int i-                 , const uint8_t *rd-                 , const uint8_t *qs-                 , const uint8_t *ad )-{-    int acc = 0 ;-    while( i > 0 )-    {-        --i;-        acc += rd[ i+off ] == ad[ i ] ? 0 : 5 +-               (qs[ i+off ] < 25 ? qs[ i+off ] : 25) ;-    }-    return acc ;-}-