diff --git a/mmtf.cabal b/mmtf.cabal
--- a/mmtf.cabal
+++ b/mmtf.cabal
@@ -1,5 +1,5 @@
 name:                mmtf
-version:             0.1.2.0
+version:             0.1.3.0
 synopsis:            Macromolecular Transmission Format implementation
 description:         Haskell implementation of MMTF biological structure format.
 homepage:            https://github.com/zmactep/mmtf#readme
@@ -7,7 +7,7 @@
 license-file:        LICENSE
 author:              Pavel Yakovlev
 maintainer:          pavel@yakovlev.me
-copyright:           (c) 2017, Pavel Yakovlev
+copyright:           (c) 2017—2019, Pavel Yakovlev
 category:            Bioinformatics
 build-type:          Simple
 extra-source-files:  README.md
@@ -16,6 +16,7 @@
 library
   hs-source-dirs:      src
   exposed-modules:     Bio.MMTF
+                     , Bio.MMTF.Structure
   other-modules:       Bio.MMTF.Type
                      , Bio.MMTF.MessagePack
                      , Bio.MMTF.Decode
@@ -28,6 +29,8 @@
                      , binary >= 0.8.3.0 && < 1.0
                      , containers >= 0.5.7.1 && < 0.7
                      , http-conduit >= 2.3 && < 2.4
+                     , array >= 0.5 && < 0.6
+                     , deepseq >= 1.4 && < 1.5
   default-language:    Haskell2010
   ghc-options:         -Wall
 
@@ -52,6 +55,7 @@
                      , binary
                      , containers
                      , http-conduit
+                     , array
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
   default-language:    Haskell2010
 
diff --git a/src/Bio/MMTF/Decode.hs b/src/Bio/MMTF/Decode.hs
--- a/src/Bio/MMTF/Decode.hs
+++ b/src/Bio/MMTF/Decode.hs
@@ -10,19 +10,21 @@
 import           Data.ByteString.Lazy        (empty)
 import           Data.Map.Strict             (Map)
 import           Data.MessagePack            (Object)
-import           Data.Text                   (Text)
+import           Data.Text                   (Text, pack)
+import           Data.Char                   (ord)
+import           Data.Array                  (listArray)
 
 -- | Parses format data from ObjectMap
 --
 formatData :: Monad m => Map Text Object -> m FormatData
-formatData mp = do v <- atP mp "mmtfVersion" asStr
+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 = ModelData <$> atP mp "chainsPerModel" asIntList
+modelData mp = ModelData . l2a <$> atP mp "chainsPerModel" asIntList
 
 -- | Parses chain data from ObjectMap
 --
@@ -30,83 +32,85 @@
 chainData mp = do gpc <- atP mp "groupsPerChain" asIntList
                   cil <- codec5 . parseBinary <$> atP   mp "chainIdList"   asBinary
                   cnl <- codec5 . parseBinary <$> atPMD mp "chainNameList" asBinary empty
-                  pure $ ChainData gpc cil cnl
+                  pure $ ChainData (l2a gpc) (l2a cil) (l2a cnl)
 
 -- | Parses atom data from ObjectMap
 --
 atomData :: Monad m => Map Text Object -> m AtomData
-atomData mp = do ail' <-  codec8 . parseBinary <$> atPMD mp "atomIdList"    asBinary empty
-                 all' <-  codec6 . parseBinary <$> atPMD mp "altLocList"    asBinary empty
-                 bfl' <- codec10 . parseBinary <$> atPMD mp "bFactorList"   asBinary empty
-                 xcl' <- codec10 . parseBinary <$> atP   mp "xCoordList"    asBinary
-                 ycl' <- codec10 . parseBinary <$> atP   mp "yCoordList"    asBinary
-                 zcl' <- codec10 . parseBinary <$> atP   mp "zCoordList"    asBinary
-                 ol' <-   codec9 . parseBinary <$> atPMD mp "occupancyList" asBinary empty
-                 pure $ AtomData ail' all' bfl' xcl' ycl' zcl' ol'
+atomData mp = do ail' <-       codec8 . parseBinary <$> atPMD mp "atomIdList"    asBinary empty
+                 all' <- c2s . codec6 . parseBinary <$> atPMD mp "altLocList"    asBinary empty
+                 bfl' <-      codec10 . parseBinary <$> atPMD mp "bFactorList"   asBinary empty
+                 xcl' <-      codec10 . parseBinary <$> atP   mp "xCoordList"    asBinary
+                 ycl' <-      codec10 . parseBinary <$> atP   mp "yCoordList"    asBinary
+                 zcl' <-      codec10 . parseBinary <$> atP   mp "zCoordList"    asBinary
+                 ol' <-        codec9 . parseBinary <$> atPMD mp "occupancyList" asBinary empty
+                 pure $ AtomData (l2a ail') (l2a all') (l2a bfl') (l2a xcl') (l2a ycl') (l2a zcl') (l2a ol')
 
 -- | Parses group data from ObjectMap
 --
 groupData :: Monad m => Map Text Object -> m GroupData
-groupData mp = do gl' <- atP mp "groupList" asObjectList >>= traverse (transformObjectMap >=> groupType)
+groupData mp = do gl' <-                                        atP   mp "groupList"          asObjectList >>= traverse (transformObjectMap >=> groupType)
                   gtl' <-              codec4 . parseBinary <$> atP   mp "groupTypeList"     asBinary
                   gil' <-              codec8 . parseBinary <$> atP   mp "groupIdList"       asBinary
                   ssl' <- fmap ssDec . codec2 . parseBinary <$> atPMD mp "secStructList"     asBinary empty
-                  icl' <-              codec6 . parseBinary <$> atPMD mp "insCodeList"       asBinary empty
+                  icl' <-        c2s . codec6 . parseBinary <$> atPMD mp "insCodeList"       asBinary empty
                   sil' <-              codec8 . parseBinary <$> atPMD mp "sequenceIndexList" asBinary empty
-                  pure $ GroupData gl' gtl' gil' ssl' icl' sil'
+                  pure $ GroupData (l2a gl') (l2a gtl') (l2a gil') (l2a ssl') (l2a icl') (l2a 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'
+groupType mp = do fcl' <-          atP mp "formalChargeList" asIntList
+                  anl' <-          atP mp "atomNameList"     asStrList
+                  el'  <-          atP mp "elementList"      asStrList
+                  bal' <- l2pl <$> 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 (l2a fcl') (l2a anl') (l2a el') (l2a bal') (l2a bol') gn' slc' cct'
 
 -- | Parses structure data from ObjectMap
 --
 structureData :: Monad m => Map Text Object -> m StructureData
-structureData mp = do ttl' <-                          atPMD mp "title"               asStr        ""
-                      sid' <-                          atPMD mp "structureId"         asStr        ""
-                      dd'  <-                          atPMD mp "depositionDate"      asStr        ""
-                      rd'  <-                          atPMD 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'  <-                          atPMD mp "spaceGroup"          asStr        ""
-                      uc'  <-          (>>= ucDec) <$> atPM  mp "unitCell"            asFloatList
-                      nol' <-    (>>= asFloatList) <$> atPMD mp "ncsOperatorList"     asObjectList []
-                      bal' <-                          atPMD mp "bioAssemblyList"     asObjectList [] >>= traverse (transformObjectMap >=> bioAssembly)
-                      el'  <-                          atPMD mp "entityList"          asObjectList [] >>= traverse (transformObjectMap >=> entity)
-                      res' <-                          atPM  mp "resolution"          asFloat
-                      rf'  <-                          atPM  mp "rFree"               asFloat
-                      rw'  <-                          atPM  mp "rWork"               asFloat
-                      em'  <-                          atPMD mp "experimentalMethods" asStrList []
-                      btl' <- codec4 . parseBinary <$> atPMD mp "bondAtomList"        asBinary empty
-                      bol' <- codec2 . parseBinary <$> atPMD mp "bondOrderList"       asBinary empty
-                      pure $ StructureData ttl' sid' dd' rd' nb' na' ng' nc' nm' sg' uc' nol'
-                                           bal' el' res' rf' rw' em' btl' bol'
+structureData mp = do ttl' <-                                  atPMD mp "title"               asStr        ""
+                      sid' <-                                  atPMD mp "structureId"         asStr        ""
+                      dd'  <-                                  atPMD mp "depositionDate"      asStr        ""
+                      rd'  <-                                  atPMD 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'  <-                                  atPMD mp "spaceGroup"          asStr        ""
+                      uc'  <-                  (>>= ucDec) <$> atPM  mp "unitCell"            asFloatList
+                      nol' <-                       m44Dec <$> atPMD mp "ncsOperatorList"     asFloatList []
+                      bal' <-                                  atPMD mp "bioAssemblyList"     asObjectList [] >>= traverse (transformObjectMap >=> bioAssembly)
+                      el'  <-                                  atPMD mp "entityList"          asObjectList [] >>= traverse (transformObjectMap >=> entity)
+                      res' <-                                  atPM  mp "resolution"          asFloat
+                      rf'  <-                                  atPM  mp "rFree"               asFloat
+                      rw'  <-                                  atPM  mp "rWork"               asFloat
+                      em'  <-                                  atPMD mp "experimentalMethods" asStrList []
+                      btl' <-  l2pl . codec4 . parseBinary <$> atPMD mp "bondAtomList"        asBinary empty
+                      bol' <-         codec2 . parseBinary <$> atPMD mp "bondOrderList"       asBinary empty
+                      pure $ StructureData ttl' sid' dd' rd' nb' na'
+                                          ng' nc' nm' sg' uc' (l2a nol')
+                                           (l2a bal') (l2a el') res' rf'
+                                           rw' (l2a em') (l2a btl') (l2a 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 >>= traverse (transformObjectMap >=> transform)
-                    pure $ Assembly tlt' nme'
+                    pure $ Assembly (l2a 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'
+                  mtx' <- atP mp "matrix"         asFloatList >>= m44Dec
+                  pure $ Transform (l2a cil') mtx'
 
 -- | Parses entity data from ObjectMap
 --
@@ -115,4 +119,23 @@
                dsc' <- atP mp "description"    asStr
                tpe' <- atP mp "type"           asStr
                sqc' <- atP mp "sequence"       asStr
-               pure $ Entity cil' dsc' tpe' sqc'
+               pure $ Entity (l2a cil') dsc' tpe' sqc'
+
+-- | Converts list of chars to list of one-sized
+-- (or zero-sized in case of zero) strings
+c2s :: [Char] -> [Text]
+c2s [] = []
+c2s (x:xs) | ord x == 0 = "":c2s xs
+           | otherwise  = (pack [x]):c2s xs
+
+-- | Converst list to an array
+--
+l2a :: [a] -> IArray a
+l2a lst = listArray (0, length lst - 1) lst
+
+-- | List to list of pairs
+--
+l2pl :: [a] -> [(a, a)]
+l2pl []       = []
+l2pl (x:y:xs) = (x,y) : l2pl xs
+l2pl _        = error "Cannot convert a list of odd length to a list of pairs"
diff --git a/src/Bio/MMTF/Decode/Codec.hs b/src/Bio/MMTF/Decode/Codec.hs
--- a/src/Bio/MMTF/Decode/Codec.hs
+++ b/src/Bio/MMTF/Decode/Codec.hs
@@ -23,6 +23,7 @@
                      , binaryParam  :: !Int32
                      , binaryData   :: !ByteString
                      }
+  deriving Show
 
 -- | Parse useless header for binary data
 --
@@ -162,3 +163,10 @@
 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"
+
+m44Dec :: Monad m => [Float] -> m M44
+m44Dec [ a11, a12, a13, a14
+       , a21, a22, a23, a24
+       , a31, a32, a33, a34
+       , a41, a42, a43, a44] = pure $ M44 a11 a12 a13 a14 a21 a22 a23 a24 a31 a32 a33 a34 a41 a42 a43 a44
+m44Dec _                     = fail "Wrong list format for 4x4 transformation matrix"
diff --git a/src/Bio/MMTF/Decode/MessagePack.hs b/src/Bio/MMTF/Decode/MessagePack.hs
--- a/src/Bio/MMTF/Decode/MessagePack.hs
+++ b/src/Bio/MMTF/Decode/MessagePack.hs
@@ -4,6 +4,7 @@
 import           Data.Map.Strict       (Map, fromList)
 import qualified Data.Map.Strict       as M (lookup)
 import           Data.MessagePack
+import           Data.Monoid           ((<>))
 import           Data.Text             (Text)
 import qualified Data.Text             as T (unpack)
 
@@ -14,54 +15,55 @@
                                     in  fromList <$> traverse mkPair kv
 transformObjectMap _ = fail "Wrong MessagePack MMTF format"
 
-atP :: Monad m => Map Text Object -> Text -> (Object -> m a) -> m a
+atP :: Monad m => Map Text Object -> Text -> (Text -> Object -> m a) -> m a
 atP m k conv =
   case M.lookup k m of
-    Just x  -> conv x
+    Just x  -> conv k 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
+atPM :: Monad m => Map Text Object -> Text -> (Text -> Object -> m a) -> m (Maybe a)
+atPM m k conv = traverse (conv k) $ M.lookup k m
 
-atPMD :: Monad m => Map Text Object -> Text -> (Object -> m a) -> a -> m a
+atPMD :: Monad m => Map Text Object -> Text -> (Text -> Object -> m a) -> a -> m a
 atPMD m k conv def = do x <- atPM m k conv
                         case x of
                           Just r  -> pure r
                           Nothing -> pure def
                        
-asStr :: Monad m => Object -> m Text
-asStr (ObjectStr s) = pure s
-asStr _             = fail "Not a string data"
+asStr :: Monad m => Text -> Object -> m Text
+asStr _ (ObjectStr s) = pure s
+asStr m _             = fail $ T.unpack m <> ": not a string data"
 
-asChar :: Monad m => Object -> m Char
-asChar = (head . T.unpack <$>) . asStr
+asChar :: Monad m => Text -> Object -> m Char
+asChar m = (head . T.unpack <$>) . asStr m
 
-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"
+asInt :: (Monad m, Integral a) => Text -> Object -> m a
+asInt _ (ObjectInt i)  = pure (fromIntegral i)
+asInt _ (ObjectWord w) = pure (fromIntegral w)
+asInt m _              = fail $ T.unpack m <> ": not an int data"
 
-asFloat :: Monad m => Object -> m Float
-asFloat (ObjectFloat f) = pure f
-asFloat _               = fail "Not a float data"
+asFloat :: Monad m => Text -> Object -> m Float
+asFloat _ (ObjectFloat  f) = pure f
+asFloat _ (ObjectDouble f) = pure (realToFrac f)
+asFloat m _                = fail $ T.unpack m <> ": not a float data"
 
-asIntList :: (Monad m, Integral a) => Object -> m [a]
-asIntList (ObjectArray l) = traverse asInt l
-asIntList _               = fail "Not an array of ints data"
+asIntList :: (Monad m, Integral a) => Text -> Object -> m [a]
+asIntList m (ObjectArray l) = traverse (asInt m) l
+asIntList m _               = fail $ T.unpack m <> ": not an array of ints data"
 
-asStrList :: Monad m => Object -> m [Text]
-asStrList (ObjectArray l) = traverse asStr l
-asStrList _               = fail "Not an array of string data"
+asStrList :: Monad m => Text -> Object -> m [Text]
+asStrList m (ObjectArray l) = traverse (asStr m) l
+asStrList m _               = fail $ T.unpack m <> ": not an array of string data"
 
-asFloatList :: Monad m => Object -> m [Float]
-asFloatList (ObjectArray l) = traverse asFloat l
-asFloatList _               = fail "Not an array of float data"
+asFloatList :: Monad m => Text -> Object -> m [Float]
+asFloatList m (ObjectArray l) = traverse (asFloat m) l
+asFloatList m _               = fail $ T.unpack m <> ": not an array of float data"
 
-asObjectList :: Monad m => Object -> m [Object]
-asObjectList (ObjectArray l) = pure l
-asObjectList _               = fail "Not an array data"
+asObjectList :: Monad m => Text -> Object -> m [Object]
+asObjectList _ (ObjectArray l) = pure l
+asObjectList m _               = fail $ T.unpack m <> ": not an array data"
 
-asBinary :: Monad m => Object -> m ByteString
-asBinary (ObjectBin bs) = pure (fromStrict bs)
-asBinary _              = fail "Not a binary data"
+asBinary :: Monad m => Text -> Object -> m ByteString
+asBinary _ (ObjectBin bs) = pure (fromStrict bs)
+asBinary m _              = fail $ T.unpack m <> ": not a binary data"
diff --git a/src/Bio/MMTF/Structure.hs b/src/Bio/MMTF/Structure.hs
new file mode 100644
--- /dev/null
+++ b/src/Bio/MMTF/Structure.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE DeriveGeneric #-}
+module Bio.MMTF.Structure where
+
+import           Data.Array      ( Array, (!), elems )
+import           Data.List       ( mapAccumL, zip3, zip4 )
+import           Data.Text       ( Text )
+import           Data.Int        ( Int32 )
+import           Data.Bifunctor  ( Bifunctor (..) )
+import           GHC.Generics    ( Generic )
+import           Control.DeepSeq ( NFData (..) )
+
+import Bio.MMTF.Decode (l2a)
+import Bio.MMTF
+
+data Atom = Atom { atomName     :: Text
+                 , atomElement  :: Text
+                 , atomCoords   :: (Float, Float, Float)
+                 , formalCharge :: Int
+                 , bFactor      :: Float
+                 , occupancy    :: Float
+                 }
+  deriving (Show, Eq, Generic)
+
+instance NFData Atom
+
+data Bond = Bond { bondStart :: Int
+                 , bondEnd   :: Int
+                 , bondOrder :: Int
+                 }
+  deriving (Show, Eq, Generic)
+
+instance NFData Bond
+
+data Residue = Residue { resName      :: Text
+                       , resAtoms     :: Array Int Atom
+                       , resBonds     :: Array Int Bond
+                       , resSecondary :: SecondaryStructure
+                       }
+  deriving (Show, Eq, Generic)
+
+instance NFData Residue
+
+data Chain = Chain { chainName     :: Text
+                   , chainResidues :: Array Int Residue
+                   }
+  deriving (Show, Eq, Generic)
+
+instance NFData Chain
+
+newtype Model = Model { modelChains :: Array Int Chain }
+  deriving (Show, Eq, Generic)
+
+instance NFData Model
+
+modelsOf :: MMTF -> Array Int Model
+modelsOf m = l2a (Model . l2a <$> zipWith (zipWith Chain) chainNames chainResis)
+  where
+    chainsCnts = fromIntegral <$> elems (chainsPerModel (model m))
+    groupsCnts = fromIntegral <$> elems (groupsPerChain (chain m))
+    groupsRaws = snd $ mapAccumL getGroups (0, 0) groupsCnts
+    groups     = cutter chainsCnts groupsRaws
+    chainNames = cutter chainsCnts (elems $ chainNameList $ chain m)
+    chainResis = fmap (fmap (l2a . (fmap mkResidue))) groups
+
+    getGroups :: (Int, Int) -> Int -> ((Int, Int), [(GroupType, SecondaryStructure, [Atom])])
+    getGroups (chOffset, atOffset) sz = let chEnd        = chOffset + sz
+                                            gtl          = groupTypeList (group m)
+                                            gl           = groupList (group m)
+                                            ssl          = secStructList (group m)
+                                            chr          = [chOffset .. chEnd - 1]
+                                            rgt          = (gl !) . fromIntegral . (gtl !) <$> chr
+                                            rss          = (ssl !) <$> chr
+                                            (atEnd, ats) = mapAccumL getAtoms atOffset rgt
+                                        in  ((chEnd, atEnd), zip3 rgt rss ats)
+
+    getAtoms :: Int -> GroupType -> (Int, [Atom])
+    getAtoms offset gt = let cl  = fmap fromIntegral . elems . gtFormalChargeList $ gt
+                             nl  = elems . gtAtomNameList $ gt
+                             el  = elems . gtElementList $ gt
+                             ics = [offset .. end - 1]
+                             end = offset + length cl
+                         in  (end, mkAtom <$> zip4 cl nl el ics)
+
+    mkResidue :: (GroupType, SecondaryStructure, [Atom]) -> Residue
+    mkResidue (gt, ss, atoms) = Residue (gtGroupName gt) (l2a atoms) (mkBonds (gtBondAtomList gt) (gtBondOrderList gt)) ss
+
+    mkBonds :: Array Int (Int32, Int32) -> Array Int Int32 -> Array Int Bond
+    mkBonds bal bol = let ball = bimap fromIntegral fromIntegral <$> elems bal
+                          boll = fromIntegral <$> elems bol
+                          res  = zipWith (\(f, t) o -> Bond f t o) ball boll
+                      in  l2a res
+
+    mkAtom :: (Int, Text, Text, Int) -> Atom
+    mkAtom (fc, n, e, idx) = let x = xCoordList (atom m)
+                                 y = yCoordList (atom m)
+                                 z = zCoordList (atom m)
+                                 o = occupancyList (atom m)
+                                 b = bFactorList (atom m)
+                             in  Atom n e (x ! idx, y ! idx, z ! idx) fc (b ! idx) (o ! idx)
+
+    cutter :: [Int] -> [a] -> [[a]]
+    cutter []     []    = []
+    cutter (x:xs) ys    = take x ys : cutter xs (drop x ys)
+    cutter []     (_:_) = error "Cutter: you cannot be here"
diff --git a/src/Bio/MMTF/Type.hs b/src/Bio/MMTF/Type.hs
--- a/src/Bio/MMTF/Type.hs
+++ b/src/Bio/MMTF/Type.hs
@@ -1,8 +1,26 @@
+{-# LANGUAGE DeriveGeneric #-}
 module Bio.MMTF.Type where
 
-import           Data.Int  (Int32, Int8)
-import           Data.Text (Text)
+import           Data.Int        ( Int32, Int8 )
+import           Data.Text       ( Text )
+import           Data.Array      ( Array )
+import           GHC.Generics    ( Generic )
+import           Control.DeepSeq ( NFData (..) )
 
+-- | All arrays are int-indexed
+--
+type IArray a = Array Int a
+
+-- | Transformation matrix
+--
+data M44 = M44 Float Float Float Float
+               Float Float Float Float
+               Float Float Float Float
+               Float Float Float Float
+  deriving (Show, Eq, Generic)
+
+instance NFData M44
+
 -- | Unit cell data
 --
 data UnitCell = UnitCell { ucA     :: !Float -- ^ length of side 'a'
@@ -12,44 +30,54 @@
                          , ucBeta  :: !Float -- ^ beta angle in degrees
                          , ucGamma :: !Float -- ^ gamma angle in degrees
                          }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData UnitCell
+
 -- | Transform data
 --
-data Transform = Transform { chainIndexList :: ![Int32] -- ^ indices into the 'chainIdList' and 'chainNameList' fields
-                           , matrix         :: ![Float] -- ^ 4x4 transformation matrix
+data Transform = Transform { chainIndexList :: !(IArray Int32) -- ^ indices into the 'chainIdList' and 'chainNameList' fields
+                           , matrix         :: !M44            -- ^ 4x4 transformation matrix
                            }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData Transform
+
 -- | Assembly data
 --
-data Assembly = Assembly { transformList :: ![Transform] -- ^ List of transform objects
-                         , assemblyName  :: !Text        -- ^ Name of the biological assembly
+data Assembly = Assembly { transformList :: !(IArray Transform) -- ^ List of transform objects
+                         , assemblyName  :: !Text               -- ^ Name of the biological assembly
                          }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData Assembly
+
 -- | Entity data
 --
-data Entity = Entity { entityChainIndexList :: ![Int32] -- ^ indices into the 'chainIdList' and 'chainNameList' fields
-                     , entityDescription    :: !Text    -- ^ Description of the entity
-                     , entityType           :: !Text    -- ^ Name of the entity type
-                     , entitySequence       :: !Text    -- ^ Sequence of the full construct in one-letter-code
+data Entity = Entity { entityChainIndexList :: !(IArray Int32) -- ^ indices into the 'chainIdList' and 'chainNameList' fields
+                     , entityDescription    :: !Text           -- ^ Description of the entity
+                     , entityType           :: !Text           -- ^ Name of the entity type
+                     , entitySequence       :: !Text           -- ^ Sequence of the full construct in one-letter-code
                      }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData Entity
+
 -- | Group type data
 --
-data GroupType = GroupType { gtFormalChargeList :: ![Int32] -- ^ List of formal charges
-                           , gtAtomNameList     :: ![Text]  -- ^ List of atom names
-                           , gtElementList      :: ![Text]  -- ^ List of elements
-                           , gtBondAtomList     :: ![Int32] -- ^ List of bonded atom indices
-                           , gtBondOrderList    :: ![Int32] -- ^ List of bond orders
-                           , gtGroupName        :: !Text    -- ^ The name of the group
-                           , gtSingleLetterCode :: !Char    -- ^ The single letter code
-                           , gtChemCompType     :: !Text    -- ^ The chemical component type
+data GroupType = GroupType { gtFormalChargeList :: !(IArray Int32)          -- ^ List of formal charges
+                           , gtAtomNameList     :: !(IArray Text)           -- ^ List of atom names
+                           , gtElementList      :: !(IArray Text)           -- ^ List of elements
+                           , gtBondAtomList     :: !(IArray (Int32, Int32)) -- ^ List of bonded atom indices
+                           , gtBondOrderList    :: !(IArray Int32)          -- ^ List of bond orders
+                           , gtGroupName        :: !Text                    -- ^ The name of the group
+                           , gtSingleLetterCode :: !Char                    -- ^ The single letter code
+                           , gtChemCompType     :: !Text                    -- ^ The chemical component type
                            }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData GroupType
+
 -- | Protein secondary structure
 --
 data SecondaryStructure = PiHelix       -- ^ pi helix
@@ -61,77 +89,91 @@
                         | Turn          -- ^ turn
                         | Coil          -- ^ coil
                         | Undefined     -- ^ unknown structure
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData SecondaryStructure
+
 -- | MMTF format data
 --
 data FormatData = FormatData { mmtfVersion  :: !Text -- ^ The version number of the specification the file adheres to
                              , mmtfProducer :: !Text -- ^ The name and version of the software used to produce the file
                              }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData FormatData
+
 -- | Structure data
 --
-data StructureData = StructureData { title               :: !Text               -- ^ A short description of the structural data included in the file
-                                   , structureId         :: !Text               -- ^ An ID for the structure, for example the PDB ID if applicable
-                                   , depositionDate      :: !Text               -- ^ A date that relates to the deposition of the structure in a database
-                                   , releaseDate         :: !Text               -- ^ A date that relates to the release of the structure in a database
-                                   , numBonds            :: !Int32              -- ^ The overall number of bonds
-                                   , numAtoms            :: !Int32              -- ^ The overall number of atoms in the structure
-                                   , numGroups           :: !Int32              -- ^ The overall number of groups in the structure
-                                   , numChains           :: !Int32              -- ^ The overall number of chains in the structure
-                                   , numModels           :: !Int32              -- ^ The overall number of models in the structure
-                                   , spaceGroup          :: !Text               -- ^ The Hermann-Mauguin space-group symbol
-                                   , unitCell            :: !(Maybe UnitCell)   -- ^ Array of six values defining the unit cell
-                                   , ncsOperatorList     :: ![[Float]]          -- ^ List of lists representing 4x4 transformation matrices that are stored linearly in row major order (transformation matrices describe noncrystallographic symmetry operations needed to create all molecules in the unit cell)
-                                   , bioAssemblyList     :: ![Assembly]         -- ^ List of instructions on how to transform coordinates for an array of chains to create (biological) assemblies
-                                   , entityList          :: ![Entity]           -- ^ List of unique molecular entities within the structure
-                                   , resolution          :: !(Maybe Float)      -- ^ The experimental resolution in Angstrom
-                                   , rFree               :: !(Maybe Float)      -- ^ The R-free value
-                                   , rWork               :: !(Maybe Float)      -- ^ The R-work value
-                                   , experimentalMethods :: ![Text]             -- ^ List of experimental methods employed for structure determination
-                                   , bondAtomList        :: ![Int32]            -- ^ Pairs of values represent indices of covalently bonded atoms [binary (type 4)]
-                                   , bondOrderList       :: ![Int8]             -- ^ List of bond orders for bonds in 'bondAtomList' [binary (type 2)]
+data StructureData = StructureData { title               :: !Text                    -- ^ A short description of the structural data included in the file
+                                   , structureId         :: !Text                    -- ^ An ID for the structure, for example the PDB ID if applicable
+                                   , depositionDate      :: !Text                    -- ^ A date that relates to the deposition of the structure in a database
+                                   , releaseDate         :: !Text                    -- ^ A date that relates to the release of the structure in a database
+                                   , numBonds            :: !Int32                   -- ^ The overall number of bonds
+                                   , numAtoms            :: !Int32                   -- ^ The overall number of atoms in the structure
+                                   , numGroups           :: !Int32                   -- ^ The overall number of groups in the structure
+                                   , numChains           :: !Int32                   -- ^ The overall number of chains in the structure
+                                   , numModels           :: !Int32                   -- ^ The overall number of models in the structure
+                                   , spaceGroup          :: !Text                    -- ^ The Hermann-Mauguin space-group symbol
+                                   , unitCell            :: !(Maybe UnitCell)        -- ^ Array of six values defining the unit cell
+                                   , ncsOperatorList     :: !(IArray M44)            -- ^ List of 4x4 transformation matrices (transformation matrices describe noncrystallographic symmetry operations needed to create all molecules in the unit cell)
+                                   , bioAssemblyList     :: !(IArray Assembly)       -- ^ List of instructions on how to transform coordinates for an array of chains to create (biological) assemblies
+                                   , entityList          :: !(IArray Entity)         -- ^ List of unique molecular entities within the structure
+                                   , resolution          :: !(Maybe Float)           -- ^ The experimental resolution in Angstrom
+                                   , rFree               :: !(Maybe Float)           -- ^ The R-free value
+                                   , rWork               :: !(Maybe Float)           -- ^ The R-work value
+                                   , experimentalMethods :: !(IArray Text)           -- ^ List of experimental methods employed for structure determination
+                                   , bondAtomList        :: !(IArray (Int32, Int32)) -- ^ Pairs of values represent indices of covalently bonded atoms [binary (type 4)]
+                                   , bondOrderList       :: !(IArray Int8)           -- ^ List of bond orders for bonds in 'bondAtomList' [binary (type 2)]
                                    }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData StructureData
+
 -- | Models data
 --
-data ModelData = ModelData { chainsPerModel :: ![Int32] -- ^ List of the number of chains in each model
+data ModelData = ModelData { chainsPerModel :: !(IArray Int32) -- ^ List of the number of chains in each model
                            }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData ModelData
+
 -- | Chains data
 --
-data ChainData = ChainData { groupsPerChain :: ![Int32]        -- ^ List of the number of groups (aka residues) in each chain
-                           , chainIdList    :: ![Text]         -- ^ List of chain IDs [binary (type 5)]
-                           , chainNameList  :: ![Text]         -- ^ List of chain names [binary (type 5)]
+data ChainData = ChainData { groupsPerChain :: !(IArray Int32)       -- ^ List of the number of groups (aka residues) in each chain
+                           , chainIdList    :: !(IArray Text)        -- ^ List of chain IDs [binary (type 5)]
+                           , chainNameList  :: !(IArray Text)        -- ^ List of chain names [binary (type 5)]
                            }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData ChainData
+
 -- | Groups data
 --
-data GroupData = GroupData { groupList         :: ![GroupType]                  -- ^ List of groupType objects
-                           , groupTypeList     :: ![Int32]                      -- ^ List of pointers to 'groupType' entries in 'groupList' by their keys [binary (type 4)]
-                           , groupIdList       :: ![Int32]                      -- ^ List of group (residue) numbers [binary (type 8)]
-                           , secStructList     :: ![SecondaryStructure]         -- ^ List of secondary structure assignments [binary (type 2)]
-                           , insCodeList       :: ![Char]                       -- ^ List of insertion codes, one for each group (residue) [binary (type 6)]
-                           , sequenceIndexList :: ![Int32]                      -- ^ List of indices that point into the sequence property of an entity object in the 'entityList' field that is associated with the chain the group belongs to [binary (type 8)]
+data GroupData = GroupData { groupList         :: !(IArray GroupType)              -- ^ List of groupType objects
+                           , groupTypeList     :: !(IArray Int32)                  -- ^ List of pointers to 'groupType' entries in 'groupList' by their keys [binary (type 4)]
+                           , groupIdList       :: !(IArray Int32)                  -- ^ List of group (residue) numbers [binary (type 8)]
+                           , secStructList     :: !(IArray SecondaryStructure)     -- ^ List of secondary structure assignments [binary (type 2)]
+                           , insCodeList       :: !(IArray Text)                   -- ^ List of insertion codes, one for each group (residue) [binary (type 6)]
+                           , sequenceIndexList :: !(IArray Int32)                  -- ^ List of indices that point into the sequence property of an entity object in the 'entityList' field that is associated with the chain the group belongs to [binary (type 8)]
                            }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData GroupData
+
 -- | Atoms data
 --
-data AtomData = AtomData { atomIdList    :: ![Int32]         -- ^ List of atom serial numbers [binary (type 8)]
-                         , altLocList    :: ![Char]          -- ^ List of alternate location labels, one for each atom [binary (type 6)]
-                         , bFactorList   :: ![Float]         -- ^ List of atom B-factors in in A^2, one for each atom [binary (type 10)]
-                         , xCoordList    :: ![Float]         -- ^ List of x atom coordinates in A, one for each atom [binary (type 10)]
-                         , yCoordList    :: ![Float]         -- ^ List of y atom coordinates in A, one for each atom [binary (type 10)]
-                         , zCoordList    :: ![Float]         -- ^ List of z atom coordinates in A, one for each atom [binary (type 10)]
-                         , occupancyList :: ![Float]         -- ^ List of atom occupancies, one for each atom [binary (type 9)]
+data AtomData = AtomData { atomIdList    :: !(IArray Int32)        -- ^ List of atom serial numbers [binary (type 8)]
+                         , altLocList    :: !(IArray Text)         -- ^ List of alternate location labels, one for each atom [binary (type 6)]
+                         , bFactorList   :: !(IArray Float)        -- ^ List of atom B-factors in in A^2, one for each atom [binary (type 10)]
+                         , xCoordList    :: !(IArray Float)        -- ^ List of x atom coordinates in A, one for each atom [binary (type 10)]
+                         , yCoordList    :: !(IArray Float)        -- ^ List of y atom coordinates in A, one for each atom [binary (type 10)]
+                         , zCoordList    :: !(IArray Float)        -- ^ List of z atom coordinates in A, one for each atom [binary (type 10)]
+                         , occupancyList :: !(IArray Float)        -- ^ List of atom occupancies, one for each atom [binary (type 9)]
                          }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
+instance NFData AtomData
+
 -- | MMTF datatype
 --
 data MMTF = MMTF { format    :: !FormatData    -- ^ MMTF format data
@@ -141,4 +183,6 @@
                  , group     :: !GroupData     -- ^ Groups data
                  , atom      :: !AtomData      -- ^ Atoms data
                  }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
+
+instance NFData MMTF
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -3,6 +3,7 @@
 import           Bio.MMTF
 import qualified Data.ByteString.Lazy as B
 import           Data.Int             (Int8)
+import           Data.Array           ((!))
 import           Test.Hspec
 
 import Bio.MMTF.Decode.Codec
@@ -19,7 +20,7 @@
     it "unpacks by Recursive indexing encoding" $ do
       let sample = [ 127, 41, 34, 1, 0, -50, -128, 0, 7, 127, 0, 127, 127, 14 ] :: [Int8]
       recIndexDec sample `shouldBe` [ 168, 34, 1, 0, -50, -128, 7, 127, 268 ]
-    it "upacks by Integer encoding" $ do
+    it "unpacks by Integer encoding" $ do
       let sample = [ 100, 100, 100, 100, 50, 50 ] :: [Int8]
       integerDec 100 sample `shouldBe` [ 1.00, 1.00, 1.00, 1.00, 0.50, 0.50 ]
 
@@ -31,9 +32,9 @@
     (structureId . structure) m `shouldBe` "1FSD"
     (numModels . structure) m `shouldBe` 41
     (length . bFactorList . atom) m `shouldBe` 20664
-    (experimentalMethods . structure) m `shouldBe` ["SOLUTION NMR"]
-    (head . xCoordList . atom) m `shouldBe` (-12.847)
-    (last . xCoordList . atom) m `shouldBe` 5.672
+    ((! 0) . experimentalMethods . structure) m `shouldBe` "SOLUTION NMR"
+    ((! 0) . xCoordList . atom) m `shouldBe` (-12.847)
+    ((! 20663) . xCoordList . atom) m `shouldBe` 5.672
 
 main :: IO ()
 main = hspec $ do
