packages feed

flower-0.1: src/Flower.hs

-- FlowEr - FLOWgram ExtractoR

module Main (main) where

import Bio.Sequence.SFF
import Bio.Sequence.Fasta
import Bio.Sequence.FastQ

import System.IO (stdout)
import System.Environment (getArgs)
import Numeric (showFFloat)
import Data.List (intersperse, partition)
import Data.ByteString.Char8 (unpack,ByteString)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString as B1
import qualified Data.ByteString.Lazy.Char8 as LB

import Data.Array.Unboxed
import Data.Array.ST
import Control.Monad.ST

main :: IO ()
main = do
  args <- getArgs
  let (opts,files) = partition (\p -> case p of ('-':_) -> True; _ -> False) args
  case opts of 
    ["-r"] -> mapM_ (\f -> hWriteFasta stdout . sffToSequence =<< readSFF f) files
    ["-q"] -> mapM_ (\f -> hWriteFastQ stdout . sffToSequence =<< readSFF f) files
    ["-f"] -> LB.putStrLn . LB.fromChunks . intersperse (B.pack "\n") . concat =<< mapM showflow files
    ["-h"] -> mapM_ (\f -> (putStr . sffToHistogram) =<< readSFF f) files

    _ -> error ("Usage: flower -[f|q|r] <file.sff> [<file2.sff> ..]\n"
                ++"  -r  output reads in Fasta format\n"
                ++"  -q  output in FastQ format\n"
                ++"  -f  output the flowgram in tabular format\n"
                ++"  -h  output a histogram table of flow values")
tab :: ByteString
tab = B.pack "\t"

-- these are clumsy, since we just might need the file name
showflow :: FilePath -> IO [ByteString]
showflow f = return . {- map (\s -> B.concat [B.pack f,t,s]) . -} showrun =<< readSFF f

fi :: Flow -> ByteString
fi = (!) farray 

farray :: Array Flow ByteString
farray = listArray (0,10000) [B.pack (showFFloat (Just 2) i "") | i <- [0,0.01..99.99::Double]]

showrun :: SFF -> [ByteString]
showrun (SFF h rs) = concatMap (showread h) rs

showread :: CommonHeader -> ReadBlock -> [ByteString]
showread h rd = let rn = read_name $ read_header rd
                    qgroups = qgroup (B1.unpack $ flow_index rd) (B1.unpack $ quality rd)
                    format p c v q = B.concat [rn,tab,B.pack (show p),tab,B.pack [c],tab,fi v,tab,B.pack (show q)]
                in zipWith4 format [(1::Int)..] (unpack $ flow h) (flowgram rd) qgroups

zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
zipWith4 f (a:as) (b:bs) (c:cs) (d:ds) =  f a b c d : zipWith4 f as bs cs ds
zipWith4 _ _ _ _ _ = []

-- | Take the unpacked index_offsets and quality values, and return 
--   a list of groups of quality values, each group corresponding to a flow value. 
--   Flow values < 0.5 result in empty groups.
qgroup :: [Index] -> [Qual] -> [[Qual]]
qgroup [] []       = let rest = []:rest in rest
qgroup is@(1:_) qs = let (iz,irest) = span (==0) (tail is)
                         (q1,qrest) = splitAt (length iz+1) qs
                     in q1 : qgroup irest qrest
qgroup (i:is) qs = [] : qgroup (i-1:is) qs

sffToHistogram :: SFF -> String
sffToHistogram (SFF h rs) = showHist . histogram (B.unpack $ flow h) . map flowgram $ rs

type Hist = UArray Flow Int

histogram :: String -> [[Flow]] -> (Hist,Hist,Hist,Hist)
histogram fl scores = runST $ do 
  let zero = newArray (0,9999) 0 :: ST s (STUArray s Flow Int)
  a <- zero
  c <- zero
  g <- zero
  t <- zero
  let ins1 ('A',i) = bump a i
      ins1 ('C',i) = bump c i
      ins1 ('G',i) = bump g i
      ins1 ('T',i) = bump t i
      ins1 (x,_)   = error ("Illegal character "++show x++" in flow!")
      bump ar i = readArray ar i >>= \x -> writeArray ar i (x+1)
  mapM_ ins1 (zip (cycle fl) (concat scores))
  a' <- unsafeFreeze a
  c' <- unsafeFreeze c
  g' <- unsafeFreeze g
  t' <- unsafeFreeze t
  return (a',c',g',t')

showHist :: (Hist,Hist,Hist,Hist) -> String
showHist (as,cs,gs,ts) = "Score\tA\tC\tG\tT\tsum\n" ++ 
    unlines [concat $ intersperse "\t" $ (showFFloat (Just 2) (fromIntegral sc/100::Double) "") : map show [as!sc,cs!sc,gs!sc,ts!sc, as!sc+cs!sc+gs!sc+ts!sc]
                 | sc <- [0..9999]]