packages feed

sequence-formats 1.4.1 → 1.5.0

raw patch · 5 files changed

+296/−148 lines, 5 filesdep ~attoparsecdep ~basedep ~bytestring

Dependency ranges changed: attoparsec, base, bytestring, containers, errors, exceptions, foldl, hspec, lens-family, pipes, pipes-attoparsec, pipes-bytestring, pipes-safe, tasty, tasty-hunit, transformers, vector

Files

sequence-formats.cabal view
@@ -1,78 +1,54 @@-cabal-version: >=1.10-name: sequence-formats-version: 1.4.1-license: GPL-3-license-file: LICENSE-maintainer: stephan.schiffels@mac.com-author: Stephan Schiffels-synopsis: A package with basic parsing utilities for several Bioinformatic data formats.-description:-    Contains utilities to parse and write Eigenstrat, Fasta, FreqSum, VCF and other file formats used in population genetics analyses.-category: Bioinformatics-build-type: Simple-extra-source-files:-    README.md-    Changelog.md-    testDat/example.bim-    testDat/example.eigenstratgeno-    testDat/example.fasta-    testDat/example.freqsum-    testDat/example.histogram.txt-    testDat/example.ind-    testDat/example.snp-    testDat/example.vcf-    testDat/example.pileup+name:                sequence-formats+version:             1.5.0+synopsis:            A package with basic parsing utilities for several Bioinformatic data formats.+description:         Contains utilities to parse and write Eigenstrat, Fasta, FreqSum, VCF, Plink and other file formats used in population genetics analyses.+license:             GPL-3+license-file:        LICENSE+author:              Stephan Schiffels+maintainer:          stephan.schiffels@mac.com+category:            Bioinformatics+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  README.md,+                     Changelog.md,+                     testDat/example.bim,+                     testDat/example.eigenstratgeno,+                     testDat/example.fasta,+                     testDat/example.freqsum,+                     testDat/example.histogram.txt,+                     testDat/example.ind,+                     testDat/example.snp,+                     testDat/example.vcf,+                     testDat/example.pileup  library-    exposed-modules:-        SequenceFormats.RareAlleleHistogram-        SequenceFormats.FreqSum-        SequenceFormats.Fasta-        SequenceFormats.VCF-        SequenceFormats.Eigenstrat-        SequenceFormats.Utils-        SequenceFormats.Pileup-    hs-source-dirs: src-    default-language: Haskell2010-    build-depends:-        base >=4.7 && <5,-        containers >=0.6.0.1,-        errors >=2.3.0,-        attoparsec >=0.13.2.2,-        pipes >=4.3.11,-        transformers >=0.5.6.2,-        bytestring >=0.10.8.2,-        lens-family >=1.2.3,-        pipes-bytestring >=2.1.6,-        foldl >=1.4.5,-        exceptions >=0.10.2,-        pipes-safe >=2.3.1,-        pipes-attoparsec >=0.5.1.5,-        vector >=0.12.0.3+  exposed-modules:     SequenceFormats.RareAlleleHistogram,+                       SequenceFormats.FreqSum,+                       SequenceFormats.Fasta,+                       SequenceFormats.VCF,+                       SequenceFormats.Eigenstrat,+                       SequenceFormats.Plink,+                       SequenceFormats.Utils,+                       SequenceFormats.Pileup+  hs-source-dirs:      src+  build-depends:       base >= 4.7 && < 5, containers, errors, attoparsec, pipes,+                       transformers, bytestring, lens-family,+                       pipes-bytestring, foldl, exceptions, pipes-safe,+                       pipes-attoparsec, vector+  default-language:    Haskell2010 -test-suite sequenceFormatTests-    type: exitcode-stdio-1.0-    main-is: Spec.hs-    hs-source-dirs: test-    other-modules:-        SequenceFormats.EigenstratSpec-        SequenceFormats.FastaSpec-        SequenceFormats.FreqSumSpec-        SequenceFormats.RareAlleleHistogramSpec-        SequenceFormats.UtilsSpec-        SequenceFormats.VCFSpec-        SequenceFormats.PileupSpec-    default-language: Haskell2010-    build-depends:-        base >=4.12.0.0,-        sequence-formats -any,-        foldl >=1.4.5,-        pipes >=4.3.11,-        pipes-safe >=2.3.1,-        tasty >=1.2.3,-        vector >=0.12.0.3,-        transformers >=0.5.6.2,-        tasty-hunit >=0.10.0.2,-        bytestring >=0.10.8.2,-        containers >=0.6.0.1,-        hspec >=2.7.1+Test-Suite sequenceFormatTests+  type:                exitcode-stdio-1.0+  main-is:             Spec.hs+  hs-source-dirs:      test+  build-depends:       base, sequence-formats, foldl, pipes, pipes-safe, tasty, vector,+                       transformers, tasty-hunit, bytestring, containers, hspec+  other-modules:       SequenceFormats.EigenstratSpec,+                       SequenceFormats.FastaSpec,+                       SequenceFormats.FreqSumSpec,+                       SequenceFormats.RareAlleleHistogramSpec,+                       SequenceFormats.UtilsSpec,+                       SequenceFormats.PlinkSpec,+                       SequenceFormats.VCFSpec,+                       SequenceFormats.PileupSpec+  default-language:    Haskell2010
src/SequenceFormats/Eigenstrat.hs view
@@ -4,50 +4,63 @@  -} -module SequenceFormats.Eigenstrat (EigenstratSnpEntry(..), EigenstratIndEntry(..), -    readEigenstratInd, GenoEntry(..), GenoLine, Sex(..), -    readEigenstratSnpStdIn, readEigenstratSnpFile, readBimStdIn, readBimFile,-    readEigenstrat, writeEigenstrat, writeEigenstratIndFile, writeEigenstratSnp, writeBim, +module SequenceFormats.Eigenstrat (EigenstratSnpEntry(..), EigenstratIndEntry(..),+    readEigenstratInd, GenoEntry(..), GenoLine, Sex(..),+    readEigenstratSnpStdIn, readEigenstratSnpFile,+    readEigenstrat, writeEigenstrat, writeEigenstratIndFile, writeEigenstratSnp,     writeEigenstratGeno) where -import SequenceFormats.Utils (consumeProducer, SeqFormatException(..),-    Chrom(..), readFileProd, word)+import           SequenceFormats.Utils            (Chrom (..),+                                                   SeqFormatException (..),+                                                   consumeProducer,+                                                   readFileProd, word) -import Control.Applicative ((<|>))-import Control.Exception (throw)-import Control.Monad (void, forM_)-import Control.Monad.Catch (MonadThrow)-import Control.Monad.IO.Class (MonadIO, liftIO)+import           Control.Applicative              ((<|>))+import           Control.Exception                (throw)+import           Control.Monad                    (forM_, void)+import           Control.Monad.Catch              (MonadThrow)+import           Control.Monad.IO.Class           (MonadIO, liftIO) import qualified Data.Attoparsec.ByteString.Char8 as A-import Data.Vector (Vector, fromList, toList)-import qualified Data.ByteString.Char8 as B-import Pipes (Producer, Pipe, (>->), for, cat, yield, Consumer)-import Pipes.Safe (MonadSafe)-import qualified Pipes.Safe.Prelude as PS-import qualified Pipes.Prelude as P-import qualified Pipes.ByteString as PB-import System.IO (withFile, IOMode(..), Handle, hPutStrLn)+import qualified Data.ByteString.Char8            as B+import           Data.Vector                      (Vector, fromList, toList)+import           Pipes                            (Consumer, Pipe, Producer,+                                                   cat, for, yield, (>->))+import qualified Pipes.ByteString                 as PB+import qualified Pipes.Prelude                    as P+import           Pipes.Safe                       (MonadSafe)+import qualified Pipes.Safe.Prelude               as PS+import           System.IO                        (Handle, IOMode (..),+                                                   hPutStrLn, withFile)  -- |A datatype to represent a single genomic SNP. The constructor arguments are: -- Chromosome, Position, Reference Allele, Alternative Allele.-data EigenstratSnpEntry = EigenstratSnpEntry {-    snpChrom :: Chrom,-    snpPos :: Int,-    snpGeneticPos :: Double,-    snpId :: B.ByteString,-    snpRef :: Char,-    snpAlt :: Char- } deriving (Eq, Show)+data EigenstratSnpEntry = EigenstratSnpEntry+    { snpChrom      :: Chrom+    , snpPos        :: Int+    , snpGeneticPos :: Double+    , snpId         :: B.ByteString+    , snpRef        :: Char+    , snpAlt        :: Char+    }+    deriving (Eq, Show)  -- |A datatype to represent a single individual. The constructor arguments are: -- Name, Sex and Population Name-data EigenstratIndEntry = EigenstratIndEntry String Sex String deriving (Eq, Show)+data EigenstratIndEntry = EigenstratIndEntry String Sex String+    deriving (Eq, Show)  -- |A datatype to represent Sex in an Eigenstrat Individual file-data Sex = Male | Female | Unknown deriving (Eq, Show)+data Sex = Male+    | Female+    | Unknown+    deriving (Eq, Show)  -- |A datatype to represent the genotype of an individual at a SNP.-data GenoEntry = HomRef | Het | HomAlt | Missing deriving (Eq, Show)+data GenoEntry = HomRef+    | Het+    | HomAlt+    | Missing+    deriving (Eq, Show)  -- |Vector of the genotypes of all individuals at a single SNP. type GenoLine = Vector GenoEntry@@ -63,17 +76,6 @@     void A.endOfLine     return $ EigenstratSnpEntry (Chrom chrom) pos geneticPos snpId_ ref alt -bimParser :: A.Parser EigenstratSnpEntry-bimParser = do-    chrom <- word-    snpId_ <- A.skipMany1 A.space >> word-    geneticPos <- A.skipMany1 A.space >> A.double-    pos <- A.skipMany1 A.space >> A.decimal-    ref <- A.skipMany1 A.space >> A.satisfy (A.inClass "ACTGN")-    alt <- A.skipMany1 A.space >> A.satisfy (A.inClass "ACTGX")-    void A.endOfLine-    return $ EigenstratSnpEntry (Chrom chrom) pos geneticPos snpId_ ref alt-     eigenstratIndParser :: A.Parser EigenstratIndEntry eigenstratIndParser = do     A.skipMany A.space@@ -109,7 +111,7 @@             '1' -> return Het             '2' -> return HomRef             '9' -> return Missing-            _ -> error "this should never happen"+            _   -> error "this should never happen"   where     isValidNum c = c == '0' || c == '1' || c == '2' || c == '9' @@ -121,14 +123,6 @@ readEigenstratSnpFile :: (MonadSafe m) => FilePath -> Producer EigenstratSnpEntry m () readEigenstratSnpFile = consumeProducer eigenstratSnpParser . readFileProd --- |Function to read a Bim File from StdIn. Returns a Pipes-Producer over the EigenstratSnpEntries.-readBimStdIn :: (MonadThrow m, MonadIO m) => Producer EigenstratSnpEntry m ()-readBimStdIn = consumeProducer bimParser PB.stdin---- |Function to read a Bim File from a file. Returns a Pipes-Producer over the EigenstratSnpEntries.-readBimFile :: (MonadSafe m) => FilePath -> Producer EigenstratSnpEntry m ()-readBimFile = consumeProducer bimParser . readFileProd- -- |Function to read a full Eigenstrat database from files. Returns a pair of the Eigenstrat Individual Entries, and a joint Producer over the snp entries and the genotypes. readEigenstrat :: (MonadSafe m) => FilePath -- ^The Genotype file                -> FilePath -- ^The Snp File@@ -137,7 +131,7 @@ readEigenstrat genoFile snpFile indFile = do     indEntries <- readEigenstratInd indFile     let snpProd = readEigenstratSnpFile snpFile-        genoProd = consumeProducer eigenstratGenoParser (readFileProd genoFile) >-> +        genoProd = consumeProducer eigenstratGenoParser (readFileProd genoFile) >->             validateEigenstratEntries (length indEntries)     return (indEntries, P.zip snpProd genoProd) @@ -159,8 +153,8 @@             hPutStrLn h $ name <> "\t" <> sexToStr sex <> "\t" <> popName   where     sexToStr sex = case sex of-        Male -> "M"-        Female -> "F"+        Male    -> "M"+        Female  -> "F"         Unknown -> "U"  -- |Function to write an Eigenstrat Snp File. Returns a consumer expecting EigenstratSnpEntries.@@ -174,15 +168,6 @@             in  snpLine <> "\n")     in  toTextPipe >-> snpOutTextConsumer --- |Function to write a Bim file. Returns a consumer expecting EigenstratSnpEntries.-writeBim :: (MonadIO m) => Handle -- ^The Eigenstrat Snp File handle.-    -> Consumer EigenstratSnpEntry m () -- ^A consumer to read EigenstratSnpEntries-writeBim snpFileH =-    let snpOutTextConsumer = PB.toHandle snpFileH-        toTextPipe = P.map (\(EigenstratSnpEntry chrom pos gpos gid ref alt) ->-            B.intercalate "\t" [unChrom chrom, gid, B.pack (show gpos), B.pack (show pos), B.singleton ref, B.singleton alt])-    in  toTextPipe >-> snpOutTextConsumer- -- |Function to write an Eigentrat Geno File. Returns a consumer expecting Eigenstrat Genolines. writeEigenstratGeno :: (MonadIO m) => Handle -- ^The Genotype file handle                 -> Consumer GenoLine m () -- ^A consumer to read Genotype entries.@@ -194,9 +179,9 @@     in  toTextPipe >-> genoOutTextConsumer   where     toEigenStratNum c = case c of-        HomRef -> 2 :: Int-        Het -> 1-        HomAlt -> 0+        HomRef  -> 2 :: Int+        Het     -> 1+        HomAlt  -> 0         Missing -> 9  -- |Function to write an Eigenstrat Database. Returns a consumer expecting joint Snp- and Genotype lines.
+ src/SequenceFormats/Plink.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE BinaryLiterals    #-}+{-# LANGUAGE OverloadedStrings #-}+module SequenceFormats.Plink (readBimStdIn, readBimFile, writeBim, readFamFile, readPlinkBedFile) where++import           SequenceFormats.Eigenstrat       (EigenstratIndEntry (..),+                                                   EigenstratSnpEntry (..),+                                                   GenoEntry (..), GenoLine,+                                                   Sex (..))+import           SequenceFormats.Utils            (Chrom (..), consumeProducer,+                                                   readFileProd, word)++import           Control.Applicative              ((<|>))+import           Control.Monad                    (void)+import           Control.Monad.Catch              (MonadThrow, throwM)+import           Control.Monad.IO.Class           (MonadIO, liftIO)+import           Control.Monad.Trans.State.Strict (runStateT)+import qualified Data.Attoparsec.ByteString       as AB+import qualified Data.Attoparsec.ByteString.Char8 as A+import           Data.Bits                        (shiftR, (.&.))+import qualified Data.ByteString                  as BB+import qualified Data.ByteString.Char8            as B+import           Data.Vector                      (fromList)+import           Pipes                            (Consumer, Producer, (>->))+import           Pipes.Attoparsec                 (ParsingError (..), parse)+import qualified Pipes.ByteString                 as PB+import qualified Pipes.Prelude                    as P+import           Pipes.Safe                       (MonadSafe, runSafeT)+import qualified Pipes.Safe.Prelude+import           System.IO                        (Handle, IOMode (..),+                                                   withFile)+++bimParser :: A.Parser EigenstratSnpEntry+bimParser = do+    chrom      <- word+    snpId_     <- A.skipMany1 A.space >> word+    geneticPos <- A.skipMany1 A.space >> A.double+    pos        <- A.skipMany1 A.space >> A.decimal+    ref        <- A.skipMany1 A.space >> A.satisfy (A.inClass "ACTGN")+    alt        <- A.skipMany1 A.space >> A.satisfy (A.inClass "ACTGX")+    void A.endOfLine+    return $ EigenstratSnpEntry (Chrom chrom) pos geneticPos snpId_ ref alt++famParser :: A.Parser EigenstratIndEntry+famParser = do+    A.skipMany A.space+    pop <- word+    ind <- A.skipMany1 A.space >> word+    _   <- A.skipMany1 A.space >> A.decimal+    _   <- A.skipMany1 A.space >> A.decimal+    sex <- A.skipMany1 A.space >> parseSex+    _   <- A.skipMany1 A.space >> word+    void A.endOfLine+    return $ EigenstratIndEntry (B.unpack ind) sex (B.unpack pop)+  where+    parseSex = parseMale <|> parseFemale <|> parseUnknown+    parseMale = A.char '1' >> return Male+    parseFemale = A.char '2' >> return Female+    parseUnknown = A.anyChar >> return Unknown++bedHeaderParser :: AB.Parser ()+bedHeaderParser = do+    void $ AB.word8 0b01101100 -- magic number I for BED files+    void $ AB.word8 0b00011011 -- magic number II for BED files+    void $ AB.word8 0b00000001 -- we can only parse SNP-major order++bedGenotypeParser :: Int -> AB.Parser GenoLine+bedGenotypeParser nrInds = do+    let nrBytes = if nrInds `rem` 4 == 0 then nrInds `quot` 4 else (nrInds `quot` 4) + 1+    bytes <- BB.unpack <$> AB.take nrBytes+    let indBitPairs = concatMap getBitPairs bytes+    return . fromList . take nrInds . map bitPairToGenotype $ indBitPairs+  where+    getBitPairs byte = map (0b00000011 .&.) [byte, shiftR byte 2, shiftR byte 4, shiftR byte 6]+    bitPairToGenotype 0b00000000 = HomRef+    bitPairToGenotype 0b00000010 = Het+    bitPairToGenotype 0b00000011 = HomAlt+    bitPairToGenotype 0b00000001 = Missing+    bitPairToGenotype _          = error "This should never happen"++readPlinkBedProd :: (MonadThrow m) => Int -> Producer B.ByteString m () -> m (Producer GenoLine m ())+readPlinkBedProd nrInds prod = do+    (res, rest) <- runStateT (parse bedHeaderParser) prod+    _ <- case res of+        Nothing -> throwM $ ParsingError [] "Bed file exhausted prematurely"+        Just (Left e) -> throwM e+        Just (Right h) -> return h+    return $ consumeProducer (bedGenotypeParser nrInds) rest++-- |A function to read a bed file from a file. Returns a Producer over all lines.+readPlinkBedFile :: (MonadSafe m) => FilePath -> Int -> m (Producer GenoLine m ())+readPlinkBedFile file nrInds = readPlinkBedProd nrInds (Pipes.Safe.Prelude.withFile file ReadMode PB.fromHandle)++-- |Function to read a Bim File from StdIn. Returns a Pipes-Producer over the EigenstratSnpEntries.+readBimStdIn :: (MonadThrow m, MonadIO m) => Producer EigenstratSnpEntry m ()+readBimStdIn = consumeProducer bimParser PB.stdin++-- |Function to read a Bim File from a file. Returns a Pipes-Producer over the EigenstratSnpEntries.+readBimFile :: (MonadSafe m) => FilePath -> Producer EigenstratSnpEntry m ()+readBimFile = consumeProducer bimParser . readFileProd++-- |Function to read a Plink fam file. Returns the Eigenstrat Individual Entries as list.+readFamFile :: (MonadIO m) => FilePath -> m [EigenstratIndEntry]+readFamFile fn =+    liftIO . withFile fn ReadMode $ \handle ->+        P.toListM $ consumeProducer famParser (PB.fromHandle handle)++-- |Function to read a full Plink dataset from files. Returns a pair of the Plink Individual Entries, and a joint Producer over the snp entries and the genotypes.+readPlink :: (MonadSafe m) => FilePath -- ^The Bed file+               -> FilePath -- ^The Bim File+               -> FilePath -- ^The Fam file+               -> m ([EigenstratIndEntry], Producer (EigenstratSnpEntry, GenoLine) m ()) -- The return pair of individual entries and a joint Snp/Geno Producer.+readPlink bedFile bimFile famFile = do+    indEntries <- readFamFile famFile+    let nrInds = length indEntries+        snpProd = readBimFile bimFile+    genoProd <- readPlinkBedFile bedFile nrInds+    return (indEntries, P.zip snpProd genoProd)++-- |Function to write a Bim file. Returns a consumer expecting EigenstratSnpEntries.+writeBim :: (MonadIO m) => Handle -- ^The Eigenstrat Snp File handle.+    -> Consumer EigenstratSnpEntry m () -- ^A consumer to read EigenstratSnpEntries+writeBim snpFileH =+    let snpOutTextConsumer = PB.toHandle snpFileH+        toTextPipe = P.map (\(EigenstratSnpEntry chrom pos gpos gid ref alt) ->+            B.intercalate "\t" [unChrom chrom, gid, B.pack (show gpos), B.pack (show pos), B.singleton ref, B.singleton alt])+    in  toTextPipe >-> snpOutTextConsumer
test/SequenceFormats/EigenstratSpec.hs view
@@ -7,14 +7,13 @@ import Pipes (each, runEffect, (>->)) import qualified Pipes.Prelude as P import Pipes.Safe (runSafeT)-import SequenceFormats.Eigenstrat (readEigenstrat, writeEigenstrat, readBimFile,+import SequenceFormats.Eigenstrat (readEigenstrat, writeEigenstrat,     EigenstratSnpEntry(..), EigenstratIndEntry(..), GenoLine, Sex(..), GenoEntry(..)) import SequenceFormats.Utils (Chrom(..)) import Test.Hspec  spec :: Spec spec = do-    testReadBimFile     testReadEigenstrat     testWriteEigenstrat @@ -27,12 +26,6 @@     EigenstratSnpEntry (Chrom "11") 400000 0.004000 "rs4444" 'G' 'A',     EigenstratSnpEntry (Chrom "11") 500000 0.005000 "rs5555" 'T' 'A',     EigenstratSnpEntry (Chrom "11") 600000 0.006000 "rs6666" 'G' 'T']--testReadBimFile :: Spec-testReadBimFile = describe "readBimFile" $-    it "should read a BIM file correctly" $ do-        let esSnpProd = readBimFile "testDat/example.bim"-        (runSafeT $ purely P.fold list esSnpProd) `shouldReturn` mockDatEigenstratSnp  mockDatEigenstratInd :: [EigenstratIndEntry] mockDatEigenstratInd = [
+ test/SequenceFormats/PlinkSpec.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}+module SequenceFormats.PlinkSpec (spec) where++import           SequenceFormats.Eigenstrat (EigenstratIndEntry (..),+                                             EigenstratSnpEntry (..), Sex (..), GenoLine, GenoEntry(..))+import           SequenceFormats.Plink      (readBimFile, readFamFile, readPlinkBedFile)+import           SequenceFormats.Utils      (Chrom (..))++import           Control.Foldl              (list, purely)+import           Data.Vector                (fromList)+import qualified Pipes.Prelude              as P+import           Pipes.Safe                 (runSafeT)+import           Test.Hspec++spec :: Spec+spec = do+    testReadBimFile+    testReadFamFile+    testReadBedFile++mockDatEigenstratSnp :: [EigenstratSnpEntry]+mockDatEigenstratSnp = [+    EigenstratSnpEntry (Chrom "11") 0      0.000000 "rs0000" 'A' 'C',+    EigenstratSnpEntry (Chrom "11") 100000 0.001000 "rs1111" 'A' 'G',+    EigenstratSnpEntry (Chrom "11") 200000 0.002000 "rs2222" 'A' 'T',+    EigenstratSnpEntry (Chrom "11") 300000 0.003000 "rs3333" 'C' 'A',+    EigenstratSnpEntry (Chrom "11") 400000 0.004000 "rs4444" 'G' 'A',+    EigenstratSnpEntry (Chrom "11") 500000 0.005000 "rs5555" 'T' 'A',+    EigenstratSnpEntry (Chrom "11") 600000 0.006000 "rs6666" 'G' 'T']++mockDatEigenstratInd :: [EigenstratIndEntry]+mockDatEigenstratInd = [+    EigenstratIndEntry "SAMPLE0" Female "1",+    EigenstratIndEntry "SAMPLE1" Male "2",+    EigenstratIndEntry "SAMPLE2" Female "3",+    EigenstratIndEntry "SAMPLE3" Male "4",+    EigenstratIndEntry "SAMPLE4" Female "5"]++mockDatPlinkBed :: [GenoLine]+mockDatPlinkBed = [+    fromList [Het,    Het,    Het,    HomAlt, HomAlt],+    fromList [HomAlt, Het,    HomRef, Het,    HomRef],+    fromList [HomRef, Het,    Het,    HomAlt, Het],+    fromList [HomAlt, HomAlt, Het,    HomRef, HomRef],+    fromList [HomRef, Het,    Het,    HomAlt, HomAlt],+    fromList [HomAlt, HomAlt, Het,    Het,    Het],+    fromList [HomRef, HomRef, Het,    Het,    HomAlt]]++testReadBimFile :: Spec+testReadBimFile = describe "readBimFile" $+    it "should read a BIM file correctly" $ do+        let esSnpProd = readBimFile "testDat/example.bim"+        (runSafeT $ purely P.fold list esSnpProd) `shouldReturn` mockDatEigenstratSnp++testReadFamFile :: Spec+testReadFamFile = describe "readFamFile" $+    it "should read a FAM file correctly" $ do+        readFamFile "testDat/example.fam" `shouldReturn` mockDatEigenstratInd++testReadBedFile :: Spec+testReadBedFile = describe "readBedFile" $+    it "should read genotypes correctly" $ do+        let fn = "testDat/example.plink.bed"+        bedDat <- runSafeT $ do+            bedProd <- readPlinkBedFile fn 5+            purely P.fold list bedProd+        bedDat `shouldBe` mockDatPlinkBed