biohazard 2.0 → 2.1
raw patch · 29 files changed
+1286/−707 lines, 29 filesdep +monad-controldep +optparse-applicativedep +transformers-basedep −bifunctorsdep ~attoparsecdep ~basedep ~base-preludebuild-type:Customsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: monad-control, optparse-applicative, transformers-base
Dependencies removed: bifunctors
Dependency ranges changed: attoparsec, base, base-prelude, bytestring, directory, exceptions, hashable, primitive, semigroups, text, transformers, unix, unordered-containers, vector-algorithms
API changes (from Hackage documentation)
- Bio.Bam.Fastq: parseFastqCassava :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
- Bio.Bam.Fastq: parseFastqWith :: Monad m => (Bytes -> BamRec -> BamRec) -> ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
- 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.Reader: instance GHC.Exception.Type.Exception Bio.Bam.Reader.ShortRecord
- Bio.Bam.Reader: instance GHC.Show.Show Bio.Bam.Reader.ShortRecord
- Bio.Bam.Rec: isAuxillary :: BamRec -> Bool
- Bio.Bam.Trim: mergeTrimBam :: Monad m => Int -> Int -> [Vector Nucleotides] -> [Vector Nucleotides] -> Stream (Of BamRec) m r -> Stream (Of BamRec) m r
- Bio.Bam.Trim: twoMins :: (Bounded a, Ord a) => a -> Int -> (Int -> a) -> (a, Int, a)
- Bio.Prelude: [SomeAsyncException] :: forall e. Exception e => e -> SomeAsyncException
- Bio.Prelude: [SomeException] :: forall e. Exception e => e -> SomeException
- Bio.Prelude: class Hashable1 (t :: Type -> Type)
- Bio.Prelude: class Hashable2 (t :: Type -> Type -> Type)
- Bio.Prelude: data (:~:) (a :: k) (b :: k) :: forall k. () => k -> k -> Type
- Bio.Prelude: data (:~~:) (a :: k1) (b :: k2) :: forall k1 k2. () => k1 -> k2 -> Type
- Bio.Prelude: exitFailure :: () => IO a
- Bio.Prelude: exitSuccess :: () => IO a
- Bio.Prelude: exitWith :: () => ExitCode -> IO a
- Bio.Prelude: hPrint :: Show a => Handle -> a -> IO ()
- Bio.Prelude: hPutStr :: Handle -> String -> IO ()
- Bio.Prelude: hPutStrLn :: Handle -> String -> 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: newtype Const a (b :: k) :: forall k. () => Type -> k -> Type
- Bio.Prelude: newtype Alt (f :: k -> Type) (a :: k) :: forall k. () => k -> Type -> k -> Type
- Bio.Streaming: data Sum (f :: k -> Type) (g :: k -> Type) (a :: k) :: forall k. () => k -> Type -> k -> Type -> k -> Type
- Bio.Streaming: newtype Compose (f :: k -> Type) (g :: k1 -> k) (a :: k1) :: forall k k1. () => k -> Type -> k1 -> k -> k1 -> Type
- 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.Util.MMap: unsafeMMapFile :: FilePath -> IO ByteString
+ Bio.Bam.Fastq: EmptyRecord :: !Int -> !Bytes -> EmptyRecord
+ Bio.Bam.Fastq: IncoherentQualities :: !Int -> !Bytes -> IncoherentQualities
+ Bio.Bam.Fastq: IncongruentQualities :: !Int -> !Bytes -> IncongruentQualities
+ Bio.Bam.Fastq: JunkFound :: !Int -> !Bytes -> JunkFound
+ Bio.Bam.Fastq: QualitiesMissing :: !Int -> !Bytes -> QualitiesMissing
+ Bio.Bam.Fastq: SequenceHasGaps :: !Int -> !Bytes -> SequenceHasGaps
+ Bio.Bam.Fastq: data EmptyRecord
+ Bio.Bam.Fastq: data IncoherentQualities
+ Bio.Bam.Fastq: data IncongruentQualities
+ Bio.Bam.Fastq: data JunkFound
+ Bio.Bam.Fastq: data QualitiesMissing
+ Bio.Bam.Fastq: data SequenceHasGaps
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.EmptyRecord
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.IncoherentQualities
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.IncongruentQualities
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.JunkFound
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.QualitiesMissing
+ Bio.Bam.Fastq: instance GHC.Exception.Type.Exception Bio.Bam.Fastq.SequenceHasGaps
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.EmptyRecord
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.IncoherentQualities
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.IncongruentQualities
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.JunkFound
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.QualitiesMissing
+ Bio.Bam.Fastq: instance GHC.Show.Show Bio.Bam.Fastq.SequenceHasGaps
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable (Bio.Bam.Header.Fix Bio.Bam.Header.BamPG)
+ Bio.Bam.Header: instance Data.Hashable.Class.Hashable Bio.Bam.Header.Refseq
+ Bio.Bam.Index: IndexFormatError :: Bytes -> IndexFormatError
+ Bio.Bam.Index: data IndexFormatError
+ Bio.Bam.Reader: IncompatibleRefs :: FilePath -> FilePath -> IncompatibleRefs
+ Bio.Bam.Reader: data IncompatibleRefs
+ Bio.Bam.Rec: BrokenRecord :: !Int -> [Int] -> !Bytes -> BrokenRecord
+ Bio.Bam.Rec: LengthMismatch :: !Bytes -> LengthMismatch
+ Bio.Bam.Rec: Vector_Nucs_half :: !Int -> !Int -> !ForeignPtr Word8 -> Vector_Nucs_half a
+ Bio.Bam.Rec: br_copy :: BamRaw -> BamRaw
+ Bio.Bam.Rec: data BrokenRecord
+ Bio.Bam.Rec: data LengthMismatch
+ Bio.Bam.Rec: instance GHC.Exception.Type.Exception Bio.Bam.Rec.BrokenRecord
+ Bio.Bam.Rec: instance GHC.Exception.Type.Exception Bio.Bam.Rec.LengthMismatch
+ Bio.Bam.Rec: instance GHC.Show.Show Bio.Bam.Rec.BrokenRecord
+ Bio.Bam.Rec: instance GHC.Show.Show Bio.Bam.Rec.LengthMismatch
+ Bio.Bam.Rmdup: BrokenMD :: Bytes -> Refseq -> Int -> [Cigar] -> [MdOp] -> [Nucleotides] -> [Nucleotides] -> BrokenMD
+ Bio.Bam.Rmdup: InternalError :: RmdupException
+ Bio.Bam.Rmdup: UnsortedInput :: RmdupException
+ Bio.Bam.Rmdup: data BrokenMD
+ Bio.Bam.Rmdup: data RmdupException
+ Bio.Bam.Rmdup: instance GHC.Exception.Type.Exception Bio.Bam.Rmdup.BrokenMD
+ Bio.Bam.Rmdup: instance GHC.Exception.Type.Exception Bio.Bam.Rmdup.RmdupException
+ Bio.Bam.Rmdup: instance GHC.Show.Show Bio.Bam.Rmdup.BrokenMD
+ Bio.Bam.Rmdup: instance GHC.Show.Show Bio.Bam.Rmdup.RmdupException
+ Bio.Bam.Trim: data AD_Seqs
+ Bio.Bam.Trim: withADSeqs :: MonadBaseControl IO m => [Vector Nucleotides] -> (AD_Seqs -> m r) -> m r
+ Bio.Base: instance GHC.Base.Monoid Bio.Base.Nucleotides
+ Bio.Base: instance GHC.Base.Semigroup Bio.Base.Nucleotides
+ Bio.Prelude: Debug :: Level
+ Bio.Prelude: Error :: Level
+ Bio.Prelude: Info :: Level
+ Bio.Prelude: Notice :: Level
+ Bio.Prelude: SomeAsyncException :: e -> SomeAsyncException
+ Bio.Prelude: SomeException :: e -> SomeException
+ Bio.Prelude: Warning :: Level
+ Bio.Prelude: class Monad m => MonadFail (m :: Type -> Type)
+ Bio.Prelude: class Monad m => MonadLog m
+ Bio.Prelude: class Monad m => PrimMonad (m :: Type -> Type) where {
+ Bio.Prelude: data (a :: k1) :~~: (b :: k2)
+ Bio.Prelude: data Level
+ Bio.Prelude: data Proxy (t :: k)
+ Bio.Prelude: execWithParser_ :: Parser a -> Maybe Version -> Maybe String -> InfoMod (a, LoggingConf) -> (a -> LIO b) -> IO b
+ Bio.Prelude: foldMap' :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
+ Bio.Prelude: logMsg :: (MonadLog m, Exception e) => Level -> e -> m ()
+ Bio.Prelude: logStringLn :: MonadLog m => String -> m ()
+ Bio.Prelude: logString_ :: MonadLog m => String -> m ()
+ Bio.Prelude: newtype Alt (f :: k -> Type) (a :: k)
+ Bio.Prelude: newtype Ap (f :: k -> Type) (a :: k)
+ Bio.Prelude: newtype Const a (b :: k)
+ Bio.Prelude: pattern ErrorCall :: String -> ErrorCall
+ Bio.Prelude: primitive :: PrimMonad m => (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
+ Bio.Prelude: type LIO = Logged IO
+ Bio.Streaming: UnwantedTerminal :: UnwantedTerminal
+ Bio.Streaming: data Sum (f :: k -> Type) (g :: k -> Type) (a :: k)
+ Bio.Streaming: data UnwantedTerminal
+ Bio.Streaming: infixr 5 :>
+ Bio.Streaming: instance (GHC.Base.Functor f, Control.Monad.Primitive.PrimMonad m) => Control.Monad.Primitive.PrimMonad (Streaming.Internal.Stream f m)
+ Bio.Streaming: instance GHC.Exception.Type.Exception Bio.Streaming.UnwantedTerminal
+ Bio.Streaming: instance GHC.Show.Show Bio.Streaming.UnwantedTerminal
+ Bio.Streaming: newtype Compose (f :: k -> Type) (g :: k1 -> k) (a :: k1)
+ Bio.Streaming.Bgzf: CompressionError :: !CInt -> CompressionError
+ Bio.Streaming.Bgzf: DecompressionError :: !CInt -> DecompressionError
+ Bio.Streaming.Bgzf: TkMemCopy :: {-# UNPACK #-} !Vector Word8 -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: TkMemFill :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Word8 -> BgzfTokens -> BgzfTokens
+ Bio.Streaming.Bgzf: data CompressionError
+ Bio.Streaming.Bgzf: data DecompressionError
+ Bio.Streaming.Bgzf: instance GHC.Exception.Type.Exception Bio.Streaming.Bgzf.CompressionError
+ Bio.Streaming.Bgzf: instance GHC.Exception.Type.Exception Bio.Streaming.Bgzf.DecompressionError
+ Bio.Streaming.Bgzf: instance GHC.Show.Show Bio.Streaming.Bgzf.CompressionError
+ Bio.Streaming.Bgzf: instance GHC.Show.Show Bio.Streaming.Bgzf.DecompressionError
+ Bio.Streaming.Bytes: instance (m GHC.Types.~ Data.Functor.Identity.Identity, GHC.Show.Show r) => GHC.Show.Show (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance (r GHC.Types.~ ()) => Data.String.IsString (Bio.Streaming.Bytes.ByteStream m r)
+ Bio.Streaming.Bytes: instance Control.Monad.Morph.MFunctor Bio.Streaming.Bytes.ByteStream
+ Bio.Streaming.Parse: parseLog :: MonadLog m => Level -> (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))
+ Bio.Util.MMap: createMmapFile :: FilePath -> CSize -> (Ptr a -> IO (Ptr a, b)) -> IO b
+ Bio.Util.MMap: mmapFile :: FilePath -> IO (Int, ForeignPtr a)
+ Bio.Util.Numeric: readOOM :: (Read a, Num a) => ReadM a
+ Bio.Util.Text: instance Bio.Util.Text.Unpack Data.ByteString.Lazy.Internal.ByteString
+ Control.Monad.Log: Debug :: Level
+ Control.Monad.Log: Error :: Level
+ Control.Monad.Log: Info :: Level
+ Control.Monad.Log: Logged :: ReaderT (LoggingConf, Journal) m a -> Logged m a
+ Control.Monad.Log: LoggingConf :: Level -> Level -> Level -> Int -> Bool -> LoggingConf
+ Control.Monad.Log: Notice :: Level
+ Control.Monad.Log: PanicCall :: String -> PanicCall
+ Control.Monad.Log: Warning :: Level
+ Control.Monad.Log: [error_level] :: LoggingConf -> Level
+ Control.Monad.Log: [logging_level] :: LoggingConf -> Level
+ Control.Monad.Log: [max_log_size] :: LoggingConf -> Int
+ Control.Monad.Log: [reporting_level] :: LoggingConf -> Level
+ Control.Monad.Log: [runLogged] :: Logged m a -> ReaderT (LoggingConf, Journal) m a
+ Control.Monad.Log: [want_progress] :: LoggingConf -> Bool
+ Control.Monad.Log: class Monad m => MonadLog m
+ Control.Monad.Log: data Level
+ Control.Monad.Log: data LoggingConf
+ Control.Monad.Log: data PanicCall
+ Control.Monad.Log: execWithParser :: Parser a -> Maybe Version -> Maybe String -> InfoMod (a, LoggingConf) -> (a -> LIO b) -> IO (Either ExitCode b)
+ Control.Monad.Log: execWithParser_ :: Parser a -> Maybe Version -> Maybe String -> InfoMod (a, LoggingConf) -> (a -> LIO b) -> IO b
+ Control.Monad.Log: instance (Control.Monad.Log.MonadLog m, GHC.Base.Monoid w) => Control.Monad.Log.MonadLog (Control.Monad.Trans.RWS.Strict.RWST r w s m)
+ Control.Monad.Log: instance Control.Monad.Base.MonadBase b m => Control.Monad.Base.MonadBase b (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.Log.MonadLog (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Morph.MFunctor Control.Monad.Log.Logged
+ Control.Monad.Log: instance Control.Monad.Primitive.PrimMonad m => Control.Monad.Primitive.PrimMonad (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Trans.Class.MonadTrans Control.Monad.Log.Logged
+ Control.Monad.Log: instance Control.Monad.Trans.Control.MonadBaseControl b m => Control.Monad.Trans.Control.MonadBaseControl b (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance Control.Monad.Trans.Control.MonadTransControl Control.Monad.Log.Logged
+ Control.Monad.Log: instance GHC.Arr.Ix Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Base.Alternative m => GHC.Base.Alternative (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance GHC.Base.Applicative m => GHC.Base.Applicative (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance GHC.Base.Functor m => GHC.Base.Functor (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Monad.Log.Logged m)
+ Control.Monad.Log: instance GHC.Classes.Eq Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Classes.Ord Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Enum.Bounded Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Enum.Enum Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Exception.Type.Exception Control.Monad.Log.PanicCall
+ Control.Monad.Log: instance GHC.Show.Show Control.Monad.Log.Level
+ Control.Monad.Log: instance GHC.Show.Show Control.Monad.Log.LoggingConf
+ Control.Monad.Log: instance GHC.Show.Show Control.Monad.Log.PanicCall
+ Control.Monad.Log: logMsg :: (MonadLog m, Exception e) => Level -> e -> m ()
+ Control.Monad.Log: logOptions :: Parser LoggingConf
+ Control.Monad.Log: logStringLn :: MonadLog m => String -> m ()
+ Control.Monad.Log: logString_ :: MonadLog m => String -> m ()
+ Control.Monad.Log: newtype Logged m a
+ Control.Monad.Log: panic :: MonadIO m => String -> m a
+ Control.Monad.Log: type LIO = Logged IO
+ Control.Monad.Log: withLogging :: (MonadIO m, MonadMask m) => LoggingConf -> Logged m a -> m (Either ExitCode a)
+ Control.Monad.Log: withLogging_ :: (MonadIO m, MonadMask m) => LoggingConf -> Logged m a -> m a
- Bio.Bam.Fastq: parseFastq :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)
+ Bio.Bam.Fastq: parseFastq :: MonadLog m => ByteStream m r -> Stream (Of BamRec) m r
- Bio.Bam.Header: addPG :: Maybe Version -> IO (BamMeta -> BamMeta)
+ Bio.Bam.Header: addPG :: MonadIO m => Maybe Version -> m (BamMeta -> BamMeta)
- Bio.Bam.Index: streamBamRefseq :: MonadIO m => BamIndex b -> Handle -> Refseq -> Stream (Of BamRaw) m ()
+ Bio.Bam.Index: streamBamRefseq :: (MonadIO m, MonadLog m) => BamIndex b -> Handle -> Refseq -> Stream (Of BamRaw) m ()
- Bio.Bam.Index: withIndexedBam :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> BamIndex () -> Handle -> m r) -> m r
+ Bio.Bam.Index: withIndexedBam :: (MonadIO m, MonadLog m, MonadMask m) => FilePath -> (BamMeta -> BamIndex () -> Handle -> m r) -> m r
- Bio.Bam.Pileup: pileup :: Stream (Of PosPrimChunks) IO b -> Stream (Of Pile) IO b
+ Bio.Bam.Pileup: pileup :: Monad m => Stream (Of PosPrimChunks) m b -> Stream (Of Pile) m b
- Bio.Bam.Reader: concatInputs :: (MonadIO m, MonadMask m) => [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
+ Bio.Bam.Reader: concatInputs :: (MonadIO m, MonadLog m, MonadMask m) => [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
- Bio.Bam.Reader: decodeBam :: MonadIO m => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
+ Bio.Bam.Reader: decodeBam :: (MonadIO m, MonadLog m) => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
- Bio.Bam.Reader: decodeBamFile :: (MonadIO m, MonadMask m) => FilePath -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
+ Bio.Bam.Reader: decodeBamFile :: (MonadIO m, MonadLog 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: decodeBamFiles :: (MonadMask m, MonadLog 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: decodePlainBam :: MonadLog 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: decodePlainSam :: (MonadLog m, MonadIO m) => ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r)
- Bio.Bam.Reader: getSamRec :: (Bytes -> Refseq) -> Bytes -> Either ParseError BamRec
+ Bio.Bam.Reader: getSamRec :: MonadLog m => (Bytes -> Refseq) -> Bytes -> m (Either ParseError BamRec)
- 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: mergeInputsOn :: (Ord x, MonadIO m, MonadLog m, MonadMask m) => (BamRaw -> x) -> [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r
- 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: BamRec :: Bytes -> Int -> Refseq -> Int -> Qual -> Vector Cigar -> Refseq -> Int -> Int -> Vector_Nucs_half Nucleotides -> Maybe (Vector Qual) -> Extensions -> Int64 -> BamRec
- Bio.Bam.Rec: [b_qual] :: BamRec -> Vector Qual
+ Bio.Bam.Rec: [b_qual] :: BamRec -> Maybe (Vector Qual)
- Bio.Bam.Rec: bamRaw :: Int64 -> Bytes -> BamRaw
+ Bio.Bam.Rec: bamRaw :: MonadThrow m => Int64 -> Bytes -> m BamRaw
- Bio.Bam.Rmdup: cheap_collapse :: Collapse
+ Bio.Bam.Rmdup: cheap_collapse :: Monad m => Collapse m
- Bio.Bam.Rmdup: cheap_collapse_keep :: Collapse
+ Bio.Bam.Rmdup: cheap_collapse_keep :: Monad m => Collapse m
- 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: check_sort :: (MonadThrow m, Ord b) => (a -> b) -> RmdupException -> Stream (Of a) m r -> Stream (Of a) m r
- Bio.Bam.Rmdup: cons_collapse :: Qual -> Collapse
+ Bio.Bam.Rmdup: cons_collapse :: MonadLog m => Qual -> Collapse m
- Bio.Bam.Rmdup: cons_collapse_keep :: Qual -> Collapse
+ Bio.Bam.Rmdup: cons_collapse_keep :: MonadLog m => Qual -> Collapse m
- Bio.Bam.Rmdup: data Collapse
+ Bio.Bam.Rmdup: data Collapse m
- 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.Rmdup: rmdup :: (MonadLog m, MonadThrow m, Hashable l, Eq l) => Bool -> Collapse m -> Stream (Of (l, BamRec)) m r -> Stream (Of ((l, Int), BamRec)) m r
- Bio.Bam.Trim: find_merge :: [Vector Nucleotides] -> [Vector Nucleotides] -> Vector Nucleotides -> Vector Qual -> Vector Nucleotides -> Vector Qual -> (Int, Int, Int)
+ Bio.Bam.Trim: find_merge :: AD_Seqs -> AD_Seqs -> Vector Nucleotides -> Vector Qual -> Vector Nucleotides -> Vector Qual -> IO (Int, Int, Int)
- Bio.Bam.Trim: find_trim :: [Vector Nucleotides] -> Vector Nucleotides -> Vector Qual -> (Int, Int, Int)
+ Bio.Bam.Trim: find_trim :: AD_Seqs -> Vector Nucleotides -> Vector Qual -> IO (Int, Int, Int)
- Bio.Bam.Trim: mergeBam :: Int -> Int -> [Vector Nucleotides] -> [Vector Nucleotides] -> BamRec -> BamRec -> [BamRec]
+ Bio.Bam.Trim: mergeBam :: Int -> Int -> AD_Seqs -> AD_Seqs -> BamRec -> BamRec -> IO [BamRec]
- Bio.Bam.Trim: trimBam :: Int -> Int -> [Vector Nucleotides] -> BamRec -> [BamRec]
+ Bio.Bam.Trim: trimBam :: Int -> Int -> AD_Seqs -> BamRec -> IO [BamRec]
- Bio.Base: toNucleotide :: Char -> Nucleotide
+ Bio.Base: toNucleotide :: Word8 -> Nucleotide
- Bio.Base: toNucleotides :: Char -> Nucleotides
+ Bio.Base: toNucleotides :: Word8 -> Nucleotides
- Bio.Prelude: (!!) :: () => [a] -> Int -> a
+ Bio.Prelude: (!!) :: [a] -> Int -> a
- Bio.Prelude: ($!) :: () => (a -> b) -> a -> b
+ Bio.Prelude: ($!) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
- Bio.Prelude: ($) :: () => (a -> b) -> a -> b
+ Bio.Prelude: ($) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
- Bio.Prelude: (&) :: () => a -> (a -> b) -> b
+ Bio.Prelude: (&) :: a -> (a -> b) -> b
- Bio.Prelude: (++) :: () => [a] -> [a] -> [a]
+ Bio.Prelude: (++) :: [a] -> [a] -> [a]
- Bio.Prelude: (.) :: Category cat => cat b c -> cat a b -> cat a c
+ Bio.Prelude: (.) :: forall (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c
- Bio.Prelude: (<<<) :: Category cat => cat b c -> cat a b -> cat a c
+ Bio.Prelude: (<<<) :: forall k cat (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c
- Bio.Prelude: (>>>) :: Category cat => cat a b -> cat b c -> cat a c
+ Bio.Prelude: (>>>) :: forall k cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c
- Bio.Prelude: -- | Generic representation type
+ Bio.Prelude: -- | State token type
- Bio.Prelude: Alt :: f a -> Alt
+ Bio.Prelude: Alt :: f a -> Alt (f :: k -> Type) (a :: k)
- Bio.Prelude: Ap :: f a -> Ap
+ Bio.Prelude: Ap :: f a -> Ap (f :: k -> Type) (a :: k)
- Bio.Prelude: ArrowMonad :: a () b -> ArrowMonad b
+ Bio.Prelude: ArrowMonad :: a () b -> ArrowMonad (a :: Type -> Type -> Type) b
- Bio.Prelude: Const :: a -> Const a
+ Bio.Prelude: Const :: a -> Const a (b :: k)
- Bio.Prelude: Kleisli :: (a -> m b) -> Kleisli a b
+ Bio.Prelude: Kleisli :: (a -> m b) -> Kleisli (m :: Type -> Type) a b
- Bio.Prelude: Proxy :: Proxy
+ Bio.Prelude: Proxy :: Proxy (t :: k)
- Bio.Prelude: WrapArrow :: a b c -> WrappedArrow b c
+ Bio.Prelude: WrapArrow :: a b c -> WrappedArrow (a :: Type -> Type -> Type) b c
- Bio.Prelude: WrapMonad :: m a -> WrappedMonad a
+ Bio.Prelude: WrapMonad :: m a -> WrappedMonad (m :: Type -> Type) a
- Bio.Prelude: [Dynamic] :: forall a. () => TypeRep a -> a -> Dynamic
+ Bio.Prelude: [Dynamic] :: forall a. TypeRep a -> a -> Dynamic
- Bio.Prelude: [HRefl] :: forall k1 k2 (a :: k1) (b :: k2). () => a :~~: a
+ Bio.Prelude: [HRefl] :: forall k1 (a :: k1). a :~~: a
- Bio.Prelude: [Refl] :: forall k (a :: k) (b :: k). () => a :~: a
+ Bio.Prelude: [Refl] :: forall k (a :: k). a :~: a
- Bio.Prelude: [getAlt] :: Alt -> f a
+ Bio.Prelude: [getAlt] :: Alt (f :: k -> Type) (a :: k) -> f a
- Bio.Prelude: [getAp] :: Ap -> f a
+ Bio.Prelude: [getAp] :: Ap (f :: k -> Type) (a :: k) -> f a
- Bio.Prelude: [getConst] :: Const a -> a
+ Bio.Prelude: [getConst] :: Const a (b :: k) -> a
- Bio.Prelude: [runKleisli] :: Kleisli a b -> a -> m b
+ Bio.Prelude: [runKleisli] :: Kleisli (m :: Type -> Type) a b -> a -> m b
- Bio.Prelude: [unwrapArrow] :: WrappedArrow b c -> a b c
+ Bio.Prelude: [unwrapArrow] :: WrappedArrow (a :: Type -> Type -> Type) b c -> a b c
- Bio.Prelude: [unwrapMonad] :: WrappedMonad a -> m a
+ Bio.Prelude: [unwrapMonad] :: WrappedMonad (m :: Type -> Type) a -> m a
- Bio.Prelude: addForeignPtrFinalizer :: () => FinalizerPtr a -> ForeignPtr a -> IO ()
+ Bio.Prelude: addForeignPtrFinalizer :: FinalizerPtr a -> ForeignPtr a -> IO ()
- Bio.Prelude: addForeignPtrFinalizerEnv :: () => FinalizerEnvPtr env a -> Ptr env -> ForeignPtr a -> IO ()
+ Bio.Prelude: addForeignPtrFinalizerEnv :: FinalizerEnvPtr env a -> Ptr env -> ForeignPtr a -> IO ()
- Bio.Prelude: addMVarFinalizer :: () => MVar a -> IO () -> IO ()
+ Bio.Prelude: addMVarFinalizer :: MVar a -> IO () -> IO ()
- Bio.Prelude: alignPtr :: () => Ptr a -> Int -> Ptr a
+ Bio.Prelude: alignPtr :: Ptr a -> Int -> Ptr a
- Bio.Prelude: asTypeOf :: () => a -> a -> a
+ Bio.Prelude: asTypeOf :: a -> a -> a
- Bio.Prelude: assert :: () => Bool -> a -> a
+ Bio.Prelude: assert :: Bool -> a -> a
- Bio.Prelude: assertError :: ?callStack :: CallStack => Bool -> a -> a
+ Bio.Prelude: assertError :: (?callStack :: CallStack) => Bool -> a -> a
- Bio.Prelude: atomicModifyIORef :: () => IORef a -> (a -> (a, b)) -> IO b
+ Bio.Prelude: atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b
- Bio.Prelude: atomicModifyIORef' :: () => IORef a -> (a -> (a, b)) -> IO b
+ Bio.Prelude: atomicModifyIORef' :: IORef a -> (a -> (a, b)) -> IO b
- Bio.Prelude: atomicWriteIORef :: () => IORef a -> a -> IO ()
+ Bio.Prelude: atomicWriteIORef :: IORef a -> a -> IO ()
- Bio.Prelude: atomically :: () => STM a -> IO a
+ Bio.Prelude: atomically :: STM a -> IO a
- Bio.Prelude: bool :: () => a -> a -> Bool -> a
+ Bio.Prelude: bool :: a -> a -> Bool -> a
- Bio.Prelude: break :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Bio.Prelude: break :: (a -> Bool) -> [a] -> ([a], [a])
- Bio.Prelude: castForeignPtr :: () => ForeignPtr a -> ForeignPtr b
+ Bio.Prelude: castForeignPtr :: ForeignPtr a -> ForeignPtr b
- Bio.Prelude: castFunPtr :: () => FunPtr a -> FunPtr b
+ Bio.Prelude: castFunPtr :: FunPtr a -> FunPtr b
- Bio.Prelude: castFunPtrToPtr :: () => FunPtr a -> Ptr b
+ Bio.Prelude: castFunPtrToPtr :: FunPtr a -> Ptr b
- Bio.Prelude: castPtr :: () => Ptr a -> Ptr b
+ Bio.Prelude: castPtr :: Ptr a -> Ptr b
- Bio.Prelude: castPtrToFunPtr :: () => Ptr a -> FunPtr b
+ Bio.Prelude: castPtrToFunPtr :: Ptr a -> FunPtr b
- Bio.Prelude: castPtrToStablePtr :: () => Ptr () -> StablePtr a
+ Bio.Prelude: castPtrToStablePtr :: Ptr () -> StablePtr a
- Bio.Prelude: castStablePtrToPtr :: () => StablePtr a -> Ptr ()
+ Bio.Prelude: castStablePtrToPtr :: StablePtr a -> Ptr ()
- Bio.Prelude: catMaybes :: () => [Maybe a] -> [a]
+ Bio.Prelude: catMaybes :: [Maybe a] -> [a]
- Bio.Prelude: const :: () => a -> b -> a
+ Bio.Prelude: const :: a -> b -> a
- Bio.Prelude: curry :: () => ((a, b) -> c) -> a -> b -> c
+ Bio.Prelude: curry :: ((a, b) -> c) -> a -> b -> c
- Bio.Prelude: cycle :: () => [a] -> [a]
+ Bio.Prelude: cycle :: [a] -> [a]
- Bio.Prelude: data RealWorld :: Type
+ Bio.Prelude: data RealWorld
- Bio.Prelude: deRefStablePtr :: () => StablePtr a -> IO a
+ Bio.Prelude: deRefStablePtr :: StablePtr a -> IO a
- Bio.Prelude: deleteBy :: () => (a -> a -> Bool) -> a -> [a] -> [a]
+ Bio.Prelude: deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
- Bio.Prelude: deleteFirstsBy :: () => (a -> a -> Bool) -> [a] -> [a] -> [a]
+ Bio.Prelude: deleteFirstsBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
- Bio.Prelude: denominator :: () => Ratio a -> a
+ Bio.Prelude: denominator :: Ratio a -> a
- Bio.Prelude: die :: () => String -> IO a
+ Bio.Prelude: die :: String -> IO a
- Bio.Prelude: drop :: () => Int -> [a] -> [a]
+ Bio.Prelude: drop :: Int -> [a] -> [a]
- Bio.Prelude: dropWhile :: () => (a -> Bool) -> [a] -> [a]
+ Bio.Prelude: dropWhile :: (a -> Bool) -> [a] -> [a]
- Bio.Prelude: dropWhileEnd :: () => (a -> Bool) -> [a] -> [a]
+ Bio.Prelude: dropWhileEnd :: (a -> Bool) -> [a] -> [a]
- Bio.Prelude: dupChan :: () => Chan a -> IO (Chan a)
+ Bio.Prelude: dupChan :: Chan a -> IO (Chan a)
- Bio.Prelude: either :: () => (a -> c) -> (b -> c) -> Either a b -> c
+ Bio.Prelude: either :: (a -> c) -> (b -> c) -> Either a b -> c
- Bio.Prelude: eqStableName :: () => StableName a -> StableName b -> Bool
+ Bio.Prelude: eqStableName :: StableName a -> StableName b -> Bool
- Bio.Prelude: eqT :: (Typeable a, Typeable b) => Maybe (a :~: b)
+ Bio.Prelude: eqT :: forall k (a :: k) (b :: k). (Typeable a, Typeable b) => Maybe (a :~: b)
- Bio.Prelude: error :: HasCallStack => [Char] -> a
+ Bio.Prelude: error :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => [Char] -> a
- Bio.Prelude: errorWithoutStackTrace :: () => [Char] -> a
+ Bio.Prelude: errorWithoutStackTrace :: forall (r :: RuntimeRep) (a :: TYPE r). [Char] -> a
- Bio.Prelude: evaluate :: () => a -> IO a
+ Bio.Prelude: evaluate :: a -> IO a
- Bio.Prelude: fail :: Monad m => String -> m a
+ Bio.Prelude: fail :: MonadFail m => String -> m a
- Bio.Prelude: filter :: () => (a -> Bool) -> [a] -> [a]
+ Bio.Prelude: filter :: (a -> Bool) -> [a] -> [a]
- Bio.Prelude: finalizeForeignPtr :: () => ForeignPtr a -> IO ()
+ Bio.Prelude: finalizeForeignPtr :: ForeignPtr a -> IO ()
- Bio.Prelude: findIndex :: () => (a -> Bool) -> [a] -> Maybe Int
+ Bio.Prelude: findIndex :: (a -> Bool) -> [a] -> Maybe Int
- Bio.Prelude: findIndices :: () => (a -> Bool) -> [a] -> [Int]
+ Bio.Prelude: findIndices :: (a -> Bool) -> [a] -> [Int]
- Bio.Prelude: fix :: () => (a -> a) -> a
+ Bio.Prelude: fix :: (a -> a) -> a
- Bio.Prelude: fixST :: () => (a -> ST s a) -> ST s a
+ Bio.Prelude: fixST :: (a -> ST s a) -> ST s a
- Bio.Prelude: flip :: () => (a -> b -> c) -> b -> a -> c
+ Bio.Prelude: flip :: (a -> b -> c) -> b -> a -> c
- Bio.Prelude: foldl1' :: () => (a -> a -> a) -> [a] -> a
+ Bio.Prelude: foldl1' :: (a -> a -> a) -> [a] -> a
- Bio.Prelude: forkFinally :: () => IO a -> (Either SomeException a -> IO ()) -> IO ThreadId
+ Bio.Prelude: forkFinally :: IO a -> (Either SomeException a -> IO ()) -> IO ThreadId
- Bio.Prelude: freeHaskellFunPtr :: () => FunPtr a -> IO ()
+ Bio.Prelude: freeHaskellFunPtr :: FunPtr a -> IO ()
- Bio.Prelude: freeStablePtr :: () => StablePtr a -> IO ()
+ Bio.Prelude: freeStablePtr :: StablePtr a -> IO ()
- Bio.Prelude: from1 :: Generic1 f => f a -> Rep1 f a
+ Bio.Prelude: from1 :: forall (a :: k). Generic1 f => f a -> Rep1 f a
- Bio.Prelude: fromJust :: () => Maybe a -> a
+ Bio.Prelude: fromJust :: HasCallStack => Maybe a -> a
- Bio.Prelude: fromLeft :: () => a -> Either a b -> a
+ Bio.Prelude: fromLeft :: a -> Either a b -> a
- Bio.Prelude: fromMaybe :: () => a -> Maybe a -> a
+ Bio.Prelude: fromMaybe :: a -> Maybe a -> a
- Bio.Prelude: fromRight :: () => b -> Either a b -> b
+ Bio.Prelude: fromRight :: b -> Either a b -> b
- Bio.Prelude: fst :: () => (a, b) -> a
+ Bio.Prelude: fst :: (a, b) -> a
- Bio.Prelude: gcast :: (Typeable a, Typeable b) => c a -> Maybe (c b)
+ Bio.Prelude: gcast :: forall k (a :: k) (b :: k) c. (Typeable a, Typeable b) => c a -> Maybe (c b)
- Bio.Prelude: gcast1 :: (Typeable t, Typeable t') => c (t a) -> Maybe (c (t' a))
+ Bio.Prelude: gcast1 :: forall k1 k2 c (t :: k2 -> k1) (t' :: k2 -> k1) (a :: k2). (Typeable t, Typeable t') => c (t a) -> Maybe (c (t' a))
- Bio.Prelude: gcast2 :: (Typeable t, Typeable t') => c (t a b) -> Maybe (c (t' a b))
+ Bio.Prelude: gcast2 :: forall k1 k2 k3 c (t :: k2 -> k3 -> k1) (t' :: k2 -> k3 -> k1) (a :: k2) (b :: k3). (Typeable t, Typeable t') => c (t a b) -> Maybe (c (t' a b))
- Bio.Prelude: getChanContents :: () => Chan a -> IO [a]
+ Bio.Prelude: getChanContents :: Chan a -> IO [a]
- Bio.Prelude: gmapQr :: Data a => (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r
+ Bio.Prelude: gmapQr :: forall r r'. Data a => (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r
- Bio.Prelude: groupBy :: () => (a -> a -> Bool) -> [a] -> [[a]]
+ Bio.Prelude: groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
- Bio.Prelude: hashStableName :: () => StableName a -> Int
+ Bio.Prelude: hashStableName :: StableName a -> Int
- Bio.Prelude: head :: () => [a] -> a
+ Bio.Prelude: head :: [a] -> a
- Bio.Prelude: id :: Category cat => cat a a
+ Bio.Prelude: id :: forall (a :: k). Category cat => cat a a
- Bio.Prelude: imagPart :: () => Complex a -> a
+ Bio.Prelude: imagPart :: Complex a -> a
- Bio.Prelude: infixl 0 `on`
+ Bio.Prelude: infixl 0 `hashWithSalt`
- Bio.Prelude: init :: () => [a] -> [a]
+ Bio.Prelude: init :: [a] -> [a]
- Bio.Prelude: inits :: () => [a] -> [[a]]
+ Bio.Prelude: inits :: [a] -> [[a]]
- Bio.Prelude: inline :: () => a -> a
+ Bio.Prelude: inline :: a -> a
- Bio.Prelude: insertBy :: () => (a -> a -> Ordering) -> a -> [a] -> [a]
+ Bio.Prelude: insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
- Bio.Prelude: intPtrToPtr :: () => IntPtr -> Ptr a
+ Bio.Prelude: intPtrToPtr :: IntPtr -> Ptr a
- Bio.Prelude: intercalate :: () => [a] -> [[a]] -> [a]
+ Bio.Prelude: intercalate :: [a] -> [[a]] -> [a]
- Bio.Prelude: interruptible :: () => IO a -> IO a
+ Bio.Prelude: interruptible :: IO a -> IO a
- Bio.Prelude: intersectBy :: () => (a -> a -> Bool) -> [a] -> [a] -> [a]
+ Bio.Prelude: intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
- Bio.Prelude: intersperse :: () => a -> [a] -> [a]
+ Bio.Prelude: intersperse :: a -> [a] -> [a]
- Bio.Prelude: ioError :: () => IOError -> IO a
+ Bio.Prelude: ioError :: IOError -> IO a
- Bio.Prelude: ioException :: () => IOException -> IO a
+ Bio.Prelude: ioException :: IOException -> IO a
- Bio.Prelude: isEmptyMVar :: () => MVar a -> IO Bool
+ Bio.Prelude: isEmptyMVar :: MVar a -> IO Bool
- Bio.Prelude: isJust :: () => Maybe a -> Bool
+ Bio.Prelude: isJust :: Maybe a -> Bool
- Bio.Prelude: isLeft :: () => Either a b -> Bool
+ Bio.Prelude: isLeft :: Either a b -> Bool
- Bio.Prelude: isNothing :: () => Maybe a -> Bool
+ Bio.Prelude: isNothing :: Maybe a -> Bool
- Bio.Prelude: isRight :: () => Either a b -> Bool
+ Bio.Prelude: isRight :: Either a b -> Bool
- Bio.Prelude: iterate :: () => (a -> a) -> a -> [a]
+ Bio.Prelude: iterate :: (a -> a) -> a -> [a]
- Bio.Prelude: iterate' :: () => (a -> a) -> a -> [a]
+ Bio.Prelude: iterate' :: (a -> a) -> a -> [a]
- Bio.Prelude: last :: () => [a] -> a
+ Bio.Prelude: last :: [a] -> a
- Bio.Prelude: lazy :: () => a -> a
+ Bio.Prelude: lazy :: a -> a
- Bio.Prelude: lefts :: () => [Either a b] -> [a]
+ Bio.Prelude: lefts :: [Either a b] -> [a]
- Bio.Prelude: listToMaybe :: () => [a] -> Maybe a
+ Bio.Prelude: listToMaybe :: [a] -> Maybe a
- Bio.Prelude: makeStableName :: () => a -> IO (StableName a)
+ Bio.Prelude: makeStableName :: a -> IO (StableName a)
- Bio.Prelude: mallocForeignPtrBytes :: () => Int -> IO (ForeignPtr a)
+ Bio.Prelude: mallocForeignPtrBytes :: Int -> IO (ForeignPtr a)
- Bio.Prelude: map :: () => (a -> b) -> [a] -> [b]
+ Bio.Prelude: map :: (a -> b) -> [a] -> [b]
- Bio.Prelude: mapMaybe :: () => (a -> Maybe b) -> [a] -> [b]
+ Bio.Prelude: mapMaybe :: (a -> Maybe b) -> [a] -> [b]
- Bio.Prelude: maybe :: () => b -> (a -> b) -> Maybe a -> b
+ Bio.Prelude: maybe :: b -> (a -> b) -> Maybe a -> b
- Bio.Prelude: maybeToList :: () => Maybe a -> [a]
+ Bio.Prelude: maybeToList :: Maybe a -> [a]
- Bio.Prelude: minusPtr :: () => Ptr a -> Ptr b -> Int
+ Bio.Prelude: minusPtr :: Ptr a -> Ptr b -> Int
- Bio.Prelude: mkWeakIORef :: () => IORef a -> IO () -> IO (Weak (IORef a))
+ Bio.Prelude: mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a))
- Bio.Prelude: mkWeakMVar :: () => MVar a -> IO () -> IO (Weak (MVar a))
+ Bio.Prelude: mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a))
- Bio.Prelude: modifyIOError :: () => (IOError -> IOError) -> IO a -> IO a
+ Bio.Prelude: modifyIOError :: (IOError -> IOError) -> IO a -> IO a
- Bio.Prelude: modifyIORef :: () => IORef a -> (a -> a) -> IO ()
+ Bio.Prelude: modifyIORef :: IORef a -> (a -> a) -> IO ()
- Bio.Prelude: modifyIORef' :: () => IORef a -> (a -> a) -> IO ()
+ Bio.Prelude: modifyIORef' :: IORef a -> (a -> a) -> IO ()
- Bio.Prelude: modifyMVar :: () => MVar a -> (a -> IO (a, b)) -> IO b
+ Bio.Prelude: modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
- Bio.Prelude: modifyMVarMasked :: () => MVar a -> (a -> IO (a, b)) -> IO b
+ Bio.Prelude: modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b
- Bio.Prelude: modifyMVarMasked_ :: () => MVar a -> (a -> IO a) -> IO ()
+ Bio.Prelude: modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO ()
- Bio.Prelude: modifyMVar_ :: () => MVar a -> (a -> IO a) -> IO ()
+ Bio.Prelude: modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
- Bio.Prelude: modifySTRef :: () => STRef s a -> (a -> a) -> ST s ()
+ Bio.Prelude: modifySTRef :: STRef s a -> (a -> a) -> ST s ()
- Bio.Prelude: modifySTRef' :: () => STRef s a -> (a -> a) -> ST s ()
+ Bio.Prelude: modifySTRef' :: STRef s a -> (a -> a) -> ST s ()
- Bio.Prelude: newChan :: () => IO (Chan a)
+ Bio.Prelude: newChan :: IO (Chan a)
- Bio.Prelude: newEmptyMVar :: () => IO (MVar a)
+ Bio.Prelude: newEmptyMVar :: IO (MVar a)
- Bio.Prelude: newForeignPtr :: () => FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
+ Bio.Prelude: newForeignPtr :: FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
- Bio.Prelude: newForeignPtrEnv :: () => FinalizerEnvPtr env a -> Ptr env -> Ptr a -> IO (ForeignPtr a)
+ Bio.Prelude: newForeignPtrEnv :: FinalizerEnvPtr env a -> Ptr env -> Ptr a -> IO (ForeignPtr a)
- Bio.Prelude: newForeignPtr_ :: () => Ptr a -> IO (ForeignPtr a)
+ Bio.Prelude: newForeignPtr_ :: Ptr a -> IO (ForeignPtr a)
- Bio.Prelude: newIORef :: () => a -> IO (IORef a)
+ Bio.Prelude: newIORef :: a -> IO (IORef a)
- Bio.Prelude: newMVar :: () => a -> IO (MVar a)
+ Bio.Prelude: newMVar :: a -> IO (MVar a)
- Bio.Prelude: newSTRef :: () => a -> ST s (STRef s a)
+ Bio.Prelude: newSTRef :: a -> ST s (STRef s a)
- Bio.Prelude: newStablePtr :: () => a -> IO (StablePtr a)
+ Bio.Prelude: newStablePtr :: a -> IO (StablePtr a)
- Bio.Prelude: newTVar :: () => a -> STM (TVar a)
+ Bio.Prelude: newTVar :: a -> STM (TVar a)
- Bio.Prelude: newTVarIO :: () => a -> IO (TVar a)
+ Bio.Prelude: newTVarIO :: a -> IO (TVar a)
- Bio.Prelude: nubBy :: () => (a -> a -> Bool) -> [a] -> [a]
+ Bio.Prelude: nubBy :: (a -> a -> Bool) -> [a] -> [a]
- Bio.Prelude: nullFunPtr :: () => FunPtr a
+ Bio.Prelude: nullFunPtr :: FunPtr a
- Bio.Prelude: nullPtr :: () => Ptr a
+ Bio.Prelude: nullPtr :: Ptr a
- Bio.Prelude: numerator :: () => Ratio a -> a
+ Bio.Prelude: numerator :: Ratio a -> a
- Bio.Prelude: on :: () => (b -> b -> c) -> (a -> b) -> a -> a -> c
+ Bio.Prelude: on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
- Bio.Prelude: orElse :: () => STM a -> STM a -> STM a
+ Bio.Prelude: orElse :: STM a -> STM a -> STM a
- Bio.Prelude: par :: () => a -> b -> b
+ Bio.Prelude: par :: a -> b -> b
- Bio.Prelude: partition :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Bio.Prelude: partition :: (a -> Bool) -> [a] -> ([a], [a])
- Bio.Prelude: partitionEithers :: () => [Either a b] -> ([a], [b])
+ Bio.Prelude: partitionEithers :: [Either a b] -> ([a], [b])
- Bio.Prelude: permutations :: () => [a] -> [[a]]
+ Bio.Prelude: permutations :: [a] -> [[a]]
- Bio.Prelude: plusForeignPtr :: () => ForeignPtr a -> Int -> ForeignPtr b
+ Bio.Prelude: plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b
- Bio.Prelude: plusPtr :: () => Ptr a -> Int -> Ptr b
+ Bio.Prelude: plusPtr :: Ptr a -> Int -> Ptr b
- Bio.Prelude: pseq :: () => a -> b -> b
+ Bio.Prelude: pseq :: a -> b -> b
- Bio.Prelude: ptrToIntPtr :: () => Ptr a -> IntPtr
+ Bio.Prelude: ptrToIntPtr :: Ptr a -> IntPtr
- Bio.Prelude: ptrToWordPtr :: () => Ptr a -> WordPtr
+ Bio.Prelude: ptrToWordPtr :: Ptr a -> WordPtr
- Bio.Prelude: putMVar :: () => MVar a -> a -> IO ()
+ Bio.Prelude: putMVar :: MVar a -> a -> IO ()
- Bio.Prelude: readChan :: () => Chan a -> IO a
+ Bio.Prelude: readChan :: Chan a -> IO a
- Bio.Prelude: readIORef :: () => IORef a -> IO a
+ Bio.Prelude: readIORef :: IORef a -> IO a
- Bio.Prelude: readMVar :: () => MVar a -> IO a
+ Bio.Prelude: readMVar :: MVar a -> IO a
- Bio.Prelude: readP_to_Prec :: () => (Int -> ReadP a) -> ReadPrec a
+ Bio.Prelude: readP_to_Prec :: (Int -> ReadP a) -> ReadPrec a
- Bio.Prelude: readP_to_S :: () => ReadP a -> ReadS a
+ Bio.Prelude: readP_to_S :: ReadP a -> ReadS a
- Bio.Prelude: readParen :: () => Bool -> ReadS a -> ReadS a
+ Bio.Prelude: readParen :: Bool -> ReadS a -> ReadS a
- Bio.Prelude: readPrec_to_P :: () => ReadPrec a -> Int -> ReadP a
+ Bio.Prelude: readPrec_to_P :: ReadPrec a -> Int -> ReadP a
- Bio.Prelude: readPrec_to_S :: () => ReadPrec a -> Int -> ReadS a
+ Bio.Prelude: readPrec_to_S :: ReadPrec a -> Int -> ReadS a
- Bio.Prelude: readSTRef :: () => STRef s a -> ST s a
+ Bio.Prelude: readSTRef :: STRef s a -> ST s a
- Bio.Prelude: readS_to_P :: () => ReadS a -> ReadP a
+ Bio.Prelude: readS_to_P :: ReadS a -> ReadP a
- Bio.Prelude: readS_to_Prec :: () => (Int -> ReadS a) -> ReadPrec a
+ Bio.Prelude: readS_to_Prec :: (Int -> ReadS a) -> ReadPrec a
- Bio.Prelude: readTVar :: () => TVar a -> STM a
+ Bio.Prelude: readTVar :: TVar a -> STM a
- Bio.Prelude: readTVarIO :: () => TVar a -> IO a
+ Bio.Prelude: readTVarIO :: TVar a -> IO a
- Bio.Prelude: realPart :: () => Complex a -> a
+ Bio.Prelude: realPart :: Complex a -> a
- Bio.Prelude: repeat :: () => a -> [a]
+ Bio.Prelude: repeat :: a -> [a]
- Bio.Prelude: replicate :: () => Int -> a -> [a]
+ Bio.Prelude: replicate :: Int -> a -> [a]
- Bio.Prelude: retry :: () => STM a
+ Bio.Prelude: retry :: STM a
- Bio.Prelude: reverse :: () => [a] -> [a]
+ Bio.Prelude: reverse :: [a] -> [a]
- Bio.Prelude: rights :: () => [Either a b] -> [b]
+ Bio.Prelude: rights :: [Either a b] -> [b]
- Bio.Prelude: runInBoundThread :: () => IO a -> IO a
+ Bio.Prelude: runInBoundThread :: IO a -> IO a
- Bio.Prelude: runInUnboundThread :: () => IO a -> IO a
+ Bio.Prelude: runInUnboundThread :: IO a -> IO a
- Bio.Prelude: runST :: () => (forall s. () => ST s a) -> a
+ Bio.Prelude: runST :: (forall s. () => ST s a) -> a
- Bio.Prelude: scanl :: () => (b -> a -> b) -> b -> [a] -> [b]
+ Bio.Prelude: scanl :: (b -> a -> b) -> b -> [a] -> [b]
- Bio.Prelude: scanl' :: () => (b -> a -> b) -> b -> [a] -> [b]
+ Bio.Prelude: scanl' :: (b -> a -> b) -> b -> [a] -> [b]
- Bio.Prelude: scanl1 :: () => (a -> a -> a) -> [a] -> [a]
+ Bio.Prelude: scanl1 :: (a -> a -> a) -> [a] -> [a]
- Bio.Prelude: scanr :: () => (a -> b -> b) -> b -> [a] -> [b]
+ Bio.Prelude: scanr :: (a -> b -> b) -> b -> [a] -> [b]
- Bio.Prelude: scanr1 :: () => (a -> a -> a) -> [a] -> [a]
+ Bio.Prelude: scanr1 :: (a -> a -> a) -> [a] -> [a]
- Bio.Prelude: seq :: () => a -> b -> b
+ Bio.Prelude: seq :: a -> b -> b
- Bio.Prelude: snd :: () => (a, b) -> b
+ Bio.Prelude: snd :: (a, b) -> b
- Bio.Prelude: sortBy :: () => (a -> a -> Ordering) -> [a] -> [a]
+ Bio.Prelude: sortBy :: (a -> a -> Ordering) -> [a] -> [a]
- Bio.Prelude: span :: () => (a -> Bool) -> [a] -> ([a], [a])
+ Bio.Prelude: span :: (a -> Bool) -> [a] -> ([a], [a])
- Bio.Prelude: splitAt :: () => Int -> [a] -> ([a], [a])
+ Bio.Prelude: splitAt :: Int -> [a] -> ([a], [a])
- Bio.Prelude: stToIO :: () => ST RealWorld a -> IO a
+ Bio.Prelude: stToIO :: ST RealWorld a -> IO a
- Bio.Prelude: subsequences :: () => [a] -> [[a]]
+ Bio.Prelude: subsequences :: [a] -> [[a]]
- Bio.Prelude: swap :: () => (a, b) -> (b, a)
+ Bio.Prelude: swap :: (a, b) -> (b, a)
- Bio.Prelude: swapMVar :: () => MVar a -> a -> IO a
+ Bio.Prelude: swapMVar :: MVar a -> a -> IO a
- Bio.Prelude: tail :: () => [a] -> [a]
+ Bio.Prelude: tail :: [a] -> [a]
- Bio.Prelude: tails :: () => [a] -> [[a]]
+ Bio.Prelude: tails :: [a] -> [[a]]
- Bio.Prelude: take :: () => Int -> [a] -> [a]
+ Bio.Prelude: take :: Int -> [a] -> [a]
- Bio.Prelude: takeMVar :: () => MVar a -> IO a
+ Bio.Prelude: takeMVar :: MVar a -> IO a
- Bio.Prelude: takeWhile :: () => (a -> Bool) -> [a] -> [a]
+ Bio.Prelude: takeWhile :: (a -> Bool) -> [a] -> [a]
- Bio.Prelude: throw :: Exception e => e -> a
+ Bio.Prelude: throw :: forall (r :: RuntimeRep) (a :: TYPE r) e. Exception e => e -> a
- Bio.Prelude: timeout :: () => Int -> IO a -> IO (Maybe a)
+ Bio.Prelude: timeout :: Int -> IO a -> IO (Maybe a)
- Bio.Prelude: to1 :: Generic1 f => Rep1 f a -> f a
+ Bio.Prelude: to1 :: forall (a :: k). Generic1 f => Rep1 f a -> f a
- Bio.Prelude: touchForeignPtr :: () => ForeignPtr a -> IO ()
+ Bio.Prelude: touchForeignPtr :: ForeignPtr a -> IO ()
- Bio.Prelude: trace :: () => String -> a -> a
+ Bio.Prelude: trace :: String -> a -> a
- Bio.Prelude: traceEvent :: () => String -> a -> a
+ Bio.Prelude: traceEvent :: String -> a -> a
- Bio.Prelude: traceMarker :: () => String -> a -> a
+ Bio.Prelude: traceMarker :: String -> a -> a
- Bio.Prelude: traceStack :: () => String -> a -> a
+ Bio.Prelude: traceStack :: String -> a -> a
- Bio.Prelude: transpose :: () => [[a]] -> [[a]]
+ Bio.Prelude: transpose :: [[a]] -> [[a]]
- Bio.Prelude: tryIOError :: () => IO a -> IO (Either IOError a)
+ Bio.Prelude: tryIOError :: IO a -> IO (Either IOError a)
- Bio.Prelude: tryPutMVar :: () => MVar a -> a -> IO Bool
+ Bio.Prelude: tryPutMVar :: MVar a -> a -> IO Bool
- Bio.Prelude: tryReadMVar :: () => MVar a -> IO (Maybe a)
+ Bio.Prelude: tryReadMVar :: MVar a -> IO (Maybe a)
- Bio.Prelude: tryTakeMVar :: () => MVar a -> IO (Maybe a)
+ Bio.Prelude: tryTakeMVar :: MVar a -> IO (Maybe a)
- Bio.Prelude: type family Rep1 (f :: k -> Type) :: k -> Type;
+ Bio.Prelude: type family PrimState (m :: Type -> Type);
- Bio.Prelude: typeRep :: Typeable a => proxy a -> TypeRep
+ Bio.Prelude: typeRep :: forall k proxy (a :: k). Typeable a => proxy a -> TypeRep
- Bio.Prelude: uncons :: () => [a] -> Maybe (a, [a])
+ Bio.Prelude: uncons :: [a] -> Maybe (a, [a])
- Bio.Prelude: uncurry :: () => (a -> b -> c) -> (a, b) -> c
+ Bio.Prelude: uncurry :: (a -> b -> c) -> (a, b) -> c
- Bio.Prelude: undefined :: HasCallStack => a
+ Bio.Prelude: undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
- Bio.Prelude: unfoldr :: () => (b -> Maybe (a, b)) -> b -> [a]
+ Bio.Prelude: unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
- Bio.Prelude: unionBy :: () => (a -> a -> Bool) -> [a] -> [a] -> [a]
+ Bio.Prelude: unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
- Bio.Prelude: unsafeCoerce :: () => a -> b
+ Bio.Prelude: unsafeCoerce :: a -> b
- Bio.Prelude: unsafeDupablePerformIO :: () => IO a -> a
+ Bio.Prelude: unsafeDupablePerformIO :: IO a -> a
- Bio.Prelude: unsafeFixIO :: () => (a -> IO a) -> IO a
+ Bio.Prelude: unsafeFixIO :: (a -> IO a) -> IO a
- Bio.Prelude: unsafeIOToSTM :: () => IO a -> STM a
+ Bio.Prelude: unsafeIOToSTM :: IO a -> STM a
- Bio.Prelude: unsafeInterleaveIO :: () => IO a -> IO a
+ Bio.Prelude: unsafeInterleaveIO :: IO a -> IO a
- Bio.Prelude: unsafePerformIO :: () => IO a -> a
+ Bio.Prelude: unsafePerformIO :: IO a -> a
- Bio.Prelude: until :: () => (a -> Bool) -> (a -> a) -> a -> a
+ Bio.Prelude: until :: (a -> Bool) -> (a -> a) -> a -> a
- Bio.Prelude: unzip :: () => [(a, b)] -> ([a], [b])
+ Bio.Prelude: unzip :: [(a, b)] -> ([a], [b])
- Bio.Prelude: unzip3 :: () => [(a, b, c)] -> ([a], [b], [c])
+ Bio.Prelude: unzip3 :: [(a, b, c)] -> ([a], [b], [c])
- Bio.Prelude: unzip4 :: () => [(a, b, c, d)] -> ([a], [b], [c], [d])
+ Bio.Prelude: unzip4 :: [(a, b, c, d)] -> ([a], [b], [c], [d])
- Bio.Prelude: unzip5 :: () => [(a, b, c, d, e)] -> ([a], [b], [c], [d], [e])
+ Bio.Prelude: unzip5 :: [(a, b, c, d, e)] -> ([a], [b], [c], [d], [e])
- Bio.Prelude: unzip6 :: () => [(a, b, c, d, e, f)] -> ([a], [b], [c], [d], [e], [f])
+ Bio.Prelude: unzip6 :: [(a, b, c, d, e, f)] -> ([a], [b], [c], [d], [e], [f])
- Bio.Prelude: unzip7 :: () => [(a, b, c, d, e, f, g)] -> ([a], [b], [c], [d], [e], [f], [g])
+ Bio.Prelude: unzip7 :: [(a, b, c, d, e, f, g)] -> ([a], [b], [c], [d], [e], [f], [g])
- Bio.Prelude: withArgs :: () => [String] -> IO a -> IO a
+ Bio.Prelude: withArgs :: [String] -> IO a -> IO a
- Bio.Prelude: withBinaryFile :: () => FilePath -> IOMode -> (Handle -> IO r) -> IO r
+ Bio.Prelude: withBinaryFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
- Bio.Prelude: withForeignPtr :: () => ForeignPtr a -> (Ptr a -> IO b) -> IO b
+ Bio.Prelude: withForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b
- Bio.Prelude: withMVar :: () => MVar a -> (a -> IO b) -> IO b
+ Bio.Prelude: withMVar :: MVar a -> (a -> IO b) -> IO b
- Bio.Prelude: withMVarMasked :: () => MVar a -> (a -> IO b) -> IO b
+ Bio.Prelude: withMVarMasked :: MVar a -> (a -> IO b) -> IO b
- Bio.Prelude: withProgName :: () => String -> IO a -> IO a
+ Bio.Prelude: withProgName :: String -> IO a -> IO a
- Bio.Prelude: wordPtrToPtr :: () => WordPtr -> Ptr a
+ Bio.Prelude: wordPtrToPtr :: WordPtr -> Ptr a
- Bio.Prelude: writeChan :: () => Chan a -> a -> IO ()
+ Bio.Prelude: writeChan :: Chan a -> a -> IO ()
- Bio.Prelude: writeIORef :: () => IORef a -> a -> IO ()
+ Bio.Prelude: writeIORef :: IORef a -> a -> IO ()
- Bio.Prelude: writeList2Chan :: () => Chan a -> [a] -> IO ()
+ Bio.Prelude: writeList2Chan :: Chan a -> [a] -> IO ()
- Bio.Prelude: writeSTRef :: () => STRef s a -> a -> ST s ()
+ Bio.Prelude: writeSTRef :: STRef s a -> a -> ST s ()
- Bio.Prelude: writeTVar :: () => TVar a -> a -> STM ()
+ Bio.Prelude: writeTVar :: TVar a -> a -> STM ()
- Bio.Prelude: zip :: () => [a] -> [b] -> [(a, b)]
+ Bio.Prelude: zip :: [a] -> [b] -> [(a, b)]
- Bio.Prelude: zip3 :: () => [a] -> [b] -> [c] -> [(a, b, c)]
+ Bio.Prelude: zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
- Bio.Prelude: zip4 :: () => [a] -> [b] -> [c] -> [d] -> [(a, b, c, d)]
+ Bio.Prelude: zip4 :: [a] -> [b] -> [c] -> [d] -> [(a, b, c, d)]
- Bio.Prelude: zip5 :: () => [a] -> [b] -> [c] -> [d] -> [e] -> [(a, b, c, d, e)]
+ Bio.Prelude: zip5 :: [a] -> [b] -> [c] -> [d] -> [e] -> [(a, b, c, d, e)]
- Bio.Prelude: zip6 :: () => [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [(a, b, c, d, e, f)]
+ Bio.Prelude: zip6 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [(a, b, c, d, e, f)]
- Bio.Prelude: zip7 :: () => [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [(a, b, c, d, e, f, g)]
+ Bio.Prelude: zip7 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [(a, b, c, d, e, f, g)]
- Bio.Prelude: zipWith :: () => (a -> b -> c) -> [a] -> [b] -> [c]
+ Bio.Prelude: zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
- Bio.Prelude: zipWith3 :: () => (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
+ Bio.Prelude: zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
- Bio.Prelude: zipWith4 :: () => (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
+ Bio.Prelude: zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
- Bio.Prelude: zipWith5 :: () => (a -> b -> c -> d -> e -> f) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f]
+ Bio.Prelude: zipWith5 :: (a -> b -> c -> d -> e -> f) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f]
- Bio.Prelude: zipWith6 :: () => (a -> b -> c -> d -> e -> f -> g) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g]
+ Bio.Prelude: zipWith6 :: (a -> b -> c -> d -> e -> f -> g) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g]
- Bio.Prelude: zipWith7 :: () => (a -> b -> c -> d -> e -> f -> g -> h) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [h]
+ Bio.Prelude: zipWith7 :: (a -> b -> c -> d -> e -> f -> g -> h) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [h]
- Bio.Streaming: Compose :: f (g a) -> Compose
+ Bio.Streaming: Compose :: f (g a) -> Compose (f :: k -> Type) (g :: k1 -> k) (a :: k1)
- Bio.Streaming: InL :: f a -> Sum
+ Bio.Streaming: InL :: f a -> Sum (f :: k -> Type) (g :: k -> Type) (a :: k)
- Bio.Streaming: InR :: g a -> Sum
+ Bio.Streaming: InR :: g a -> Sum (f :: k -> Type) (g :: k -> Type) (a :: k)
- Bio.Streaming: [getCompose] :: Compose -> f (g a)
+ Bio.Streaming: [getCompose] :: Compose (f :: k -> Type) (g :: k1 -> k) (a :: k1) -> f (g a)
- Bio.Streaming: chunksOf :: (Monad m, Functor f) => Int -> Stream f m r -> Stream (Stream f m) m r
+ Bio.Streaming: chunksOf :: forall (m :: Type -> Type) (f :: Type -> Type) r. (Monad m, Functor f) => Int -> Stream f m r -> Stream (Stream f m) m r
- Bio.Streaming: concats :: (Monad m, Functor f) => Stream (Stream f m) m r -> Stream f m r
+ Bio.Streaming: concats :: forall (m :: Type -> Type) (f :: Type -> Type) r. (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: cutoff :: forall (m :: Type -> Type) (f :: Type -> Type) r. (Monad m, Functor f) => Int -> Stream f m r -> Stream f m (Maybe r)
- Bio.Streaming: decompose :: (Monad m, Functor f) => Stream (Compose m f) m r -> Stream f m r
+ Bio.Streaming: decompose :: forall (m :: Type -> Type) (f :: Type -> Type) r. (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: delays :: forall (m :: Type -> Type) (f :: Type -> Type) r. (MonadIO m, Applicative f) => Double -> Stream f m r
- 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: distribute :: forall (m :: Type -> Type) (f :: Type -> Type) t r. (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: each :: forall (m :: Type -> Type) f a. (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: effect :: forall m (f :: Type -> Type) r. (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: embed :: forall (n :: Type -> Type) m b. (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: expand :: forall (m :: Type -> Type) f g h r. (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: expandPost :: forall (m :: Type -> Type) g f h r. (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: 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: groups :: forall (m :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) r. (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: hoist :: forall m n (b :: k). (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: hoistUnexposed :: forall m (f :: Type -> Type) n r. (Monad m, Functor f) => (forall a. () => m a -> n a) -> Stream f m r -> Stream f n r
- Bio.Streaming: intercalates :: (Monad m, Monad (t m), MonadTrans t) => t m x -> Stream (t m) m r -> t m r
+ Bio.Streaming: intercalates :: forall (m :: Type -> Type) t x r. (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: interleaves :: forall (m :: Type -> Type) (h :: Type -> Type) r. (Monad m, Applicative h) => Stream h m r -> Stream h m r -> Stream h m r
- 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: iterTM :: forall f (m :: Type -> Type) t a. (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: lazily :: () => Of a b -> (a, b)
+ Bio.Streaming: lazily :: Of a b -> (a, b)
- Bio.Streaming: maps :: (Monad m, Functor f) => (forall x. () => f x -> g x) -> Stream f m r -> Stream g m r
+ Bio.Streaming: maps :: forall (m :: Type -> Type) f g r. (Monad m, Functor f) => (forall x. () => f x -> 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: mapsMPost :: forall m f g r. (Monad m, Functor g) => (forall x. () => f x -> m (g x)) -> Stream f m r -> Stream g 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: mapsPost :: forall (m :: Type -> Type) f g r. (Monad m, Functor g) => (forall x. () => f x -> g x) -> Stream f m r -> Stream g m r
- Bio.Streaming: never :: (Monad m, Applicative f) => Stream f m r
+ Bio.Streaming: never :: forall (m :: Type -> Type) (f :: Type -> Type) r. (Monad m, Applicative f) => Stream f m r
- Bio.Streaming: progressGen :: MonadIO m => (Int -> a -> String) -> Int -> (String -> IO ()) -> Stream (Of a) m r -> Stream (Of a) m r
+ Bio.Streaming: progressGen :: MonadLog m => (Int -> a -> String) -> Int -> 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: progressNum :: MonadLog m => String -> Int -> 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: progressPos :: MonadLog m => (a -> (Refseq, Int)) -> String -> Refs -> Int -> Stream (Of a) m r -> Stream (Of a) m r
- Bio.Streaming: repeats :: (Monad m, Functor f) => f () -> Stream f m r
+ Bio.Streaming: repeats :: forall (m :: Type -> Type) f r. (Monad m, Functor f) => f () -> Stream f m r
- Bio.Streaming: replicates :: (Monad m, Functor f) => Int -> f () -> Stream f m ()
+ Bio.Streaming: replicates :: forall (m :: Type -> Type) f. (Monad m, Functor f) => Int -> f () -> Stream f m ()
- Bio.Streaming: separate :: (Monad m, Functor f, Functor g) => Stream (Sum f g) m r -> Stream f (Stream g m) r
+ Bio.Streaming: separate :: forall (m :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) r. (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: splitsAt :: forall (m :: Type -> Type) (f :: Type -> Type) r. (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: streamBuild :: (forall b. () => (r -> b) -> (m b -> b) -> (f b -> b) -> b) -> Stream f m r
- Bio.Streaming: strictly :: () => (a, b) -> Of a b
+ 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: takes :: forall (m :: Type -> Type) (f :: Type -> Type) r. (Monad m, Functor f) => Int -> Stream f m r -> Stream f m ()
- Bio.Streaming: unseparate :: (Monad m, Functor f, Functor g) => Stream f (Stream g m) r -> Stream (Sum f g) m r
+ Bio.Streaming: unseparate :: forall (m :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) r. (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: untilJust :: forall m (f :: Type -> Type) r. (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: unzips :: forall (m :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) r. (Monad m, Functor f, Functor g) => Stream (Compose f g) m r -> Stream f (Stream g m) r
- Bio.Streaming: wrap :: (Monad m, Functor f) => f (Stream f m r) -> Stream f m r
+ Bio.Streaming: wrap :: forall (m :: Type -> Type) f r. (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: yields :: forall (m :: Type -> Type) f r. (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: zips :: forall (m :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) r. (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 :: forall f g h (m :: Type -> Type) r. (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: zipsWith' :: forall f g h (m :: Type -> Type) r. 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.Parse: parse :: Monad m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either SomeException (Either r (a, ByteStream m r)))
+ Bio.Streaming.Parse: parse :: Monad m => (Int64 -> Parser r m a) -> ByteStream m r -> m (Either (SomeException, ByteStream m r) (Either r (a, ByteStream m r)))
- Bio.TwoBit: TBF :: ByteString -> !HashMap Bytes TwoBitSequence -> TwoBitFile
+ Bio.TwoBit: TBF :: ForeignPtr CChar -> !HashMap Bytes TwoBitSequence -> TwoBitFile
- Bio.TwoBit: [tbf_raw] :: TwoBitFile -> ByteString
+ Bio.TwoBit: [tbf_raw] :: TwoBitFile -> ForeignPtr CChar
- Bio.Util.Numeric: showOOM :: Double -> String
+ Bio.Util.Numeric: showOOM :: (Enum a, Num a, Ord a) => a -> String
Files
- Bio/Bam/Evan.hs +18/−1
- Bio/Bam/Fastq.hs +184/−97
- Bio/Bam/Filter.hs +9/−7
- Bio/Bam/Header.hs +26/−28
- Bio/Bam/Index.hs +8/−7
- Bio/Bam/Pileup.hs +2/−11
- Bio/Bam/Reader.hs +39/−40
- Bio/Bam/Rec.hs +37/−13
- Bio/Bam/Rmdup.hs +116/−103
- Bio/Bam/Trim.hs +163/−188
- Bio/Bam/Writer.hs +7/−7
- Bio/Base.hs +41/−39
- Bio/Prelude.hs +6/−4
- Bio/Streaming.hs +28/−16
- Bio/Streaming/Bgzf.hs +35/−8
- Bio/Streaming/Bytes.hs +17/−7
- Bio/Streaming/Parse.hs +28/−22
- Bio/TwoBit.hs +26/−21
- Bio/Util/MMap.hs +0/−28
- Bio/Util/MMap.hsc +43/−0
- Bio/Util/Numeric.hs +21/−10
- Bio/Util/Text.hs +2/−0
- CHANGELOG.md +10/−1
- Control/Monad/Log.hs +247/−0
- Setup.hs +50/−2
- biohazard.cabal +37/−26
- cbits/mmap.c +0/−10
- cbits/trim.c +78/−11
- cbits/util.c +8/−0
Bio/Bam/Evan.hs view
@@ -71,7 +71,24 @@ -- | Removes syntactic warts from old read names or the read names used--- in FastQ files.+-- in FastQ files. Supported 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.+ removeWarts :: BamRec -> BamRec removeWarts br = br { b_qname = name, b_flag = flags, b_exts = tags } where
Bio/Bam/Fastq.hs view
@@ -1,121 +1,208 @@--- | 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.+-- | Parser for @FastA/FastQ@, 'ByteStream' style, written such that it+-- works well with module "Bio.Bam".+--+-- Input streams are broken into numbered lines, then into records.+-- Records can start with empty lines, which are ignored, or random+-- junk, which is ignored, but results in a warning, followed by a+-- header indicating either a @FastA@ (begins with @\>@ or @;@) or+-- @FastQ@ record (begins with @\@@). More description lines begining+-- with @;@ are allowed, and silently ignored. All following lines not+-- starting with @+@, @\>@, @;@ or @\@@ are sequence lines. (Only) in a+-- @FastQ@ record, this is followed by a separator line starting with a+-- @+@, which is ignored, and exactly as many quality lines as there+-- were sequence lines. A missing separator results in a warning and+-- the record being parsed without quality scores.+--+-- In sequence lines, IUPAC-IUB ambiguity codes are converted to+-- 'Nucleotides', white space is skipped silently. Any other character+-- becomes an unknown base ('=' in SAM) and a warning is emitted. Note+-- that downstream tools are unlikely to handle the resulting unknown+-- bases and/or empty records gracefully. If the quality lines do not+-- have the same total length as the sequence lines (this includes+-- missing quality lines due to end-of-stream), a warning is emitted,+-- and the record receives no quality scores (just as if it was a+-- @FastA@ record). Else, if the quality lines have a different layout+-- than the sequence lines, a warning is emitted, but they are still+-- used.+--+-- Quality scores must be stored as raw bytes with offset 33. (Other+-- variants, like 454's ASCII qualities and Solexa's raw bytes with+-- offset 64 are difficult to detect, and extinct in the wild anyway.)+-- If the second word of the header stores multiple fields, we try to+-- extract Illumina's \"QC failed\" flag and either an index sequence or+-- a read group name from it.+--+-- Other flags are commonly encoded into the sequence names. We do not+-- handle those here, but most of the conventions at MPI EVAN are dealt+-- with by 'Bio.Bam.Evan.removeWarts'. -module Bio.Bam.Fastq ( parseFastq, parseFastqWith, parseFastqCassava ) where+module Bio.Bam.Fastq+ ( parseFastq+ , EmptyRecord(..)+ , IncoherentQualities(..)+ , IncongruentQualities(..)+ , JunkFound(..)+ , QualitiesMissing(..)+ , SequenceHasGaps(..)+ ) where import Bio.Bam.Header import Bio.Bam.Rec-import Bio.Prelude hiding ( isSpace )+import Bio.Prelude 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 Bio.Streaming.Bytes ( lines' ) -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 Data.ByteString.Unsafe as B+import qualified Data.Vector.Storable as W 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.+-- | Emitted when random text is found instead of a header.+data JunkFound = JunkFound !Int !Bytes deriving (Typeable, Show) -parseFastq :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)-parseFastq = parseFastqWith (const id)+instance Exception JunkFound where+ displayException (JunkFound n s) = printf "junk found at line %d: %s" n (unpack s) --- | 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.+-- | Emitted when a quality separator was expected, but not found.+data QualitiesMissing = QualitiesMissing !Int !Bytes deriving (Typeable, Show) -parseFastqCassava :: Monad m => ByteStream m r -> Stream (Of BamRec) m (Either SomeException r)-parseFastqCassava = parseFastqWith (pdesc . C.split ':' . C.takeWhile (' ' /=))+instance Exception QualitiesMissing where+ displayException (QualitiesMissing 0 _) = printf "expected '+' symbol at end of file"+ displayException (QualitiesMissing n s) = printf "expected '+' symbol at line %d, but found %s" n (unpack s)++-- | Emitted when a quality record does not fit the sequence record.+data IncoherentQualities = IncoherentQualities !Int !Bytes deriving (Typeable, Show)++instance Exception IncoherentQualities where+ displayException (IncoherentQualities n s) = printf "quality record of incorrect length ignored at line %d (%s)" n (unpack s)++-- | Emitted when a quality record has different layout than the+-- sequence.+data IncongruentQualities = IncongruentQualities !Int !Bytes deriving (Typeable, Show)++instance Exception IncongruentQualities where+ displayException (IncongruentQualities n s) = printf "quality and sequence have different layouts at line %d (%s)" n (unpack s)++-- | Emitted when a sequence record contains strange characters+data SequenceHasGaps = SequenceHasGaps !Int !Bytes deriving (Typeable, Show)++instance Exception SequenceHasGaps where+ displayException (SequenceHasGaps n cs) = printf "undefined characters %s stored as unknown bases at line %d" (show cs) n++data EmptyRecord = EmptyRecord !Int !Bytes deriving (Typeable, Show)++instance Exception EmptyRecord where+ displayException (EmptyRecord n s) = printf "(effectively) empty record at line %d (%s)" n (unpack s)+++{-# INLINE parseFastq #-}+parseFastq :: MonadLog m => ByteStream m r -> Stream (Of BamRec) m r+parseFastq = Q.unfoldr go . Q.zip (Q.enumFrom (1::Int)) . lines' 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+ go = inspect >=> \case+ Left r -> return (Left r)+ Right ((i,h) :> ls)+ | B.null h -> go ls+ | C.head h == '>' || C.head h == ';' -> goFasta (i, B.tail h) (Q.dropWhile isDescr ls)+ | C.head h == '@' -> goFastq (i, B.tail h) (Q.dropWhile isDescr ls)+ | otherwise -> logMsg Warning (JunkFound i h) >> go ls --- | 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.+ isDescr (_,s) = not (B.null s) && C.head s == ';'+ isHeader (_,s) = not (B.null s) && (C.head s == '>' || C.head s == '@' || C.head s == ';')+ isSep (_,s) = not (B.null s) && C.head s == '+'+ isSeq x = not (isHeader x || isSep x) -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+ goFasta h ls = do+ sq :> ls' <- Q.toList $ Q.span isSeq ls+ make_record h sq Nothing ls'++ goFastq h ls = do+ sq :> ls1 <- Q.toList $ Q.span isSeq ls+ Q.next ls1 >>= \case+ Right (sep, ls2)+ | isSep sep -> do+ qs :> ls3 <- Q.toList $ Q.splitAt (length sq) ls2+ if sum (map (B.length . snd) qs) /= sum (map (B.length . snd) sq) then do+ logMsg Error $ uncurry IncoherentQualities h+ make_record h sq Nothing ls3+ else do+ when (map (B.length . snd) qs /= map (B.length . snd) sq) $+ logMsg Warning $ uncurry IncongruentQualities h+ make_record h sq (Just qs) ls3+ | otherwise -> do+ logMsg Error $ uncurry QualitiesMissing sep+ make_record h sq Nothing (Q.cons sep ls2)+ Left x -> do+ logMsg Error $ QualitiesMissing 0 C.empty+ make_record h sq Nothing (pure x)++ make_record h sq qs k = do+ when (ngaps > 0) . logMsg Warning $+ SequenceHasGaps (fst $ head sq) (B.filter (isGap . toNucleotides) $ B.concat $ map snd sq)+ when (l == 0) . logMsg Warning $ uncurry EmptyRecord h+ return $ Right (r,k)+ where+ !l = sum $ map (B.length . snd) sq+ (!nseq, !ngaps) = mkSeq l sq+ !qual = case qs of Nothing -> Nothing+ Just [] -> Nothing+ Just q -> Just $! mkQual l q++ (!qname, !descr) = B.break (== 32) (snd h)+ !fflag = B.drop 1 . C.dropWhile (/= ':') $ descr++ !r = if B.length fflag < 2 || C.index fflag 1 /= ':' || (C.head fflag /= 'Y' && C.head fflag /= 'N')+ then nullBamRec { b_qname = qname, b_seq = nseq, b_qual = qual }+ else let !flag | C.head fflag /= 'Y' = b_flag nullBamRec .|. flagFailsQC+ | otherwise = b_flag nullBamRec++ !sample = B.takeWhile (/=32). B.drop 1. C.dropWhile (/=':'). B.drop 1. C.dropWhile (/=':') $ fflag+ !exts | B.null sample = [ ]+ | C.all (`C.elem` "ACGTN") sample = [("XI", Text sample)]+ | otherwise = [("RG", Text sample)]+ in nullBamRec { b_qname = qname, b_seq = nseq, b_qual = qual, b_flag = flag, b_exts = exts }+++mkSeq :: Int -> [(Int,Bytes)] -> (Vector_Nucs_half Nucleotides, Int)+mkSeq ltot xs0 = unsafePerformIO $ do+ fp <- mallocForeignPtrBytes (shiftR (ltot+1) 1)+ g <- withForeignPtr fp $ \p -> go_even p 0 xs0+ return ( Vector_Nucs_half 0 ltot fp, g ) where- twiddle (Left e) = Left (Left e)- twiddle (Right (Left r)) = Left (Right r)- twiddle (Right (Right a)) = Right a+ go_even !p !g (s:ss) = B.unsafeUseAsCStringLen (snd s) $ \(q,l) -> go1_even ss p q g l+ go_even _ !g [ ] = return g - isCBase = inClass "ACGTUBDHVSWMKRYNacgtubdhvswmkryn"- canSkip c = isSpace c || c == '.' || c == '-'- isHdr c = c == '@' || c == '>'+ go_odd !p !g !a (s:ss) = B.unsafeUseAsCStringLen (snd s) $ \(q,l) -> go1_odd a ss p q g l+ go_odd !p !g !a [ ] = poke p a >> return g - 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"+ go1_odd a ss !p !q !g !l+ | l > 0 = do !b <- unNs . toNucleotides <$> peekByteOff q 0+ poke p $ a `shiftL` 4 .|. b+ go1_even ss (plusPtr p 1) (plusPtr q 1) (g + fromEnum (b == 0)) (l-1)+ | otherwise = go_odd p g a ss - 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)+ go1_even ss !p !q !g !l+ | l > 1 = do !a <- unNs . toNucleotides <$> peekByteOff q 0+ !b <- unNs . toNucleotides <$> peekByteOff q 1+ poke p $ a `shiftL` 4 .|. b+ go1_even ss (plusPtr p 1) (plusPtr q 2) (g + fromEnum (a == 0) + fromEnum (b == 0)) (l-2)+ | l > 0 = do !a <- unNs . toNucleotides <$> peekByteOff q 0+ go_odd p (g + fromEnum (a == 0)) (a `shiftL` 4) ss+ | otherwise = go_even p g ss -skipJunk :: Monad m => ByteStream m r -> ByteStream m r-skipJunk = lift . S.nextByte >=> check++mkQual :: Int -> [(Int, Bytes)] -> W.Vector Qual+mkQual ltot qs0 = unsafePerformIO $ do+ fp <- mallocForeignPtrBytes ltot+ withForeignPtr fp $ \p -> go (castPtr p) qs0+ return $! W.unsafeFromForeignPtr0 fp ltot 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 '@'+ go !p (s:ss) = B.unsafeUseAsCStringLen (snd s) $ \(q,l) -> go1 ss p (castPtr q) l+ go _ [ ] = return () -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 }+ go1 ss !p !q !l+ | l > 0 = do peek q >>= poke p . subtract (33::Word8)+ go1 ss (plusPtr p 1) (plusPtr q 1) (pred l)+ | otherwise = go p ss
Bio/Bam/Filter.hs view
@@ -82,30 +82,32 @@ 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.+-- | Filter on average quality. Reads without qualities 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)+ p = case b_qual b of Nothing -> True+ Just qs -> let total = V.foldl' (\a x -> a + fromIntegral (unQ x)) 0 qs+ in total >= q * V.length qs -- | 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.+-- without qualities 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+ p = case b_qual b of Nothing -> True+ Just qs -> V.length (V.filter (< Q q) qs) <= 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 }+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)@@ -116,6 +118,6 @@ -- | 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 }+qualityFromNewIllumina b = b { b_qual = V.map (Q . subtract 31 . unQ) <$> b_qual b }
Bio/Bam/Header.hs view
@@ -56,6 +56,7 @@ import Data.ByteString.Builder ( Builder, byteString, char7, intDec, word16LE ) import qualified Data.Attoparsec.ByteString.Char8 as P+import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as S import qualified Data.HashMap.Strict as H import qualified Data.Vector as V@@ -88,8 +89,8 @@ -- (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+addPG :: MonadIO m => Maybe Version -> m (BamMeta -> BamMeta)+addPG vn = liftIO $ do args <- getArgs pn <- getProgName @@ -168,9 +169,6 @@ 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,@@ -185,13 +183,9 @@ 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 []+instance Hashable (Fix BamPG) where+ hashWithSalt s (Fix (BamPG n Nothing o)) = hashWithSalt (hashWithSalt s n) o+ hashWithSalt s (Fix (BamPG n (Just p) o)) = hashWithSalt (hashWithSalt (hashWithSalt s n) p) o -- | Possible sorting orders from bam header. Thanks to samtools, which@@ -219,7 +213,7 @@ parseBamMeta :: P.Parser BamMeta parseBamMeta = fixupMeta . foldl' (flip ($)) emptyHeader- <$> many (parseBamMetaLine <* P.skipWhile (=='\t') <* P.char '\n')+ <$> many (parseBamMetaLine <* P.skipWhile (=='\t') <* P.char '\n') <* P.endOfInput -- Bam header in the process of being parsed. Better suited for -- collecting lines than 'BamMeta'.@@ -234,13 +228,13 @@ 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+-- | Fixes a bam header after parsing. It turns accumulated lists into+-- vectors, throws errors for mandatory fields that weren't parsed+-- correctly, and it handles the program (PG) lines. Program lines come+-- in as an arbitrary graph. It should 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.+-- all nodes have predecessors. Tracing stops if it would form a cycle. fixupMeta :: PreBamMeta -> BamMeta fixupMeta PreBamMeta{..} = BamMeta { meta_hdr = pmeta_hdr@@ -287,14 +281,18 @@ (\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+ sqLine = do _ <- P.string "SQ\t"+ fns <- P.sepBy1 (P.choice [sqnm, sqln, sqother]) tabs+ let sq = foldr ($) (BamSQ "" (-1) []) fns+ guard (not . B.null $ sq_name sq) P.<?> "SQ:NM field"+ guard (sq_length sq >= 0) P.<?> "SQ:LN field"+ pure $ \meta -> meta { pmeta_refs = sq : pmeta_refs meta } - 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+ pgLine = do _ <- P.string "PG\t"+ fns <- P.sepBy1 (P.choice [pgid, pgpp, pgother]) tabs+ let pg = foldr ($) (BamPG "" Nothing []) fns+ guard (not . B.null $ pg_pref_name pg) P.<?> "PG:ID field"+ pure $ \meta -> meta { pmeta_pgs = H.insert (pg_pref_name pg) pg (pmeta_pgs meta) } hdvn = P.string "VN:" >> (\a b hdr -> hdr { hdr_version = (a,b) })@@ -415,7 +413,7 @@ -- | 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)+newtype Refseq = Refseq { unRefseq :: Word32 } deriving (Eq, Ord, Ix, Bounded, Hashable) instance Show Refseq where showsPrec p (Refseq r) = showsPrec p r@@ -539,8 +537,8 @@ | 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)+ in (MdDel (map toNucleotides $ B.unpack a) :) <$> readMd b+ | otherwise = (MdRep (toNucleotides $ B.head s) :) <$> readMd (S.tail s) -- | Normalizes a series of 'MdOp's and encodes them in the way BAM and -- SAM expect it.
Bio/Bam/Index.hs view
@@ -4,6 +4,7 @@ readBamIndex, readBaiIndex, readTabix,+ IndexFormatError(..), Region(..), Subsequence(..),@@ -143,11 +144,10 @@ xs ~~ [] = xs -data IndexFormatError = IndexFormatError Bytes deriving Typeable+data IndexFormatError = IndexFormatError Bytes deriving (Typeable, Show) -instance Exception IndexFormatError-instance Show IndexFormatError where- show (IndexFormatError m) = "index signature " ++ show m ++ " not recognized"+instance Exception IndexFormatError where+ displayException (IndexFormatError m) = "index signature " ++ show m ++ " not recognized" {- | Reads any index we can find for a file. @@ -204,7 +204,7 @@ | 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 :: (MonadIO m, MonadLog 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@@ -322,15 +322,16 @@ '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 :: (MonadIO m, MonadLog 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) $+ Q.unfoldr (P.parseLog Error getBamRaw) $ streamBgzf hdl voff Nothing | otherwise = pure ()+ {- | Reads from a Bam file the part with unaligned reads.
Bio/Bam/Pileup.hs view
@@ -115,7 +115,7 @@ | 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 = maybe (Q 23) (`V.unsafeIndex` i) b_qual -- 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@@ -270,7 +270,7 @@ -- encountered or a t end of file. {-# INLINE pileup #-}-pileup :: Stream (Of PosPrimChunks) IO b -> Stream (Of Pile) IO b+pileup :: Monad m => Stream (Of PosPrimChunks) m b -> Stream (Of Pile) m b pileup = runPileM pileup' finish (Refseq 0) 0 ([],[]) (Empty,Empty) where finish () _ _ ([],[]) (Empty,Empty) inp = lift (Q.effects inp)@@ -318,15 +318,6 @@ 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 ()
Bio/Bam/Reader.hs view
@@ -4,6 +4,7 @@ decodeBam, decodeBamFile, decodeBamFiles,+ IncompatibleRefs(..), decodePlainBam, decodePlainSam,@@ -43,7 +44,7 @@ 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+decodeBam :: (MonadIO m, MonadLog m) => S.ByteStream m r -> m (BamMeta, Stream (Of BamRaw) m r) decodeBam = getBgzfHdr >=> S.splitAt' 4 . pgunzip >=> unbam@@ -55,7 +56,7 @@ 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 :: (MonadIO m, MonadLog m, MonadMask m) => FilePath -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r decodeBamFile f k = streamFile f $ decodeBam >=> uncurry k {-# INLINE decodeBamFile #-} @@ -66,17 +67,18 @@ 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 :: (MonadMask m, MonadLog 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 :: MonadLog 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)))+ S.parse (const getBamMeta) >=> \case+ Left (exception, rest) -> logMsg Error exception >> S.effects rest >>= \r -> pure (mempty, pure r)+ Right (Left r) -> logMsg Error S.EofException >> pure (mempty, pure r)+ Right (Right (h,s)) -> return (h, Q.unfoldr (S.parseLog Error getBamRaw) s) getBamMeta :: Monad m => S.Parser r m BamMeta getBamMeta = liftM2 mmerge get_bam_header get_ref_array@@ -103,42 +105,41 @@ | 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+ 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)+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 :: (MonadLog m, 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))+ 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+ report = fmap (const Nothing) . logMsg Error+ use = fmap Just . liftIO . packBam+ strm = Q.concat . Q.mapM (either report use <=< getSamRec ref) $ S.lines' rest+ return (hdr, strm) -getSamRec :: (Bytes -> Refseq) -> Bytes -> Either S.ParseError BamRec+getSamRec :: MonadLog m => (Bytes -> Refseq) -> Bytes -> m (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+ Left e -> pure . Left $ S.ParseError [unpack s] e+ Right b -> case b_qual b of+ Nothing -> pure $ Right b+ Just qs | W.length qs == V.length (b_seq b) -> pure $ Right b+ | otherwise -> do logMsg Warning $ LengthMismatch (b_qname b)+ pure . Right $ b { b_qual = Nothing } where record = do b_qname <- word b_flag <- num@@ -150,7 +151,7 @@ b_mpos <- subtract 1 <$> num b_isize <- snum b_seq <- sequ- b_qual <- quals <*> pure b_seq+ b_qual <- quals b_exts <- exts let b_virtual_offset = 0 return BamRec{..}@@ -163,12 +164,11 @@ rnext = id <$ P.char '=' <* sep <|> const . ref <$> word sequ = (V.empty <$ P.char '*' <|>- V.fromList . map toNucleotides . C.unpack <$> P.takeWhile is_nuc) <* sep+ V.fromList . map toNucleotides . B.unpack <$> P.takeWhile is_nuc) <* sep - quals = defaultQs <$ P.char '*' <* sep <|> bsToVec <$> word+ quals = Nothing <$ 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+ bsToVec = Just . W.fromList . map (Q . subtract 33) . B.unpack cigar = [] <$ P.char '*' <* sep <|> P.manyTill (flip (:*) <$> P.decimal <*> cigop) sep@@ -193,11 +193,10 @@ is_nuc = P.inClass "acgtswkmrybdhvnACGTSWKMRYBDHVN" -data IncompatibleRefs = IncompatibleRefs FilePath FilePath deriving Typeable+data IncompatibleRefs = IncompatibleRefs FilePath FilePath deriving (Typeable, Show) -instance Exception IncompatibleRefs-instance Show IncompatibleRefs where- show (IncompatibleRefs a b) = "references in " ++ a ++ " and " ++ b ++ " are incompatible"+instance Exception IncompatibleRefs where+ displayException (IncompatibleRefs a b) = "references in " ++ a ++ " and " ++ b ++ " are incompatible" guardRefCompat :: MonadThrow m => (FilePath,BamMeta) -> (FilePath,BamMeta) -> m () guardRefCompat (f0,hdr0) (f1,hdr1) =@@ -216,7 +215,7 @@ 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 :: (MonadIO m, MonadLog 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@@ -241,7 +240,7 @@ exception unless every input is compatible with the effective reference list. -}-mergeInputsOn :: (Ord x, MonadIO m, MonadMask m)+mergeInputsOn :: (Ord x, MonadIO m, MonadLog m, MonadMask m) => (BamRaw -> x) -> [FilePath] -> (BamMeta -> Stream (Of BamRaw) m () -> m r) -> m r mergeInputsOn _ [] k = decodeBam (streamHandle stdin) >>= uncurry k
Bio/Bam/Rec.hs view
@@ -6,17 +6,20 @@ bamRaw, virt_offset, raw_data,+ br_copy, BamRec(..), unpackBam, nullBamRec, getMd,+ LengthMismatch(..),+ BrokenRecord(..), Cigar(..), CigOp(..), alignedLength, - Nucleotides(..), Vector_Nucs_half,+ Nucleotides(..), Vector_Nucs_half(..), Extensions, Ext(..), extAsInt, extAsString, setQualFlag, deleteE, insertE, updateE, adjustE,@@ -29,7 +32,6 @@ isMateReversed, isFirstMate, isSecondMate,- isAuxillary, isSecondary, isFailsQC, isDuplicate,@@ -91,7 +93,9 @@ where l (op :* n) = if op == Mat || op == Del || op == Nop then n else 0 --- | internal representation of a BAM record+-- | More convenient representation of a BAM record.+-- Invariant: Either @b_qual == Nothing@ or+-- @fmap V.length b_qual == Just (V.length b_seq)@. data BamRec = BamRec { b_qname :: Bytes, b_flag :: Int,@@ -103,7 +107,7 @@ b_mpos :: Int, b_isize :: Int, b_seq :: Vector_Nucs_half Nucleotides,- b_qual :: VS.Vector Qual,+ b_qual :: Maybe (VS.Vector Qual), b_exts :: Extensions, b_virtual_offset :: Int64 -- ^ virtual offset for indexing purposes } deriving Show@@ -120,7 +124,7 @@ b_mpos = invalidPos, b_isize = 0, b_seq = V.empty,- b_qual = VS.empty,+ b_qual = Nothing, b_exts = [], b_virtual_offset = 0 }@@ -131,6 +135,14 @@ Just (Char mdfield) -> readMd $ B.singleton mdfield _ -> Nothing +data LengthMismatch = LengthMismatch !Bytes deriving (Typeable, Show)+instance Exception LengthMismatch where+ displayException (LengthMismatch nm) =+ "length of nucleotide and quality sequences" +++ (if B.null nm then "" else " in " ++ unpack nm) +++ " must be equal"++ -- | 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) @@ -204,13 +216,22 @@ data BamRaw = BamRaw { virt_offset :: {-# UNPACK #-} !Int64 , raw_data :: {-# UNPACK #-} !Bytes } +br_copy :: BamRaw -> BamRaw+br_copy (BamRaw o r) = BamRaw o $! B.copy r++data BrokenRecord = BrokenRecord !Int [Int] !Bytes deriving (Typeable, Show)+instance Exception BrokenRecord where+ displayException (BrokenRecord ln m raw) =+ "broken BAM record of length " ++ shows ln " , need offsets " ++ shows m ", begins with " ++ show (B.unpack raw)+ -- | 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))+bamRaw :: MonadThrow m => Int64 -> Bytes -> m BamRaw+bamRaw o s+ | S.length s < 32 = throwM $ BrokenRecord (S.length s) [32] (S.take 16 s)+ | S.length s < sum m = throwM $ BrokenRecord (S.length s) m (S.take 16 s)+ | otherwise = pure $ BamRaw o 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@@ -231,8 +252,7 @@ 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_qual = mk_qual, b_exts = unpackExtensions $ S.drop off_e $ raw_data br, b_virtual_offset = virt_offset br } where@@ -246,6 +266,10 @@ l_seq = getWord32 16 l_cigar = getInt16 12 + mk_qual | l_seq == 0 = Just VS.empty+ | B.unsafeIndex (raw_data br) off_q == 0xff = Nothing+ | otherwise = Just $ VS.unsafeFromForeignPtr (castForeignPtr fp) (off0+off_q) l_seq+ getInt8 :: Num a => Int -> a getInt8 o = fromIntegral (B.unsafeIndex (raw_data br) o) @@ -261,6 +285,7 @@ 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 )]@@ -335,7 +360,7 @@ isPaired, isProperlyPaired, isUnmapped, isMateUnmapped, isReversed,- isMateReversed, isFirstMate, isSecondMate, isAuxillary, isSecondary,+ isMateReversed, isFirstMate, isSecondMate, isSecondary, isFailsQC, isDuplicate, isSupplementary, isTrimmed, isMerged, isAlternative, isExactIndex :: BamRec -> Bool @@ -347,7 +372,6 @@ 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
Bio/Bam/Rmdup.hs view
@@ -2,7 +2,8 @@ rmdup, Collapse, cons_collapse, cheap_collapse, cons_collapse_keep, cheap_collapse_keep, check_sort, normalizeTo, wrapTo,- ECig(..), toECig, setMD, toCigar+ ECig(..), toECig, setMD, toCigar,+ RmdupException(..), BrokenMD(..) ) where import Bio.Bam.Header@@ -10,31 +11,33 @@ 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+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as T+import qualified Data.HashMap.Strict as M+import qualified Data.Vector as VV ( fromList )+import qualified Data.Vector.Algorithms.Intro as VV ( sortBy )+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+data Collapse m = Collapse {+ collapse :: [BamRec] -> m (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 :: MonadLog m => Qual -> Collapse m cons_collapse maxq = Collapse (do_collapse maxq) (const []) -cons_collapse_keep :: Qual -> Collapse+cons_collapse_keep :: MonadLog m => Qual -> Collapse m cons_collapse_keep maxq = Collapse (do_collapse maxq) (map (\b -> b { b_flag = b_flag b .|. flagDuplicate })) -cheap_collapse :: Collapse+cheap_collapse :: Monad m => Collapse m cheap_collapse = Collapse do_cheap_collapse (const []) -cheap_collapse_keep :: Collapse+cheap_collapse_keep :: Monad m => Collapse m cheap_collapse_keep = Collapse do_cheap_collapse (map (\b -> b { b_flag = b_flag b .|. flagDuplicate })) @@ -103,17 +106,18 @@ -- 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 =+rmdup :: (MonadLog m, MonadThrow m, Hashable l, Eq l)+ => Bool -> Collapse m -> Q.Stream (Of (l,BamRec)) m r -> Q.Stream (Of ((l,Int),BamRec)) m r+rmdup 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"+ check_sort (b_cpos . snd) InternalError .+ mapGroups ( fmap (V.modify (VV.sortBy (comparing $ V.length . b_seq . snd)) . VV.fromList)+ . fmap (M.foldrWithKey (\l mrs -> (++) [ ((l,n),r) | (n,r) <- mrs ]) [])+ . mapM (do_rmdup strand_preserved collapse_cfg)+ . accumMap fst snd ) .+ check_sort (b_cpos . snd) UnsortedInput where b_cpos = b_rname &&& b_pos @@ -121,17 +125,22 @@ mg1 f acc a s = lift (inspect s) >>= \case- Left r -> Q.each (f (a:acc)) >> pure r+ Left r -> lift (f (a:acc)) >>= Q.each >> 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'+ | b_cpos (snd a) == b_cpos (snd b) -> mg1 f (b:acc) a s'+ | otherwise -> lift (f (a:acc)) >>= Q.each >> mg1 f [] b s' -check_sort :: (Monad m, Ord b) => (a -> b) -> String -> Stream (Of a) m r -> Stream (Of a) m r+data RmdupException = UnsortedInput | InternalError deriving (Typeable, Show)+instance Exception RmdupException where+ displayException UnsortedInput = "input must be sorted for rmdup to work"+ displayException InternalError = "internal error, output isn't sorted anymore"++check_sort :: (MonadThrow m, Ord b) => (a -> b) -> RmdupException -> 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+ | f a > f b = lift $ throwM msg | otherwise = Q.cons a $ step (b :> s) {-# INLINE check_sort #-} @@ -196,33 +205,36 @@ (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+do_rmdup :: Monad m => Bool -> Collapse m -> [BamRec] -> m [(Int,BamRec)]+do_rmdup strand_preserved Collapse{..} rds = do+ let (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)+ unaligned' = accumMap b_strand id half_unaligned - (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+ (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 -> b_strand b) (true_singles++half_aligned) - b_strand b = strand_preserved && isReversed b- b_mate b = strand_preserved && isFirstMate b+ let (results, leftovers) = merge_singles singles' unaligned' $+ [ (str, second fromDecision b) | ((_,str ),b) <- M.toList merged' ] +++ [ (str, second fromDecision b) | ((_,str,_),b) <- M.toList pairs' ]+ pure $ results ++ map ((,) 0) (originals (leftovers ++ r1 ++ r2 ++ r3))+ where+ mkMap f x = do m1 <- traverse (\xs -> (,) (length xs) <$> collapse xs) $ accumMap f id x+ pure (M.map (second fst) m1, concatMap (snd.snd) $ M.elems m1) + b_strand b = strand_preserved && isReversed b+ b_mate b = strand_preserved && isFirstMate b+ b_mate_pos = b_mrnm &&& b_mpos &&& isUnmapped &&& isMateUnmapped+ b_totally_aligned = (&&) <$> not . isUnmapped <*> not . isMateUnmapped +++ -- | Merging information about true singles, merged singles, -- half-aligned pairs, actually aligned pairs. --@@ -240,8 +252,8 @@ -- 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+merge_singles :: M.HashMap Bool (Int,Decision) -- strand --> true singles & half aligned+ -> M.HashMap Bool [BamRec] -- strand --> half unaligned -> [ (Bool, (Int, BamRec)) ] -- strand --> paireds & mergeds -> ([(Int,BamRec)],[BamRec]) -- results, leftovers @@ -255,7 +267,7 @@ -- 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+ unal = M.lookupDefault [] str unaligneds ++ l in case M.lookup str singles of Nothing -> ( (m,v) : r, unal )@@ -282,7 +294,7 @@ -- singleton. Duplication is ugly, so in this case, we force it to -- singleton. -merge_halves :: M.Map Bool [BamRec] -- strand --> half unaligned+merge_halves :: M.HashMap Bool [BamRec] -- strand --> half unaligned -> [(Bool, (Int,Decision))] -- strand --> true singles & half aligned -> ([(Int,BamRec)],[BamRec]) -- results, leftovers @@ -290,7 +302,8 @@ -- 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 )+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@@ -301,32 +314,23 @@ -- 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)+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+ (same,diff) = partition (is_mate_of v) $ M.lookupDefault [] 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)+merge_halves unaligneds [] =+ ( [], concat $ M.elems unaligneds ) -accumMap :: Ord k => (a -> k) -> (a -> v) -> [a] -> M.Map k [v]+accumMap :: (Hashable k, Eq k) => (a -> k) -> (a -> v) -> [a] -> M.HashMap 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+ go m (a:as) = let ws = M.lookupDefault [] (f a) m ; g' = g a in g' `seq` go (M.insert (f a) (g':ws) m) as @@ -359,22 +363,25 @@ 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+do_collapse :: MonadLog m => Qual -> [BamRec] -> m (Decision, [BamRec])+do_collapse maxq [br]+ | maybe True (V.all (<= maxq)) (b_qual br) = pure ( Representative br, [ ] ) -- no modification, pass through+ | otherwise = pure ( Consensus lq_br, [br] ) -- qualities reduced, must keep original where- lq_br = br { b_qual = V.map (min maxq) $ b_qual br+ 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+do_collapse maxq brs = do+ forM_ ws $ logMsg Warning+ pure ( 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 = Just $ 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@@ -392,23 +399,17 @@ 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 ]+ | b <- good brs+ , let q = maybe (Q 23) (V.! i) (b_qual b) ] | 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- [ ] -> []+ (md',ws) = case [ (b_seq b,md,b) | b <- good brs, Just md <- [ getMd b ] ] of+ [ ] -> ([],Nothing) (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 ]-+ Right x -> (x,Nothing)+ Left (MdFail cigs ms osq nsq) -> ([],Just e)+ where e = BrokenMD (b_qname b) (b_rname b) (b_pos b) cigs ms osq 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 ]@@ -428,9 +429,21 @@ 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++data BrokenMD = BrokenMD Bytes Refseq Int [Cigar] [MdOp] [Nucleotides] [Nucleotides] deriving (Typeable, Show)+instance Exception BrokenMD where+ displayException (BrokenMD qname rname pos cigs ms osq nsq)+ = unlines [ "Broken MD field when trying to construct new MD!"+ , "QNAME: " ++ show qname+ , "POS: " ++ shows (unRefseq rname) ":" ++ show pos+ , "CIGAR: " ++ show cigs+ , "MD: " ++ show ms+ , "refseq: " ++ show osq+ , "readseq: " ++ show nsq ]+++minViewBy :: (a -> a -> Ordering) -> a -> [a] -> (a,[a])+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@@ -478,19 +491,19 @@ 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"+ (n0,q0) : (_,q1) : _ | qr > 3 -> (n0, Q qr)+ where qr = fromIntegral $ (q0-q1) `min` fromIntegral maxq+ _ -> (nucsN, Q 0) -- 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 )+do_cheap_collapse :: Monad m => [BamRec] -> m ( Decision, [BamRec] )+do_cheap_collapse [ ] = pure ( Representative nullBamRec, [] )+do_cheap_collapse [ b ] = pure ( Representative b, [] )+do_cheap_collapse (b:bs) = pure ( 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+ (b0, bx) = minViewBy (comparing b_qname) b bs+ new_xp = foldl' (\a -> oplus a . extAsInt 1 "XP") (extAsInt 1 "XP" b) bs replaceXP :: Int -> BamRec -> BamRec replaceXP new_xp b0 = b0 { b_exts = updateE "XP" (Int new_xp) $ b_exts b0 }
Bio/Bam/Trim.hs view
@@ -1,28 +1,34 @@--- | Trimming of reads as found in BAM files. Implements trimming low--- quality sequence from the 3' end.+-- | Trimming and fusing of reads as found in BAM files.+--+-- This API is remarkably ugly because the core loop is implemented in+-- C. This requires the adapters to be in storable vectors, and since+-- they shouldn't be constantly copied around, the ugly 'withADSeqs'+-- function is needed. The performance gain seems to be worth it,+-- though. module Bio.Bam.Trim ( trim_3 , trim_3' , trim_low_quality+ , AD_Seqs+ , withADSeqs , 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.Bam.Rmdup ( ECig(..), setMD, toECig ) import Bio.Prelude-import Bio.Streaming-import Foreign.C.Types ( CInt(..) )+import Control.Monad.Trans.Control ( MonadBaseControl, control )+import Foreign.C.Types ( CInt(..) )+import Foreign.Marshal.Array ( allocaArray ) import qualified Data.ByteString as B import qualified Data.Vector.Generic as V@@ -36,31 +42,33 @@ -- 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.+-- information, notably the "mate" information and many optional fields.+-- Since the intention is to trim based on quality scores, reads without+-- qualities are passed along unchanged. 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_3' p b = case b_qual b of+ Nothing -> b+ Just qs | b_flag b `testBit` 4 -> trim_3 len_rev b+ | otherwise -> trim_3 len_fwd b+ where+ len_fwd = subtract 1 . length . takeWhile (uncurry p) $+ zip (inits . reverse . V.toList $ b_seq b)+ (inits . reverse . V.toList $ qs) - 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+ len_rev = subtract 1 . length . takeWhile (uncurry p) $+ zip (inits . V.toList $ b_seq b)+ (inits . V.toList $ qs) + 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)+ in c { b_seq = V.take (V.length (b_seq c) - l) $ b_seq c+ , b_qual = V.take (V.length (b_seq c) - l) <$> b_qual c , b_cigar = cigar' , b_exts = map (\(k,e) -> case e of Text t | k `elem` trim_set@@ -70,8 +78,8 @@ 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)+ 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@@ -160,15 +168,15 @@ -- | 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]+find_merge :: AD_Seqs -> AD_Seqs -> 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+ -> IO (Int, Int, Int)+find_merge pads1 pads2 r1 q1 r2 q2 =+ with_fw_seq r1 q1 $ \pr1 ->+ with_fw_seq r2 q2 $ \pr2 ->+ with_rc_seq r2 q2 $ \prv2 -> do+ min_merge_score pads1 pads2 pr1 pr2 prv2 -- | Overlap-merging of read pairs. We shall compute the likelihood -- for every possible overlap, then select the most likely one (unless it@@ -189,41 +197,45 @@ -- 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+mergeBam :: Int -> Int+ -> AD_Seqs -> AD_Seqs+ -> BamRec -> BamRec -> IO [BamRec]+mergeBam lowq highq ads1 ads2 r1 r2 = do+ let 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+ b_seq_r1 = V.convert $ b_seq r1+ b_seq_r2 = V.convert $ b_seq r2+ b_qual_r1 = fromMaybe (V.map (const (Q 23)) b_seq_r1) (b_qual r1)+ b_qual_r2 = fromMaybe (V.map (const (Q 23)) b_seq_r2) (b_qual r2) - (mlen, qual1, qual2) = find_merge ads1 ads2 b_seq_r1 b_qual_r1 b_seq_r2 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+ let 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)+ 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 }+ 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 = Just $ 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 } + return $ case () of+ _ | 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 ]+ {-# INLINE merged_seq #-} merged_seq :: (V.Vector v Nucleotides, V.Vector v Qual) => Int -> v Nucleotides -> v Qual -> v Nucleotides -> v Qual -> v Nucleotides@@ -262,47 +274,44 @@ -- | 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]+find_trim :: AD_Seqs -> 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+ -> IO (Int, Int, Int)+find_trim pads1 r1 q1 =+ withADSeqs [W.empty] $ \pads2 ->+ with_fw_seq r1 q1 $ \pr1 ->+ min_merge_score pads1 pads2 pr1 (FW_Seq nullPtr nullPtr 0) (RC_Seq nullPtr nullPtr 0) -- | 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+trimBam :: Int -> Int -> AD_Seqs -> BamRec -> IO [BamRec]+trimBam lowq highq ads1 r1 = do+ let b_seq_r1 = V.convert $ b_seq r1+ (mlen, qual1, qual2) <- find_trim ads1 b_seq_r1 $+ fromMaybe (V.map (const (Q 23)) b_seq_r1) (b_qual r1) - (mlen, qual1, qual2) = find_trim ads1 b_seq_r1 b_qual_r1+ let 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 } - 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 - 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 } - 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 }+ return $ case () of+ _ | V.null (b_seq r1) -> [ ]+ | mlen == 0 && qual1 >= highq -> [ ]+ | qual1 < lowq || mlen < 0 -> [ r1' ]+ | qual1 >= highq -> [ r1t ]+ | otherwise -> map flag_alternative [ r1', r1t ] -- | For merging, we don't need the complete adapters (length around 70!),@@ -313,8 +322,8 @@ -- (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)+default_fwd_adapters :: [W.Vector Nucleotides]+default_fwd_adapters = map (W.fromList . map toNucleotides . map c2w) [ {- Genomic R2 -} "AGATCGGAAGAGCGGTTCAG" , {- Multiplex R2 -} "AGATCGGAAGAGCACACGTC" , {- Graft P7 -} "AGATCGGAAGAGCTCGTATG" ]@@ -323,8 +332,8 @@ -- 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)+default_rev_adapters :: [W.Vector Nucleotides]+default_rev_adapters = map (W.fromList . map toNucleotides . map c2w) [ {- Genomic_R1 -} "AGATCGGAAGAGCGTCGTGT" , {- CL72 -} "GGAAGAGCGTCGTGTAGGGA" ] @@ -337,106 +346,72 @@ -- 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-+min_merge_score+ :: AD_Seqs -- 3' adapters as they appear in the first read+ -> AD_Seqs -- 5' adapters as they appear in the second read+ -> FW_Seq -- first read, prepped+ -> FW_Seq -- second read, qual, prepped+ -> RC_Seq -- second read, qual, reversed and prepped+ -> IO (Int,Int,Int) -- best length, min score, 2nd min score+min_merge_score (AD_Seqs !p_fwd_ads !p_fwd_lns !n_fwd_ads) (AD_Seqs !p_rev_ads !p_rev_lns !n_rev_ads)+ (FW_Seq !p_rd1 !p_qs1 !l1) (FW_Seq !p_rd2 !p_qs2 !l2) (RC_Seq !p_rrd2 !p_rqs2 _) =+ allocaArray 2 $ \pmins ->+ liftM3 (,,)+ (fromIntegral <$>+ prim_merge_score p_fwd_ads p_fwd_lns (fromIntegral n_fwd_ads)+ p_rev_ads p_rev_lns (fromIntegral n_rev_ads)+ p_rd1 p_qs1 (fromIntegral l1)+ p_rd2 p_qs2 (fromIntegral l2)+ p_rrd2 p_rqs2 pmins)+ (fromIntegral <$> peekElemOff pmins 0)+ (fromIntegral <$> peekElemOff pmins 1) -foreign import ccall unsafe "prim_match_reads"- prim_match_reads :: CInt -> CInt -> CInt+foreign import ccall unsafe "prim_merge_score"+ prim_merge_score :: Ptr (Ptr Nucleotides) -> Ptr CInt -> CInt+ -> Ptr (Ptr Nucleotides) -> Ptr CInt -> CInt+ -> Ptr Nucleotides -> Ptr Qual -> CInt+ -> Ptr Nucleotides -> Ptr Qual -> CInt -> Ptr Nucleotides -> Ptr Qual- -> Ptr Nucleotides -> Ptr Qual -> IO CInt+ -> Ptr CInt -> 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) +data AD_Seqs = AD_Seqs !(Ptr (Ptr Nucleotides)) !(Ptr CInt) !Int+data FW_Seq = FW_Seq !(Ptr Nucleotides) !(Ptr Qual) !Int+data RC_Seq = RC_Seq !(Ptr Nucleotides) !(Ptr Qual) !Int -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+-- Maybe pad with something suitable?+withADSeqs :: MonadBaseControl IO m => [W.Vector Nucleotides] -> (AD_Seqs -> m r) -> m r+withADSeqs ads0 k =+ control $ \run_io ->+ allocaArray (length ads0) $ \pps ->+ allocaArray (length ads0) $ \pls ->+ let go !n [ ] = run_io (k $! AD_Seqs pps pls n)+ go !n (v:vs) = W.unsafeWith v $ \pa -> do+ pokeElemOff pps n pa+ pokeElemOff pls n (fromIntegral (W.length v))+ go (succ n) vs+ in go 0 ads0 - go1 (r1 :> s) | isPaired r1 = lift (inspect s) >>= go2 r1- | otherwise = each (trimBam lowq highq fwd_ads r1) >> go s+-- Maybe pad with something suitable?+with_fw_seq :: W.Vector Nucleotides -> W.Vector Qual -> (FW_Seq -> IO r) -> IO r+with_fw_seq ns qs k+ | W.length ns == W.length qs+ = W.unsafeWith ns $ \p_ns ->+ W.unsafeWith qs $ \p_qs ->+ k (FW_Seq p_ns p_qs $ W.length ns)+ | otherwise+ = throwIO $ LengthMismatch "forward adapter"+{-# INLINE with_fw_seq #-} - 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+-- Maybe pad with something suitable?+with_rc_seq :: W.Vector Nucleotides -> W.Vector Qual -> (RC_Seq -> IO r) -> IO r+with_rc_seq ns qs k+ | W.length ns == W.length qs+ = W.unsafeWith (W.reverse $ W.map compls ns) $ \p_rns ->+ W.unsafeWith (W.reverse qs) $ \p_rqs ->+ k (RC_Seq p_rns p_rqs $ W.length ns)+ | otherwise+ = throwIO $ LengthMismatch "reverse adapter"+{-# INLINE with_rc_seq #-}
Bio/Bam/Writer.hs view
@@ -19,7 +19,7 @@ import Bio.Streaming.Bgzf import Data.ByteString.Builder.Prim ( (>*<) )-import Data.ByteString.Internal ( ByteString(..) )+import Data.ByteString.Internal ( fromForeignPtr ) import Data.ByteString.Lazy ( foldrChunks ) import Foreign.Marshal.Alloc ( alloca ) @@ -69,11 +69,11 @@ | 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)+ buildQual = maybe (B.char7 '*') (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.word8 (fromIntegral k ) <>+ B.word8 (fromIntegral (shiftR k 8)) <> B.char7 ':' <> buildExtVal v @@ -182,9 +182,9 @@ . TkWord32 (fromIntegral b_isize) . TkString b_qname . TkWord8 0- . W.foldr ((.) . TkWord8) id (W.unsafeCast b_cigar :: W.Vector Word8)+ . TkMemCopy (W.unsafeCast b_cigar) . pushSeq b_seq- . W.foldr ((.) . TkWord8 . unQ) id b_qual+ . maybe (TkMemFill (V.length b_seq) 0xff) (TkMemCopy . W.unsafeCast) b_qual . foldr ((.) . pushExt) id b_exts . TkEndRecord where@@ -238,7 +238,7 @@ 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)+ bamRaw 0 $ fromForeignPtr (buffer bb') 4 (used bb' - 4) where store_loop bb tk = do (bb',tk') <- fillBuffer bb tk case tk' of TkEnd -> return (bb',tk')
Bio/Base.hs view
@@ -34,10 +34,14 @@ import BasePrelude #if MIN_VERSION_base(4,9,0)- hiding ( log1pexp, log1mexp )+ hiding ( log1pexp, log1mexp, (<>) )+#else+ hiding ( (<>) ) #endif import Bio.Util.Numeric ( log1pexp, log1mexp ) +import Data.ByteString.Internal ( c2w )+import Data.Semigroup ( Semigroup(..) ) import qualified Data.ByteString.Char8 as C import qualified Data.Vector.Unboxed as U @@ -65,6 +69,9 @@ minBound = Ns 0 maxBound = Ns 15 +instance Semigroup Nucleotides where Ns a <> Ns b = Ns (a .|. b)+instance Monoid Nucleotides where mempty = Ns 0 ; mappend = (<>)+ nucToNucs :: Nucleotide -> Nucleotides nucToNucs (N x) = Ns $ 1 `shiftL` fromIntegral (x .&. 3) @@ -72,7 +79,7 @@ -- 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.+-- score, which means lower error probability. newtype Qual = Q { unQ :: Word8 } deriving ( Eq, Ord, Storable, Bounded ) @@ -185,43 +192,38 @@ } 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 'Nucleotide'.+-- The codes for A,C,G understood, everything else is a T. (Error+-- detection is the caller's responsibility.+toNucleotide :: Word8 -> Nucleotide+toNucleotide x | x .|. 32 == c2w 'a' = nucA+ | x .|. 32 == c2w 'c' = nucC+ | x .|. 32 == c2w 'g' = nucG+ | otherwise = nucT+{-# INLINABLE toNucleotide #-} -- | 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) ]+-- The usual codes for A,C,G,T and U are understood along with the IUPAC+-- ambiguity codes, '-' and '.' become gaps and everything else is an N.+toNucleotides :: Word8 -> Nucleotides+toNucleotides x | x .|. 32 == c2w 'a' = nucsA+ | x .|. 32 == c2w 'c' = nucsC+ | x .|. 32 == c2w 'g' = nucsG+ | x .|. 32 == c2w 't' = nucsT+ | x .|. 32 == c2w 'n' = nucsN+ | x .|. 32 == c2w 'u' = nucsT+ | x .|. 32 == c2w 'b' = nucsC <> nucsG <> nucsT+ | x .|. 32 == c2w 'd' = nucsA <> nucsG <> nucsT+ | x .|. 32 == c2w 'h' = nucsA <> nucsC <> nucsT+ | x .|. 32 == c2w 'v' = nucsA <> nucsC <> nucsG+ | x .|. 32 == c2w 'w' = nucsA <> nucsT+ | x .|. 32 == c2w 's' = nucsC <> nucsG+ | x .|. 32 == c2w 'm' = nucsA <> nucsC+ | x .|. 32 == c2w 'k' = nucsG <> nucsT+ | x .|. 32 == c2w 'y' = nucsC <> nucsT+ | x .|. 32 == c2w 'r' = nucsA <> nucsG+ | otherwise = gap+{-# INLINABLE toNucleotides #-} -- | Tests if a 'Nucleotides' is a base. -- Returns 'True' for everything but gaps.@@ -279,10 +281,10 @@ showList l = (map showNucleotides l ++) instance Read Nucleotides where- readsPrec _ (c:cs) = [(toNucleotides c, cs)]+ readsPrec _ (c:cs) = [(toNucleotides (c2w c), cs)] readsPrec _ [ ] = [] readList s = let (hd,tl) = span (\c -> isAlpha c || isSpace c || '-' == c) s- in [(map toNucleotides $ filter (not . isSpace) hd, tl)]+ in [(map (toNucleotides . c2w) $ filter (not . isSpace) hd, tl)] -- | Complements a Nucleotides. {-# INLINE compl #-}
Bio/Prelude.hs view
@@ -5,6 +5,7 @@ module Bio.Util.Text, module Control.Monad.Catch, module Control.Monad.IO.Class,+ module Control.Monad.Log, module Control.Monad.Trans.Class, module Data.Bifunctor, module Data.List.NonEmpty,@@ -14,13 +15,12 @@ Bytes, LazyBytes, Generic1(..), Hashable(..),- Hashable1(..),- Hashable2(..), HashMap, HashSet, IntMap, IntSet, NonEmpty(..),+ PrimMonad(..), Semigroup(..), Text, LazyText ) where@@ -31,6 +31,7 @@ , Handler, handle, handleJust , finally, try, tryJust, onException , mask, mask_, uninterruptibleMask, uninterruptibleMask_+ , exitSuccess, exitFailure, exitWith #if MIN_VERSION_base(4,9,0) , log1p, log1pexp, log1mexp, expm1 #endif@@ -40,6 +41,8 @@ import Bio.Util.Text import Control.Monad.Catch import Control.Monad.IO.Class+import Control.Monad.Log ( LIO, execWithParser_, MonadLog(..), Level(..) )+import Control.Monad.Primitive ( PrimMonad(..) ) import Control.Monad.Trans.Class import Data.Bifunctor import Data.ByteString ( ByteString )@@ -47,13 +50,12 @@ 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+import System.IO ( stdin, stdout, stderr , openBinaryFile, withBinaryFile, IOMode(..) , hFlush, hSeek, hClose, SeekMode(..) )
Bio/Streaming.hs view
@@ -1,3 +1,9 @@+{-# LANGUAGE CPP, TypeFamilies #-}+#if __GLASGOW_HASKELL__ >= 800+{-# OPTIONS_GHC -Wno-orphans #-}+#else+{-# OPTIONS_GHC -fno-warn-orphans #-}+#endif module Bio.Streaming ( MonadIO(..) , MonadMask@@ -9,6 +15,7 @@ , streamInputs , withOutputFile + , UnwantedTerminal(..) , protectTerm , psequence , progressGen@@ -30,10 +37,14 @@ import Streaming hiding ( (<>) ) import Streaming.Internal ( Stream(..) ) import Streaming.Prelude ( each )-import System.IO+import System.IO ( hIsTerminalDevice ) import qualified Streaming.Prelude as Q +instance (Functor f, PrimMonad m) => PrimMonad (Stream f m) where+ type PrimState (Stream f m) = PrimState m+ primitive = lift . primitive+ {- | Default buffer size in elements. Since we often want to merge many files, a read should take more time@@ -74,6 +85,10 @@ liftIO $ hClose h {-# INLINE streamInputs #-} +data UnwantedTerminal = UnwantedTerminal deriving (Typeable, Show)+instance Exception UnwantedTerminal where+ displayException _ = "cowardly refusing to write binary data to terminal"+ {- | Protects the terminal from binary junk. If @s@ is a 'Stream', then @protectTerm s@ throws an error if 'stdout'@@ -84,7 +99,7 @@ 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"+ when t . liftIO . throwM $ UnwantedTerminal str {-# INLINE protectTerm #-} @@ -167,27 +182,24 @@ GT -> Step (b :> go str0 rest1) {-# INLINABLE mergeStreamsBy #-} --- | A general progress indicator that prints some message after a set+-- | A general progress indicator that logs 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+progressGen :: MonadLog m => (Int -> a -> String) -> Int -> Q.Stream (Q.Of a) m r -> Q.Stream (Q.Of a) m r+progressGen msg sz = 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"+ go !n = lift . Q.next >=> either fin (step $ succ n)+ step !n (a,s) = do when (n `mod` sz == 0) . lift . logString_ $ msg n a Q.cons a (go n s)+ fin r = r <$ lift (logString_ "") --- | 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+-- | A simple progress indicator that logs the number of records.+progressNum :: MonadLog m => String -> Int -> 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+-- | A simple progress indicator that logs a position every set number -- of passed records.-progressPos :: MonadIO m- => (a -> (Refseq, Int)) -> String -> Refs -> Int -> (String -> IO ())+progressPos :: MonadLog m+ => (a -> (Refseq, Int)) -> String -> Refs -> Int -> 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
Bio/Streaming/Bgzf.hs view
@@ -16,13 +16,15 @@ BclArgs(..), BclSpecialType(..), loop_dec_int,- loop_bcl_special+ loop_bcl_special,+ CompressionError(..),+ DecompressionError(..) ) where import Bio.Prelude import Bio.Streaming import Foreign.C.Types ( CInt(..) )-import Foreign.Marshal.Utils ( copyBytes, with )+import Foreign.Marshal.Utils ( copyBytes, fillBytes, with ) import qualified Bio.Streaming.Bytes as S import qualified Data.ByteString as B@@ -89,6 +91,9 @@ | TkString {-# UNPACK #-} !B.ByteString BgzfTokens -- a raw string | TkDecimal {-# UNPACK #-} !Int BgzfTokens -- roughly ':%d' + | TkMemFill {-# UNPACK #-} !Int {-# UNPACK #-} !Word8 BgzfTokens+ | TkMemCopy {-# UNPACK #-} !(V.Vector Word8) BgzfTokens+ | TkSetMark BgzfTokens -- sets the first mark | TkEndRecord BgzfTokens -- completes a BAM record | TkEndRecordPart1 BgzfTokens -- completes part 1 of a BCF record@@ -129,13 +134,21 @@ , mark = if mark b == maxBound then maxBound else mark b - off b , mark2 = if mark2 b == maxBound then maxBound else mark2 b - off b } +data CompressionError = CompressionError !CInt deriving (Typeable,Show)+instance Exception CompressionError where+ displayException (CompressionError rc) = "compress_chunk failed: " ++ show rc++data DecompressionError = DecompressionError !CInt deriving (Typeable,Show)+instance Exception DecompressionError where+ displayException (DecompressionError rc) = "decompress_chunk failed: " ++ show rc+ 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+ when (rc /= 0 && rc /= 1) $ throwIO $ CompressionError rc fromIntegral <$> peek p_len decompressChunk :: B.ByteString -> IO B.ByteString@@ -144,7 +157,7 @@ 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+ when (rc /= 0) $ throwIO $ DecompressionError rc -- | Expand a chain of tokens into a buffer, sending finished pieces@@ -220,10 +233,10 @@ TkDouble x tk' -> do pokeByteOff p use x go_fast p bb (use + 8) tk' + -- The next three may be too big to handle. By returning with+ -- unfinished business, we will get progressively bigger buffers+ -- and eventually handle it just fine. 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@@ -231,6 +244,21 @@ copyBytes (p `plusPtr` use) q ln go_fast p bb (use + ln) tk' + TkMemFill ln c tk'+ | ln > size bb - use -> return (bb { used = use },tk1)++ | otherwise -> do fillBytes (p `plusPtr` use) c ln+ go_fast p bb (use + ln) tk'++ TkMemCopy v tk'+ | V.length v > size bb - use -> return (bb { used = use },tk1)++ | otherwise -> do let ln = V.length v+ V.unsafeWith v $ \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' @@ -247,7 +275,6 @@ 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
Bio/Streaming/Bytes.hs view
@@ -124,7 +124,7 @@ ,AllocationStrategy,ChunkIOStream(..),buildStepToCIOS ,byteStringFromBuffer,safeStrategy,defaultChunkSize) import GHC.Exts (SpecConstrAnnotation(..))-import Streaming (Of(..),Identity(..),destroy)+import Streaming (MFunctor(..),Of(..),Identity(..),destroy) import Streaming.Internal (Stream (..)) import System.Directory (renameFile) @@ -143,8 +143,8 @@ -- also contains an offset, which will be needed to track the virtual -- offsets in the BGZF decode. -data ByteStream m r =- Empty r+data ByteStream m r+ = Empty r | Chunk {-# UNPACK #-} !Bytes {-# UNPACK #-} !Int64 (ByteStream m r) | Go (m (ByteStream m r)) @@ -187,6 +187,13 @@ lift ma = Go $ liftM Empty ma {-# INLINE lift #-} +instance MFunctor ByteStream where+ hoist f = loop where+ loop (Empty r) = Empty r+ loop (Chunk c o s) = Chunk c o (loop s)+ loop (Go m) = Go (f (liftM loop m))+ {-# INLINEABLE hoist #-}+ instance (r ~ ()) => IsString (ByteStream m r) where fromString = chunk . fromString {-# INLINE fromString #-}@@ -682,8 +689,7 @@ {- | 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. -}+ This will cost memory if the lines are very long. -} lines' :: Monad m => ByteStream m r -> Stream (Of Bytes) m r lines' = loop1 []@@ -691,15 +697,19 @@ loop1 :: Monad m => [Bytes] -> ByteStream m r -> Stream (Of Bytes) m r loop1 acc text = case text of- Empty r -> Return r+ Empty r -> r <$ unless (null acc) (Q.yield (checkCR $ B.concat (reverse acc))) 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)))+ Just i -> Q.cons (checkCR $ 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+ checkCR s+ | B.null s = s+ | B.last s == 13 = B.init s+ | otherwise = s {-# INLINABLE lines' #-} -- --------------------------------------------------------------------------
Bio/Streaming/Parse.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE Rank2Types #-}+-- | Parsers for use with 'ByteStream's. module Bio.Streaming.Parse ( Parser , ParseError(..) , EofException(..) , parse , parseIO+ , parseLog , parseM , abortParse , isFinished@@ -18,8 +20,6 @@ , atto ) where --- ^ Parsers for use with 'ByteStream's.- import Bio.Prelude hiding ( drop ) import Bio.Streaming.Bytes ( ByteStream ) @@ -30,10 +30,10 @@ 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 }+ (a -> ByteStream m r -> m x) -- successful parse+ -> (r -> m x) -- end of input stream+ -> (SomeException -> ByteStream m r -> m x) -- exception and remaining input+ -> ByteStream m r -> m x } -- input, result instance Functor (Parser r m) where fmap f p = P $ \sk -> runP p (sk . f)@@ -53,12 +53,13 @@ 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)+ throwM e = P $ \_sk _rk ek -> 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 :: Monad m => (Int64 -> Parser r m a) -> ByteStream m r+ -> m (Either (SomeException, ByteStream m r) (Either r (a, ByteStream m r))) parse p = go where go (S.Empty r) = return $ Right $ Left r@@ -66,14 +67,18 @@ 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)+ (curry $ 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+parseIO p = parse p >=> either (liftIO . throwM . fst) return +parseLog :: MonadLog m => Level -> (Int64 -> Parser r m a) -> ByteStream m r -> m (Either r (a, ByteStream m r))+parseLog lv p = parse p >=> either throw_it pure+ where throw_it (ex,rest) = logMsg lv ex >> Left <$> S.effects rest+ 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+parseM p = parse p >=> either (throwM . fst) return abortParse :: Monad m => Parser r m a abortParse = P $ \_sk rk _ek -> S.effects >=> rk@@ -96,7 +101,7 @@ 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)+getByte = P $ \sk _rk ek -> S.nextByte >=> either (ek (toException EofException) . pure) (uncurry sk) getString :: Monad m => Int -> Parser r m B.ByteString getString l = liftFun $ liftM Q.lazily . S.splitAt' l@@ -110,16 +115,17 @@ 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)+ (\e rest -> ek e (join rest)) .+ S.splitAt (fromIntegral l) -data ParseError = ParseError {errorContexts :: [String], errorMessage :: String}- deriving (Show, Typeable) -data EofException = EofException- deriving (Show, Typeable)+data EofException = EofException deriving (Show, Typeable)+instance Exception EofException where displayException _ = "end-of-file" -instance Exception ParseError-instance Exception EofException+data ParseError = ParseError {errorContexts :: [String], errorMessage :: String} deriving (Show, Typeable)+instance Exception ParseError where+ displayException (ParseError ctx msg)+ = "Parse error at " ++ intercalate ", " ctx ++ ": " ++ msg atto :: Monad m => A.Parser a -> Parser r m a atto = go . A.parse@@ -127,13 +133,13 @@ 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.Fail _ err dsc -> ek (toException (ParseError err dsc)) (pure r)+ A.Partial _ -> ek (toException EofException) (pure r) 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.Fail _ err dsc -> ek (toException (ParseError err dsc)) s' A.Partial k' -> runP (go k') sk rk ek s' A.Done rest v -> sk v (S.consChunk rest s')
Bio/TwoBit.hs view
@@ -33,19 +33,19 @@ Mask(..) ) where -import Bio.Prelude hiding ( left, right, chr )+import Bio.Prelude hiding ( left, right, chr ) import Bio.Util.MMap import Bio.Util.Storable-import Control.Monad.Trans.State ( StateT(..), get, evalStateT )+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 )+import Foreign.C.Types ( CChar )+import Foreign.ForeignPtr.Unsafe ( unsafeForeignPtrToPtr ) data TwoBitFile = TBF {- tbf_raw :: B.ByteString,+ tbf_raw :: ForeignPtr CChar, -- This map is intentionally lazy. May or may not be important. tbf_seqs :: !(M.HashMap Bytes TwoBitSequence) }@@ -60,10 +60,9 @@ -- 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+ (_sz, raw) <- mmapFile fp+ withForeignPtr raw $+ evalStateT $ do sig <- getWord32be getWord32 <- case sig :: Word32 of 0x1A412743 -> return getWord32be@@ -91,10 +90,10 @@ 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 :: ForeignPtr CChar -> Get Int -> Int -> TwoBitSequence mkBlockIndex raw getWord32 ofs =- unsafePerformIO $- B.unsafeUseAsCString raw $ \praw ->+ unsafeDupablePerformIO $+ withForeignPtr raw $ \praw -> evalStateT getBlock (plusPtr praw ofs) where getBlock = do p0 <- get@@ -132,10 +131,10 @@ 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+ toDNA $ plusPtr (unsafeForeignPtrToPtr tbf_raw) (tbs_dna_offset + (start `shiftR` 2)) where- toDNA b = (++) [ 3 .&. (b `shiftR` x) | x <- [6,4,2,0] ]+ toDNA p = let !b = unsafeDupablePerformIO $ do peek p <* touchForeignPtr tbf_raw+ in [ 3 .&. (b `shiftR` x) | x <- [6,4,2,0] ] ++ toDNA (plusPtr p 1) do_mask _ _ [] = [] do_mask [ ] _ ws = map (`nt` None) ws@@ -160,10 +159,10 @@ 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+-- | Extract a subsequence and apply masking. TwoBit files 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@@ -175,6 +174,7 @@ where fwd_nt = (!!) [nucT, nucC, nucA, nucG] . fromIntegral cmp_nt = (!!) [nucA, nucG, nucT, nucC] . fromIntegral+{-# INLINE getSubseqWith #-} -- | Works only in forward direction. getLazySubseq :: TwoBitFile -> Position -> [Nucleotide]@@ -183,15 +183,16 @@ 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+{-# INLINE getLazySubseq #-} -- | Extract a subsequence without masking. getSubseq :: TwoBitFile -> Range -> [Nucleotide] getSubseq = getSubseqWith const+{-# INLINE getSubseq #-} -- | Extract a subsequence with typical masking: soft masking is -- ignored, hard masked regions are replaced with Ns.@@ -202,6 +203,7 @@ mymask n Soft = nucToNucs n mymask _ Hard = nucsN mymask _ Both = nucsN+{-# INLINE getSubseqMasked #-} -- | Extract a subsequence with masking for biologists: soft masking is -- done by lowercasing, hard masking by printing an N.@@ -212,6 +214,7 @@ mymask n Soft = toLower (showNucleotide n) mymask _ Hard = 'N' mymask _ Both = 'N'+{-# INLINE getSubseqAscii #-} getSeqnames :: TwoBitFile -> [Bytes]@@ -291,6 +294,8 @@ | 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)+ x = unsafeDupablePerformIO $+ withForeignPtr tbf_raw $ \p ->+ peekByteOff p (tbs_dna_offset + off `shiftR` 2) y = x `shiftR` (6 - 2 * (off .&. 3)) .&. 3 -- T,C,A,G
− Bio/Util/MMap.hs
@@ -1,28 +0,0 @@-{-# 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/MMap.hsc view
@@ -0,0 +1,43 @@+module Bio.Util.MMap ( mmapFile, createMmapFile ) where++import BasePrelude+import Foreign.C.Error ( getErrno, errnoToIOError )+import Foreign.C.Types+import System.Posix.Files ( fileSize, getFdStatus, setFdSize )+import System.Posix.IO ( openFd, closeFd, defaultFileFlags, OpenMode(ReadOnly,ReadWrite) )+import System.Posix.Types ( Fd(..), COff(..) )++#include <sys/mman.h>++-- | Maps a whole file into memory, returns the size in bytes and a+-- 'ForeignPtr' to the contents.+mmapFile :: FilePath -> IO (Int, ForeignPtr a)+mmapFile fp =+ bracket (openFd fp ReadOnly Nothing defaultFileFlags) closeFd $ \fd -> do+ size <- fileSize <$> getFdStatus fd+ if size <= 0+ then (,) 0 <$> newForeignPtr_ nullPtr+ else do+ ptr <- mmap nullPtr (fromIntegral size) (#const PROT_READ) (#const MAP_SHARED) fd 0+ if ptrToIntPtr ptr == #const MAP_FAILED+ then do errno <- getErrno+ ioError $ errnoToIOError "mmapFile" errno Nothing (Just fp)+ else (,) (fromIntegral size) <$> newForeignPtrEnv my_munmap (intPtrToPtr $ fromIntegral size) ptr++-- | Creates a new file of a desired initial size, maps it into memory,+-- and calls a function to fill it. That function returns a pointer to+-- the first unused byte in the file, and it is truncated accordingly.+createMmapFile :: FilePath -> CSize -> (Ptr a -> IO (Ptr a, b)) -> IO b+createMmapFile fp sz k =+ bracket (openFd fp ReadWrite (Just 0x1b6) defaultFileFlags) closeFd $ \fd -> do+ setFdSize fd (fromIntegral sz)+ bracket (mmap nullPtr sz (#const PROT_READ | PROT_WRITE) (#const MAP_SHARED) fd 0)+ (flip munmap sz) $ \p -> do+ (p',r) <- k p+ setFdSize fd (fromIntegral $ minusPtr p' p)+ return r++foreign import ccall unsafe "&my_munmap" my_munmap :: FunPtr (Ptr () -> Ptr a -> IO ())+foreign import ccall unsafe "sys/mman.h mmap" mmap :: Ptr a -> CSize -> CInt -> CInt -> Fd -> COff -> IO (Ptr a)+foreign import ccall unsafe "sys/mman.h munmap" munmap :: Ptr a -> CSize -> IO ()+
Bio/Util/Numeric.hs view
@@ -1,8 +1,7 @@ -- | Random useful stuff I didn't know where to put.- module Bio.Util.Numeric ( wilson, invnormcdf, choose,- estimateComplexity, showNum, showOOM,+ estimateComplexity, showNum, showOOM, readOOM, log1p, expm1, (<#>), log1mexp, log1pexp, lsum, llerp@@ -11,6 +10,7 @@ import Prelude import Data.Char ( intToDigit ) import Data.List ( foldl1' )+import Options.Applicative.Builder ( eitherReader, ReadM ) -- | Calculates the Wilson Score interval. -- If @(l,m,h) = wilson c x n@, then @m@ is the binary proportion and@@ -37,18 +37,29 @@ 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"++showOOM :: (Enum a, Num a, Ord a) => a -> String+showOOM x | x < 0 = '-' : showOOM (negate x)+ | otherwise = findSuffix (fromEnum (x * 100 + 5) `div` 10) ".kMGTPEZY" where- findSuffix _ [] = "many"- findSuffix y (s:ss) | y < 100 = intToDigit (round y `div` 10) : case (round y `mod` 10, s) of+ findSuffix _ [ ] = "many"+ findSuffix y (s:ss) | y < 100 = intToDigit (div y 10) : case (mod y 10, s) of (0,'.') -> [] ; (0,_) -> [s] ; (d,_) -> [s, intToDigit d]- | y < 1000 = intToDigit (round y `div` 100) : intToDigit ((round y `mod` 100) `div` 10) :+ | y < 1000 = intToDigit (div y 100) : intToDigit (mod y 100 `div` 10) : if s == '.' then [] else [s]- | y < 10000 = intToDigit (round y `div` 1000) : intToDigit ((round y `mod` 1000) `div` 100) :+ | y < 10000 = intToDigit (div y 1000) : intToDigit (mod y 1000 `div` 100) : '0' : if s == '.' then [] else [s]- | otherwise = findSuffix (y*0.001) ss+ | otherwise = findSuffix (div y 1000) ss++readOOM :: (Read a, Num a) => ReadM a+readOOM = eitherReader $ \s -> case reads s of+ [(n,[ ])] -> Right n+ [(n,"k")] -> Right $ n * 1000+ [(n,"M")] -> Right $ n * 1000000+ [(n,"G")] -> Right $ n * 1000000000+ [(n,"T")] -> Right $ n * 1000000000000+ _ -> Left $ "unable to parse: " ++ show s+ -- 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.
Bio/Util/Text.hs view
@@ -13,6 +13,7 @@ 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.Char8 as C ( unpack ) import qualified Data.ByteString.Lazy.Internal as L ( ByteString(..) ) import qualified Data.Text as T @@ -20,6 +21,7 @@ -- opposite of 'IsString'. class Unpack s where unpack :: s -> String +instance Unpack L.ByteString where unpack = C.unpack instance Unpack S.ByteString where unpack = S.unpack instance Unpack T.Text where unpack = T.unpack instance Unpack String where unpack = id
CHANGELOG.md view
@@ -1,4 +1,13 @@-# 2.0.0+# 2.1 (2020-01-06)++ * Proper error handling. Instead of calling error, now either a+ warning is logged or a proper exception is thrown.++ * Faster fastq parser, faster fusion of read pairs.++ * Cleanup of messy code. Ghc >= 7.10 is now required.++# 2.0 (2019-05-28) * Switched from "iteratee" to "streaming". The code looks much cleaner and is easier to understand.
+ Control/Monad/Log.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE UndecidableInstances, TypeFamilies #-}+module Control.Monad.Log+ ( MonadLog(..)+ , Level(..)+ , LoggingConf(..)+ , Logged(..)+ , LIO+ , withLogging+ , withLogging_+ , logOptions+ , execWithParser+ , execWithParser_+ , PanicCall(..)+ , panic+ ) where++import BasePrelude hiding ( try, catchIOError )+import Control.Monad.Base ( MonadBase(..) )+import Control.Monad.Catch+import Control.Monad.Primitive+import Control.Monad.Trans.Class+import Control.Monad.Trans.Control+import Control.Monad.Trans.Reader+import Control.Monad.Trans.RWS.Strict ( RWST )+import GitVersion ( gitFullVersion )+import Options.Applicative+import Paths_biohazard ( version )+import Streaming+import System.IO ( hPutStr, hPutStrLn, hFlush, stderr, openFile, IOMode(..) )++import qualified Data.Vector as V++-- | Severity levels for logging.+data Level = Debug -- ^ Message only useful for debugging. Typically ignored.+ | Info -- ^ Purely informative message, e.g. progress reports. Sometimes printed.+ | Notice -- ^ Something remarkable, but harmless. Sometimes printed, but not collected.+ | Warning -- ^ Something unexpected, but usually not a problem. Typically printed, but not collected.+ | Error -- ^ Recoverable error, will normally result in `ExitFailure 1`. Printed and collected.+ deriving ( Show, Eq, Ord, Enum, Bounded, Ix )++color_coded :: Level -> String -> String+color_coded Debug s = "\27[90m" ++ s ++ "\27[0m" -- gray+color_coded Info s = "\27[34m" ++ s ++ "\27[0m" -- blue+color_coded Notice s = "\27[32;1m" ++ s ++ "\27[0m" -- bold green+color_coded Warning s = "\27[33m" ++ s ++ "\27[0m" -- yellow+color_coded Error s = "\27[31;1m" ++ s ++ "\27[0m" -- bold red++-- | Monads in which messages can be logged. Any 'Exception' can be+-- logged; it is reported and/or collected, but does not abort any+-- computation.+class Monad m => MonadLog m where+ -- | Logs a message at a given level. Depending on settings, the+ -- message may be printed and/or stored.+ logMsg :: Exception e => Level -> e -> m ()++ -- | Updates the progress indicator. The message should not contain+ -- line feeds, as it is intended to fit on one line and be+ -- overwritten repeatedly.+ logString_ :: String -> m ()++ -- | Prints a progress indication. The message should persist on+ -- the user's terminal.+ logStringLn :: String -> m ()++instance (MonadLog m, Monoid w) => MonadLog (RWST r w s m) where+ logMsg l e = lift (logMsg l e)+ logString_ e = lift (logString_ e)+ logStringLn e = lift (logStringLn e)+++-- | Adds logging to any 'MonadIO' type. Warnings are printed+-- to stderr immediately, but we remember whether any were emitted. If+-- so, we exit with an error code. The advantage over @WarningT IO@ is+-- that the warnings are tracked even if the computation exits with an+-- exception. Progress indicators are sent to the controlling terminal,+-- and dicarded if none exists.+newtype Logged m a = Logged { runLogged :: ReaderT (LoggingConf, Journal) m a }+ deriving ( Functor, Applicative, Alternative, Monad, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask, MFunctor )++instance MonadTransControl Logged where+ type StT Logged a = StT (ReaderT (LoggingConf, Journal)) a+ liftWith = defaultLiftWith Logged runLogged+ restoreT = defaultRestoreT Logged++instance MonadBase b m => MonadBase b (Logged m) where+ liftBase = lift . liftBase++instance MonadBaseControl b m => MonadBaseControl b (Logged m) where+ type StM (Logged m) a = StM (ReaderT (LoggingConf, Journal) m) a+ liftBaseWith f = defaultLiftBaseWith f+ restoreM = defaultRestoreM++instance PrimMonad m => PrimMonad (Logged m) where+ type PrimState (Logged m) = PrimState m+ primitive = lift . primitive++type LIO = Logged IO++data LoggingConf = LoggingConf+ { reporting_level :: Level -- ^ minimum 'Level' to print a message+ , logging_level :: Level -- ^ minimum 'Level' to remember a message+ , error_level :: Level -- ^ minimum 'Level' that results in a call to 'exitFailure'+ , max_log_size :: Int -- ^ number of messages to keep at any given level+ , want_progress :: Bool }+ deriving Show++data Journal = Journal+ { logged_messages :: V.Vector (IORef [SomeException]) -- ^ collected messages per level+ , num_messages :: V.Vector (IORef Int) -- ^ number of collected messages per level+ , error_exit :: IORef Bool+ , cterminal :: Maybe Handle+ , spinner :: IORef String }++instance MonadIO m => MonadLog (Logged m) where+ logMsg lv e = Logged $ ReaderT $ \(LoggingConf{..},Journal{..}) -> do+ when (lv >= reporting_level) $ liftIO $ do+ -- clear spinner+ forM_ cterminal $ \h -> tryIO $ hPutStr h "\r\27[K" >> hFlush h+ pn <- getProgName+ hPutStrLn stderr $ color_coded lv $ printf "%s: [%s] %s" pn (show lv) (displayException e)+ hFlush stderr+ -- restore spinner+ forM_ cterminal $ \h -> readIORef spinner >>= \s ->+ hPutStr h ("\27[?7l" ++ s ++ "\27[?7h") >> hFlush h+ when (lv >= logging_level) $ liftIO $+ atomicModifyIORef' (num_messages V.! fromEnum lv)+ (\num -> if num < max_log_size then (succ num, True) else (num, False)) >>=+ flip when (atomicModifyIORef (logged_messages V.! fromEnum lv)+ (\es -> (toException e : es, ())))+ when (lv >= error_level) $ liftIO $+ atomicWriteIORef error_exit True++ logString_ m = Logged $ ReaderT $ \(LoggingConf{..},Journal{..}) ->+ liftIO $ forM_ cterminal $ \h -> do+ pn <- getProgName+ let s = if null m then m else pn ++ ": " ++ m+ writeIORef spinner s+ tryIO $ hPutStr h ("\r\27[K\27[?7l" ++ s ++ "\27[?7h") >> hFlush h++ logStringLn m = Logged $ ReaderT $ \(LoggingConf{..},Journal{..}) ->+ liftIO $ forM_ cterminal $ \h -> do+ s <- readIORef spinner+ tryIO $ hPutStr h ("\r\27[K" ++ m ++ "\n\27[?7l" ++ s ++ "\27[?7h") >> hFlush h+++withLogging_ :: (MonadIO m, MonadMask m) => LoggingConf -> Logged m a -> m a+withLogging_ conf = withLogging conf >=> either (liftIO . exitWith) pure++withLogging :: (MonadIO m, MonadMask m) => LoggingConf -> Logged m a -> m (Either ExitCode a)+withLogging conf (Logged k) = do+ journal <- let n = fromEnum (maxBound :: Level) - fromEnum (minBound :: Level) + 1+ in liftIO $ Journal <$> V.replicateM n (newIORef [])+ <*> V.replicateM n (newIORef 0)+ <*> newIORef False+ <*> bool (pure Nothing) (tryIO $ openFile "/dev/tty" WriteMode) (want_progress conf)+ <*> newIORef []++ r <- try $ runReaderT k (conf,journal)+ liftIO $ do+ ws <- V.mapM readIORef (logged_messages journal)+ nws <- V.mapM readIORef (num_messages journal)+ pn <- getProgName+ forM_ (cterminal journal) $ \h -> do+ s <- readIORef (spinner journal)+ tryIO $ unless (null s) (hPutStrLn h []) >> hClose h++ do let eff_warnings = [ (l,e) | l <- [minBound ..], l < error_level conf, e <- ws V.! fromEnum l ]+ neff_warnings = sum [ n | l <- [minBound ..], l < error_level conf, let n = nws V.! fromEnum l ]+ unless (neff_warnings == 0) $ do+ hPrintf stderr "%s: there were %d warnings\n" pn neff_warnings+ forM_ eff_warnings $ \(l,e) -> hPutStrLn stderr . color_coded l $ displayException e+ unless (neff_warnings - length eff_warnings <= 0 || null eff_warnings) $+ hPrintf stderr "(and %d more)\n" (neff_warnings - length eff_warnings)++ do let eff_errors = [ (l,e) | l <- [error_level conf ..], e <- ws V.! fromEnum l ]+ neff_errors = sum [ n | l <- [error_level conf ..], let n = nws V.! fromEnum l ]+ unless (null eff_errors) $ do+ hPrintf stderr "%s: there were %d (non-catastrophic) errors\n" pn neff_errors+ forM_ eff_errors $ \(l,e) -> hPutStrLn stderr . color_coded l $ displayException e+ unless (neff_errors - length eff_errors <= 0 || null eff_errors) $+ hPrintf stderr "(and %d more)\n" (neff_errors - length eff_errors)++ case r of+ Left e -> do case fromException e of+ Just UserInterrupt -> hPutStrLn stderr $ pn ++ ": Interrupted"+ _ -> hPutStrLn stderr $ pn ++ ": catastrophic error: " ++ displayException e+ return . Left $ ExitFailure 2++ Right x -> bool (Right x) (Left $ ExitFailure 1) <$> readIORef (error_exit journal)+++-- | General wrapper around main. Runs a command line parser with added+-- standard options (logging and usage related), runs the actual main+-- function, prints collected warnings and caught exceptions, and exits+-- appropriately: `exitWith (ExitFailure 2)` if an exception was+-- caught, `exitFailure` if there were warnings of sufficient severity,+-- and `exitSuccess` otherwise.++execWithParser_ :: Parser a -> Maybe Version -> Maybe String -> InfoMod (a,LoggingConf) -> (a -> LIO b) -> IO b+execWithParser_ opts prog_ver prog_git_ver inf =+ execWithParser opts prog_ver prog_git_ver inf >=> either exitWith pure++execWithParser :: Parser a -> Maybe Version -> Maybe String -> InfoMod (a,LoggingConf)+ -> (a -> LIO b) -> IO (Either ExitCode b)+execWithParser opts prog_ver prog_git_ver inf k = do+ pn <- getProgName+ let verStr = printf "%s%s (%s) using biohazard-%s (%s)" pn+ (maybe "" (('-':) . showVersion) prog_ver) (fromMaybe "release" prog_git_ver)+ (showVersion version) (fromMaybe "release" gitFullVersion)+ verOpt = infoOption verStr (short 'V' <> long "version" <> help "Print version number and exit")+ (a,cf) <- execParser $ info ((,) <$> opts <*> logOptions <* verOpt <* helper) inf+ withLogging cf (k a)++logOptions :: Parser LoggingConf+logOptions =+ LoggingConf+ <$> (foldl (&) Notice <$> many+ (flag' more (long "quiet" <> help "Print only important messages") <|>+ flag' less (long "verbose" <> help "Print also trivial messages")))++ <*> (foldl (&) Warning <$> many+ (flag' more (long "drop-errors" <> help "Remember only critical messages") <|>+ flag' less (long "keep-warnings" <> help "Remember also minor messages")))++ <*> (foldl (&) Error <$> many+ (flag' more (long "warn-ignore" <> help "Fail only after critical errors") <|>+ flag' less (long "warn-error" <> help "Fail also after warnings")))++ <*> option auto (long "journal-size" <> metavar "NUM" <> help "Hold up to NUM errors in memory" <> value 20)+ <*> switch (long "progress" <> help "Print progress reports to the terminal")+ where+ more, less :: (Enum a, Bounded a, Eq a) => a -> a+ more a = if a == maxBound then a else succ a+ less a = if a == minBound then a else pred a+++-- | An exception than can be thrown when it doesn't seem warranted to+-- define a custom exception. Transports a message.+data PanicCall = PanicCall String deriving (Typeable, Show)+instance Exception PanicCall where displayException (PanicCall msg) = msg++panic :: MonadIO m => String -> m a+panic = liftIO . throwIO . PanicCall++tryIO :: IO k -> IO (Maybe k)+tryIO k = catchIOError (Just <$> k) (\_ -> pure Nothing)+
Setup.hs view
@@ -1,2 +1,50 @@-import Distribution.Simple-main = defaultMain+import Data.List ( isPrefixOf )+import Distribution.Compat.Exception ( tryIO )+import Distribution.PackageDescription ( PackageDescription(..) )+import Distribution.Simple ( UserHooks(..), defaultMainWithHooks, simpleUserHooks )+import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..) )+import Distribution.Simple.Program.Run ( getProgramInvocationOutput, simpleProgramInvocation )+import Distribution.Simple.Utils ( createDirectoryIfMissingVerbose )+import Distribution.Verbosity ( Verbosity, silent )++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks+ { buildHook = \pkgDesc lbi hooks flags -> do+ git_version <- gitDescribe+ git_branch <- gitBranch+ let git_full_version = fmap (maybe "" (++":") git_branch ++) git_version+ createDirectoryIfMissingVerbose silent False (buildDir lbi ++ "/autogen")+ writeFileIfChanged (buildDir lbi ++ "/autogen/GitVersion.hs") $ unlines+ [ "module GitVersion (gitVersion,gitBranch,gitFullVersion) where"+ , "import Prelude"+ , "gitVersion :: Maybe String"+ , "gitVersion = " ++ show git_version+ , "{-# NOINLINE gitVersion #-}"+ , "gitBranch :: Maybe String"+ , "gitBranch = " ++ show git_branch+ , "{-# NOINLINE gitBranch #-}"+ , "gitFullVersion :: Maybe String"+ , "gitFullVersion = " ++ show git_full_version+ , "{-# NOINLINE gitFullVersion #-}" ]+ buildHook simpleUserHooks pkgDesc lbi hooks flags+ }++writeFileIfChanged :: FilePath -> String -> IO ()+writeFileIfChanged fp new = do+ old <- tryIO $ do s <- readFile fp ; length s `seq` return s+ if old == Right new then return () else writeFile fp new++gitDescribe :: IO (Maybe String)+gitDescribe =+ fmap (either (const Nothing) (Just . unwords . lines)) $ tryIO $+ getProgramInvocationOutput silent $ simpleProgramInvocation "git" ["describe", "--always", "--long"]++gitBranch :: IO (Maybe String)+gitBranch =+ fmap (either (const Nothing) get_branch) $ tryIO $+ getProgramInvocationOutput silent $ simpleProgramInvocation "git" ["branch"]+ where+ get_branch s = case map words . filter ("*" `isPrefixOf`) $ lines s of+ [_:b:_] -> Just b+ _ -> Nothing+
biohazard.cabal view
@@ -1,5 +1,6 @@+Cabal-version: 2.0 Name: biohazard-Version: 2.0 +Version: 2.1 Synopsis: bioinformatics support library Description: This is a collection of modules I separated from various bioinformatics tools.@@ -12,16 +13,19 @@ Author: Udo Stenzel Maintainer: u.stenzel@web.de-Copyright: (C) 2010-2017 Udo Stenzel+Copyright: (C) 2010-2019 Udo Stenzel -Cabal-version: >= 1.10-Build-type: Simple-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+Build-type: Custom+Tested-with: GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1, GHC == 8.8.1 source-repository head type: git location: https://bitbucket.org/ustenzel/biohazard.git +custom-setup+ setup-depends: base >= 4.8 && < 4.14,+ Cabal >= 2.0 && < 3.1+ Library Exposed-modules: Bio.Adna, Bio.Align,@@ -51,31 +55,36 @@ Bio.Util.Nub, Bio.Util.Numeric, Bio.Util.Storable,- Bio.Util.Text+ Bio.Util.Text,+ Control.Monad.Log - Build-depends: attoparsec >= 0.10 && < 0.14,- base >= 4.7 && < 4.13,- base-prelude == 1.2.0.*,- bytestring >= 0.10.2 && < 0.11,+ Other-modules: Paths_biohazard, GitVersion+ Autogen-modules: Paths_biohazard, GitVersion++ Build-depends: attoparsec >= 0.13 && < 0.14,+ base >= 4.8 && < 4.14,+ base-prelude >= 1.2 && < 1.2.1,+ bytestring >= 0.10.6 && < 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,+ directory >= 1.2.2 && < 1.4,+ exceptions >= 0.6.1 && < 0.11,+ hashable >= 1.2.3.2 && < 1.4,+ monad-control == 1.0.*,+ optparse-applicative >= 0.13 && < 0.16,+ primitive >= 0.6.1 && < 0.8, 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,+ text >= 1.2.0.2 && < 1.3,+ transformers >= 0.4.2 && < 0.6,+ transformers-base == 0.4.*,+ unix >= 2.7.1 && < 2.8,+ unordered-containers >= 0.2.5.1 && < 0.3, vector >= 0.11 && < 0.13,- vector-algorithms >= 0.3 && < 0.8,+ vector-algorithms >= 0.8 && < 0.9, zlib == 0.6.* - if !impl(ghc >= 7.10)- build-depends: bifunctors == 5.* if !impl(ghc >= 8.0)- build-depends: semigroups == 0.18.*+ build-depends: semigroups >= 0.18 && < 0.20 Ghc-options: -Wall if impl(ghc >= 8.0)@@ -103,16 +112,18 @@ ExistentialQuantification, ForeignFunctionInterface, Rank2Types,- TypeFamilies+ StandaloneDeriving,+ TypeFamilies,+ UndecidableInstances + Build-tool-depends: hsc2hs:hsc2hs Hs-source-dirs: . Include-dirs: cbits- Install-Includes: myers_align.h+ Install-includes: myers_align.h C-sources: cbits/loops.c,- cbits/mmap.c, cbits/myers_align.c, cbits/trim.c+ cbits/util.c cbits/zlib.c- CC-options: -fPIC -- :vim:tw=132:
− 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 ) ; -}
cbits/trim.c view
@@ -1,28 +1,32 @@ #include <stdint.h>+#include <limits.h> -static const uint8_t compls[] =- { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 } ;+#define MIN(a,b) ((a)<(b)?(a):(b))+#define MAX(a,b) ((a)>(b)?(a):(b)) -int prim_match_reads( int i1- , int i2- , int r+/* This version receives the second sequence reverse-complemented and+ * the second quality vector reversed. Allows for a forward loop,+ * doesn't need the lookup table. */+int prim_match_reads( int i1 // matches from i2+ , int i2 // ???+ , int r // length to match , const uint8_t *rd1 , const uint8_t *qs1- , const uint8_t *rd2- , const uint8_t *qs2 )+ , const uint8_t *rc_rd2+ , const uint8_t *rv_qs2 ) { int acc = 0 ; while( r != 0 ) {- --i2 ; uint8_t n1 = rd1[ i1 ] ;- uint8_t n2 = rd2[ i2 ] ;+ uint8_t n2 = rc_rd2[ i2 ] ; uint8_t q1 = qs1[ i1 ] ;- uint8_t q2 = qs2[ i2 ] ;+ uint8_t q2 = rv_qs2[ i2 ] ; - acc += (n1 & 0xF) == compls[ n2 & 0xF ] ? 0 : 5 + (q1 < q2 ? q1 : q2) ;+ acc += (n1 & 0xF) == (n2 & 0xF) ? 0 : 5 + (q1 < q2 ? q1 : q2) ; ++i1 ;+ ++i2 ; --r ; } return acc ;@@ -42,5 +46,68 @@ (qs[ i+off ] < 25 ? qs[ i+off ] : 25) ; } return acc ;+}++// return the length that minimizes the score+int prim_merge_score (+ uint8_t **p_fwd_ads,+ int *p_fwd_lns,+ int n_fwd_adapters,++ uint8_t **p_rev_ads,+ int *p_rev_lns,+ int n_rev_adapters,++ uint8_t *p_rd1,+ uint8_t *p_qs1,+ int l1,+ uint8_t *p_rd2,+ uint8_t *p_qs2,+ int l2,+ uint8_t *p_rrd2,+ uint8_t *p_rqs2,++ int *scores )+{+ int minl = -1 ;+ int min0 = 6 * (l1+l2) ;+ int min1= INT_MAX ;++ for( int l = 0 ; l <= l1+l2 ; ++l )+ {+ int ff = INT_MAX ;+ for( int i = 0 ; i != n_fwd_adapters ; ++i )+ {+ int efflength = MIN(l1 - l, p_fwd_lns[i]) ;+ int x = prim_match_ad( l, efflength, p_rd1, p_qs1, p_fwd_ads[i] ) ;+ ff = MIN(ff, x + 6 * MAX( 0, l1 - p_fwd_lns[i] - l)) ;+ }++ int rr = INT_MAX ;+ for( int i = 0 ; i != n_rev_adapters ; ++i )+ {+ int efflength = MIN(l2 - l,p_rev_lns[i]) ;+ int x = prim_match_ad( l, efflength, p_rd2, p_qs2, p_rev_ads[i] ) ;+ rr = MIN(rr, x + 6 * MAX( 0, l1 - p_rev_lns[i] - l)) ;+ }++ int minidx1 = MAX(l - l2, 0) ; // vec1, forward+ int minidx2 = MAX(l2 - l, 0) ; // vec2, forward-on-reverse+ int efflength = MIN(l1 + l2 - l, l) ; // effective length++ int mm = efflength <= 0 ? 0 : prim_match_reads( minidx1, minidx2, efflength, p_rd1, p_qs1, p_rrd2, p_rqs2 ) ;+ int z = 6 * l + ff + rr + mm ;++ if( z < min0 ) {+ min1 = min0 ;+ min0 = z ;+ minl = l ;+ } else if( z < min1 ) {+ min1 = z ;+ }+ }+ scores[0] = min1 - min0 ;+ scores[1] = 6*(l1+l2) - min0 ;+ return minl ; }
+ cbits/util.c view
@@ -0,0 +1,8 @@+#include <sys/mman.h>++void my_munmap(void *len, unsigned char *p)+{+ munmap( p, (size_t)len ) ;+}++