packages feed

mmtf (empty) → 0.1.0.0

raw patch · 11 files changed

+560/−0 lines, 11 filesdep +QuickCheckdep +basedep +binarysetup-changed

Dependencies added: QuickCheck, base, binary, bytestring, containers, data-msgpack, hspec, mmtf, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Pavel Yakovlev (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Pavel Yakovlev nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ mmtf.cabal view
@@ -0,0 +1,47 @@+name:                mmtf+version:             0.1.0.0+synopsis:            Macromolecular Transmission Format implementation+description:         Haskell implementation of MMTF biological structure format.+homepage:            https://github.com/zmactep/mmtf#readme+license:             BSD3+license-file:        LICENSE+author:              Pavel Yakovlev+maintainer:          pavel@yakovlev.me+copyright:           (c) 2017, Pavel Yakovlev+category:            Bioinformatics+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Bio.MMTF+  other-modules:       Bio.MMTF.Type+                     , Bio.MMTF.MessagePack+                     , Bio.MMTF.Decode+                     , Bio.MMTF.Decode.Codec+                     , Bio.MMTF.Decode.MessagePack+  build-depends:       base >= 4.8 && < 5+                     , data-msgpack >= 0.0.9 && < 0.1+                     , text >= 1.2.2.1 && < 1.3+                     , bytestring >= 0.10.8.1 && < 0.11+                     , binary >= 0.8.3.0 && < 0.9+                     , containers >= 0.5.7.1 && < 0.6+  default-language:    Haskell2010+  ghc-options:         -Wall++test-suite mmtf-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , mmtf+                     , QuickCheck >= 2.9.2 && < 2.10+                     , hspec >= 2.4.1 && < 2.5+                     , bytestring+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/zmactep/mmtf
+ src/Bio/MMTF.hs view
@@ -0,0 +1,14 @@+module Bio.MMTF+  ( module T+  , decode+  ) where++import           Bio.MMTF.MessagePack ()+import           Bio.MMTF.Type        as T++import           Data.ByteString.Lazy (ByteString)+import           Data.MessagePack     (unpack)++-- |Decodes a 'ByteString' to 'MMTF'+decode :: Monad m => ByteString -> m MMTF+decode = unpack
+ src/Bio/MMTF/Decode.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE OverloadedStrings #-}++module Bio.MMTF.Decode where++import           Bio.MMTF.Decode.Codec+import           Bio.MMTF.Decode.MessagePack+import           Bio.MMTF.Type++import           Control.Monad               ((>=>))+import           Data.Map.Strict             (Map)+import           Data.MessagePack            (Object)+import           Data.Text                   (Text)++-- |Parses format data from ObjectMap+formatData :: Monad m => Map Text Object -> m FormatData+formatData mp = do v <- atP mp "mmtfVersion" asStr+                   p <- atP mp "mmtfProducer" asStr+                   pure $ FormatData v p++-- |Parses model data from ObjectMap+modelData :: Monad m => Map Text Object -> m ModelData+modelData mp = atP mp "chainsPerModel" asIntList >>= return . ModelData++-- |Parses chain data from ObjectMap+chainData :: Monad m => Map Text Object -> m ChainData+chainData mp = do gpc <- atP mp "groupsPerChain" asIntList+                  cil <- codec5 . parseBinary <$> atP mp "chainIdList" asBinary+                  cnl <- (codec5 . parseBinary <$>) <$> atPM mp "chainNameList" asBinary+                  pure $ ChainData gpc cil cnl++-- |Parses atom data from ObjectMap+atomData :: Monad m => Map Text Object -> m AtomData+atomData mp = do ail' <- (codec8 . parseBinary <$>) <$> atPM mp "atomIdList" asBinary+                 all' <- (codec6 . parseBinary <$>) <$> atPM mp "altLocList" asBinary+                 bfl' <- (codec10 . parseBinary <$>) <$> atPM mp "bFactorList" asBinary+                 xcl' <- codec10 . parseBinary <$> atP mp "xCoordList" asBinary+                 ycl' <- codec10 . parseBinary <$> atP mp "yCoordList" asBinary+                 zcl' <- codec10 . parseBinary <$> atP mp "zCoordList" asBinary+                 ol' <-  (codec9 . parseBinary <$>) <$> atPM mp "occupancyList" asBinary+                 pure $ AtomData ail' all' bfl' xcl' ycl' zcl' ol'++-- |Parses group data from ObjectMap+groupData :: Monad m => Map Text Object -> m GroupData+groupData mp = do gl' <- atP mp "groupList" asObjectList >>= sequence . map (transformObjectMap >=> groupType)+                  gtl' <- codec4 . parseBinary <$> atP mp "groupTypeList" asBinary+                  gil' <- codec8 . parseBinary <$> atP mp "groupIdList" asBinary+                  ssl' <- (map ssDec . codec2 . parseBinary <$>) <$> atPM mp "secStructList" asBinary+                  icl' <- (codec6 . parseBinary <$>) <$> atPM mp "insCodeList" asBinary+                  sil' <- (codec8 . parseBinary <$>) <$> atPM mp "sequenceIndexList" asBinary+                  pure $ GroupData gl' gtl' gil' ssl' icl' sil'++-- |Parses group type from ObjectMap+groupType :: Monad m => Map Text Object -> m GroupType+groupType mp = do fcl' <- atP mp "formalChargeList" asIntList+                  anl' <- atP mp "atomNameList" asStrList+                  el'  <- atP mp "elementList" asStrList+                  bal' <- atP mp "bondAtomList" asIntList+                  bol' <- atP mp "bondOrderList" asIntList+                  gn'  <- atP mp "groupName" asStr+                  slc' <- atP mp "singleLetterCode" asChar+                  cct' <- atP mp "chemCompType" asStr+                  pure $ GroupType fcl' anl' el' bal' bol' gn' slc' cct'++-- |Parses structure data from ObjectMap+structureData :: Monad m => Map Text Object -> m StructureData+structureData mp = do ttl' <- atPM mp "title" asStr+                      sid' <- atPM mp "structureId" asStr+                      dd'  <- atPM mp "depositionDate" asStr+                      rd'  <- atPM mp "releaseDate" asStr+                      nb'  <- atP mp "numBonds" asInt+                      na'  <- atP mp "numAtoms" asInt+                      ng'  <- atP mp "numGroups" asInt+                      nc'  <- atP mp "numChains" asInt+                      nm'  <- atP mp "numModels" asInt+                      sg'  <- atPM mp "spaceGroup" asStr+                      uc'  <- (>>= ucDec) <$> atPM mp "unitCell" asFloatList+                      nol' <- ((>>= asFloatList) <$>) <$> atPM mp "ncsOperatorList" asObjectList+                      bal' <- (>>= sequence . map (transformObjectMap >=> bioAssembly)) <$> atPM mp "bioAssemblyList" asObjectList+                      el'  <- (>>= sequence . map (transformObjectMap >=> entity)) <$> atPM mp "entityList" asObjectList+                      res' <- atPM mp "resolution" asFloat+                      rf'  <- atPM mp "rFree" asFloat+                      rw'  <- atPM mp "rWork" asFloat+                      em'  <- atPM mp "experimentalMethods" asStrList+                      btl' <- (codec4 . parseBinary <$>) <$> atPM mp "bondAtomList" asBinary+                      bol' <- (codec2 . parseBinary <$>) <$> atPM mp "bondOrderList" asBinary+                      pure $ StructureData ttl' sid' dd' rd' nb' na' ng' nc' nm' sg' uc' nol'+                                           bal' el' res' rf' rw' em' btl' bol'++-- |Parses bio assembly data from ObjectMap+bioAssembly :: Monad m => Map Text Object -> m Assembly+bioAssembly mp = do nme' <- atP mp "name" asStr+                    tlt' <- atP mp "transformList" asObjectList >>= sequence . map (transformObjectMap >=> transform)+                    pure $ Assembly tlt' nme'++-- |Parses transform data from ObjectMap+transform :: Monad m => Map Text Object -> m Transform+transform mp = do cil' <- atP mp "chainIndexList" asIntList+                  mtx' <- atP mp "matrix" asFloatList+                  pure $ Transform cil' mtx'++-- |Parses entity data from ObjectMap+entity :: Monad m => Map Text Object -> m Entity+entity mp = do cil' <- atP mp "chainIndexList" asIntList+               dsc' <- atP mp "description" asStr+               tpe' <- atP mp "type" asStr+               sqc' <- atP mp "sequence" asStr+               pure $ Entity cil' dsc' tpe' sqc'
+ src/Bio/MMTF/Decode/Codec.hs view
@@ -0,0 +1,148 @@+module Bio.MMTF.Decode.Codec where++import           Data.Binary          (Binary, decode)+import           Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B (length, null, splitAt, unpack)+import           Data.Char            (chr)+import           Data.Int             (Int16, Int32, Int8)+import           Data.List            (mapAccumL)+import           Data.Text            (Text)+import qualified Data.Text            as T (pack)++import           Bio.MMTF.Type++codecCommon :: Binary a => (ByteString -> a) -> Int -> ByteString -> [a]+codecCommon  f th bs | B.null bs         = []+                     | B.length bs < ith = error "Wrong number of bytes in bytestring"+                     | otherwise         = let (start, rest) = B.splitAt ith bs+                                           in f start : codecCommon f th rest+  where ith = fromIntegral th++data BinaryData = BD { binaryCodec  :: Int32+                     , binaryLength :: Int32+                     , binaryParam  :: Int32+                     , binaryData   :: ByteString+                     }++-- |Parse useless header for binary data+parseBinary :: ByteString -> BinaryData+parseBinary bs = let (cdc, rest1) = B.splitAt 4 bs+                     (lnh, rest2) = B.splitAt 4 rest1+                     (prm, rest)  = B.splitAt 4 rest2+                 in  BD (decode cdc) (decode lnh) (decode prm) rest++-- |Interpret bytes as array of 32-bit floating-point numbers.+codec1 :: BinaryData -> [Float]+codec1 = codecCommon decode 4 . binaryData++-- |Interpret bytes as array of 8-bit signed integers.+codec2 :: BinaryData -> [Int8]+codec2 = codecCommon decode 1 . binaryData++-- |Interpret bytes as array of 16-bit signed integers.+codec3 :: BinaryData -> [Int16]+codec3 = codecCommon decode 2 . binaryData++-- |Interpret bytes as array of 32-bit signed integers.+codec4 :: BinaryData -> [Int32]+codec4 = codecCommon decode 4 . binaryData++-- |Interpret bytes as array of 8-bit unsigned integers, then iteratively+-- consume length many bytes to form a string array.+codec5 :: BinaryData -> [Text]+codec5 bd = codecCommon decodeBytes (fromIntegral $ binaryParam bd) (binaryData bd)+  where decodeBytes :: ByteString -> Text+        decodeBytes bs = T.pack $ chr <$> filter (/=0) (fromIntegral <$> B.unpack bs)++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of characters.+codec6 :: BinaryData -> [Char]+codec6 = map (chr . fromIntegral) . codec7++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers.+codec7 :: BinaryData -> [Int32]+codec7 = runLengthDec . codec4++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers, then delta decode into+-- array of 32-bit signed integers.+codec8 :: BinaryData -> [Int32]+codec8 = deltaDec . codec7++-- |Interpret bytes as array of 32-bit signed integers, then run-length+-- decode into array of 32-bit signed integers, then integer decode into+-- array of 32-bit floating-point numbers using the divisor parameter.+codec9 :: BinaryData -> [Float]+codec9 bd = integerDec (binaryParam bd) $ codec7 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack into+-- array of 32-bit integers, then delta decode into array of 32-bit+-- integers, then integer decode into array of 32-bit floating-point+-- numbers using the divisor parameter.+codec10 :: BinaryData -> [Float]+codec10 bd = integerDec (binaryParam bd) $ map fromIntegral $ deltaDec $ codec3 bd++-- |Interpret bytes as array of 16-bit signed integers, then integer+-- decode into array of 32-bit floating-point numbers using the divisor parameter.+codec11 :: BinaryData -> [Float]+codec11 bd = integerDec (binaryParam bd) $ map fromIntegral $ codec3 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack into+-- array of 32-bit signed integers, then integer decode into array+-- of 32-bit floating-point numbers using the divisor parameter.+codec12 :: BinaryData -> [Float]+codec12 bd = integerDec (binaryParam bd) $ recIndexDec $ codec3 bd++-- |Interpret array of bytes as array of 8-bit signed integers, then+-- unpack into array of 32-bit signed integers, then integer decode into+-- array of 32-bit floating-point numbers using the divisor parameter.+codec13 :: BinaryData -> [Float]+codec13 bd = integerDec (binaryParam bd) $ recIndexDec $ codec2 bd++-- |Interpret bytes as array of 16-bit signed integers, then unpack+-- into array of 32-bit signed integers.+codec14 :: BinaryData -> [Int32]+codec14 bd = recIndexDec $ codec3 bd++-- |Interpret bytes as array of 8-bit signed integers, then unpack+-- into array of 32-bit signed integers.+codec15 :: BinaryData -> [Int32]+codec15 bd = recIndexDec $ codec2 bd++-- Decodings++runLengthDec :: Integral a => [a] -> [a]+runLengthDec [] = []+runLengthDec [_] = error "List must have even length for run-length encoding"+runLengthDec (x:l:xs) = (replicate (fromIntegral l) x) ++ runLengthDec xs++deltaDec :: Num a => [a] -> [a]+deltaDec = snd . mapAccumL (\x y -> (x+y,x+y)) 0++recIndexDec :: (Integral a, Bounded a, Eq a) => [a] -> [Int32]+recIndexDec [] = []+recIndexDec xs = recIndexDecAcc 0 xs+  where recIndexDecAcc :: (Integral a, Bounded a) => Int32 -> [a] -> [Int32]+        recIndexDecAcc acc []     | acc /= 0  = [acc]+                                  | otherwise = []+        recIndexDecAcc acc (x:ys) | x > minBound && x < maxBound = fromIntegral x + acc : recIndexDecAcc 0 ys+                                  | otherwise                    = recIndexDecAcc (fromIntegral x + acc) ys++integerDec :: Integral a => a -> [a] -> [Float]+integerDec divisor = map (\x -> fromIntegral x / fromIntegral divisor)++ssDec :: Int8 -> SecondaryStructure+ssDec n | n == 0    = PiHelix+        | n == 1    = Bend+        | n == 2    = AlphaHelix+        | n == 3    = Extended+        | n == 4    = ThreeTenHelix+        | n == 5    = Bridge+        | n == 6    = Turn+        | n == 7    = Coil+        | otherwise = Undefined++ucDec :: Monad m => [Float] -> m UnitCell+ucDec [a,b,c,d,e,f] = pure $ UnitCell a b c d e f+ucDec _  = fail "Wrong list format for unit cell"
+ src/Bio/MMTF/Decode/MessagePack.hs view
@@ -0,0 +1,61 @@+module Bio.MMTF.Decode.MessagePack where++import           Data.ByteString.Lazy  (ByteString, fromStrict)+import           Data.Map.Strict       (Map, fromList)+import qualified Data.Map.Strict       as M (lookup)+import           Data.MessagePack+import           Data.Text             (Text)+import qualified Data.Text             as T (unpack)++transformObjectMap :: Monad m => Object -> m (Map Text Object)+transformObjectMap (ObjectMap kv) = let mkPair :: Monad m => (Object, Object) -> m (Text, Object)+                                        mkPair ((ObjectStr txt), v) = pure (txt, v)+                                        mkPair _ = fail "Non-string key"+                                    in  fromList <$> sequence (map mkPair kv)+transformObjectMap _ = fail "Wrong MessagePack MMTF format"++atP :: Monad m => Map Text Object -> Text -> (Object -> m a) -> m a+atP m k conv =+  case M.lookup k m of+    Just x  -> conv x+    Nothing -> fail $ "Required field '" ++ uk ++ "' was not found"+  where uk = T.unpack k++atPM :: Monad m => Map Text Object -> Text -> (Object -> m a) -> m (Maybe a)+atPM m k conv = traverse conv $ M.lookup k m++asStr :: Monad m => Object -> m Text+asStr (ObjectStr s) = pure s+asStr _             = fail "Not a string data"++asChar :: Monad m => Object -> m Char+asChar = (head . T.unpack <$>) . asStr++asInt :: (Monad m, Integral a) => Object -> m a+asInt (ObjectInt i)  = pure (fromIntegral i)+asInt (ObjectWord w) = pure (fromIntegral w)+asInt _              = fail "Not an int data"++asFloat :: Monad m => Object -> m Float+asFloat (ObjectFloat f) = pure f+asFloat _               = fail "Not a float data"++asIntList :: (Monad m, Integral a) => Object -> m [a]+asIntList (ObjectArray l) = sequence $ map asInt l+asIntList _               = fail "Not an array of ints data"++asStrList :: Monad m => Object -> m [Text]+asStrList (ObjectArray l) = sequence $ map asStr l+asStrList _               = fail "Not an array of string data"++asFloatList :: Monad m => Object -> m [Float]+asFloatList (ObjectArray l) = sequence $ map asFloat l+asFloatList _               = fail "Not an array of float data"++asObjectList :: Monad m => Object -> m [Object]+asObjectList (ObjectArray l) = pure l+asObjectList _               = fail "Not an array data"++asBinary :: Monad m => Object -> m ByteString+asBinary (ObjectBin bs) = pure (fromStrict bs)+asBinary _              = fail "Not a binary data"
+ src/Bio/MMTF/MessagePack.hs view
@@ -0,0 +1,20 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Bio.MMTF.MessagePack where++import           Bio.MMTF.Decode+import           Bio.MMTF.Decode.MessagePack+import           Bio.MMTF.Type++import           Data.MessagePack            (MessagePack (..))++instance MessagePack MMTF where+  toObject = undefined -- TODO: add MMTF encoding+  fromObject obj = do mp <- transformObjectMap obj+                      f <- formatData mp+                      s <- structureData mp+                      m <- modelData mp+                      c <- chainData mp+                      g <- groupData mp+                      a <- atomData mp+                      pure $ MMTF f s m c g a
+ src/Bio/MMTF/Type.hs view
@@ -0,0 +1,117 @@+module Bio.MMTF.Type where++import           Data.Int  (Int32, Int8)+import           Data.Text (Text)++data UnitCell = UnitCell { ucA     :: !Float+                         , ucB     :: !Float+                         , ucC     :: !Float+                         , ucAlpha :: !Float+                         , ucBeta  :: !Float+                         , ucGamma :: !Float+                         }+  deriving (Show, Eq)++data Transform = Transform { chainIndexList :: ![Int32]+                           , matrix         :: ![Float]+                           }+  deriving (Show, Eq)++data Assembly = Assembly { transformList :: ![Transform]+                         , assemblyName  :: !Text+                         }+  deriving (Show, Eq)++data Entity = Entity { entityChainIndexList :: ![Int32]+                     , entityDescription    :: !Text+                     , entityType           :: !Text+                     , entitySequence       :: !Text+                     }+  deriving (Show, Eq)++data GroupType = GroupType { gtFormalChargeList :: ![Int32]+                           , gtAtomNameList     :: ![Text]+                           , gtElementList      :: ![Text]+                           , gtBondAtomList     :: ![Int32]+                           , gtBondOrderList    :: ![Int32]+                           , gtGroupName        :: !Text+                           , gtSingleLetterCode :: !Char+                           , gtChemCompType     :: !Text+                           }+  deriving (Show, Eq)++data SecondaryStructure = PiHelix       -- 0+                        | Bend          -- 1+                        | AlphaHelix    -- 2+                        | Extended      -- 3+                        | ThreeTenHelix -- 4+                        | Bridge        -- 5+                        | Turn          -- 6+                        | Coil          -- 7+                        | Undefined     -- -1+  deriving (Show, Eq)++data FormatData = FormatData { mmtfVersion  :: !Text+                             , mmtfProducer :: !Text+                             }+  deriving (Show, Eq)++data StructureData = StructureData { title               :: !(Maybe Text)+                                   , structureId         :: !(Maybe Text)+                                   , depositionDate      :: !(Maybe Text)+                                   , releaseDate         :: !(Maybe Text)+                                   , numBonds            :: !Int32+                                   , numAtoms            :: !Int32+                                   , numGroups           :: !Int32+                                   , numChains           :: !Int32+                                   , numModels           :: !Int32+                                   , spaceGroup          :: !(Maybe Text)+                                   , unitCell            :: !(Maybe UnitCell)+                                   , ncsOperatorList     :: !(Maybe [[Float]])+                                   , bioAssemblyList     :: !(Maybe [Assembly])+                                   , entityList          :: !(Maybe [Entity])+                                   , resolution          :: !(Maybe Float)+                                   , rFree               :: !(Maybe Float)+                                   , rWork               :: !(Maybe Float)+                                   , experimentalMethods :: !(Maybe [Text])+                                   , bondAtomList        :: !(Maybe [Int32]) -- binary (type 4)+                                   , bondOrderList       :: !(Maybe [Int8])  -- binary (type 2)+                                   }+  deriving (Show, Eq)++data ModelData = ModelData { chainsPerModel :: ![Int32] }+  deriving (Show, Eq)++data ChainData = ChainData { groupsPerChain :: ![Int32]+                           , chainIdList    :: ![Text] -- binary (type 5, length 4)+                           , chainNameList  :: !(Maybe [Text]) -- binary (type 5, length 4)+                           }+  deriving (Show, Eq)++data GroupData = GroupData { groupList         :: ![GroupType]+                           , groupTypeList     :: ![Int32] -- binary (type 4)+                           , groupIdList       :: ![Int32] -- binary (type 8)+                           , secStructList     :: !(Maybe [SecondaryStructure]) -- binary (type 2)+                           , insCodeList       :: !(Maybe [Char]) -- binary (type 6)+                           , sequenceIndexList :: !(Maybe [Int32]) -- binary (type 8)+                           }+  deriving (Show, Eq)++data AtomData = AtomData { atomIdList    :: !(Maybe [Int32]) -- binary (type 8)+                         , altLocList    :: !(Maybe [Char]) -- binary (type 6)+                         , bFactorList   :: !(Maybe [Float]) -- binary (type 10)+                         , xCoordList    :: ![Float] -- binary (type 10)+                         , yCoordList    :: ![Float] -- binary (type 10)+                         , zCoordList    :: ![Float] -- binary (type 10)+                         , occupancyList :: !(Maybe [Float]) -- binary (type 9)+                         }+  deriving (Show, Eq)++data MMTF = MMTF { format    :: !FormatData+                 , structure :: !StructureData+                 , model     :: !ModelData+                 , chain     :: !ChainData+                 , group     :: !GroupData+                 , atom      :: !AtomData+                 }+  deriving (Show, Eq)
+ test/Spec.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE OverloadedStrings #-}++import           Bio.MMTF+import qualified Data.ByteString.Lazy as B+import           Test.Hspec++main :: IO ()+main = hspec $+  describe "MMTF" $+    it "should parse 1FSD" $ do+      contents <- B.readFile "resource/1FSD.mmtf"+      m <- decode contents+      let sid = (structureId . structure) m+      sid `shouldBe` Just "1FSD"