diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/flower.cabal b/flower.cabal
new file mode 100644
--- /dev/null
+++ b/flower.cabal
@@ -0,0 +1,26 @@
+Name:           flower
+Version:        0.1
+License:        GPL
+
+Author:         Ketil Malde
+Maintainer:     Ketil Malde <ketil@malde.org>
+
+Category:       Bioinformatics
+Synopsis:       Analyze 454 flowgrams  (.SFF files)
+Description:    flower - The FLOWgram ExtracteR
+                .
+                Reads files in SFF-format and produces various output, including sequences
+		with quality, or flowgram data in tabular format.
+                .
+                The Darcs repository is at <http://malde.org/~ketil/biohaskell/flower>.
+
+HomePage:       http://malde.org/~ketil/biohaskell/flower
+Build-Depends:  bio >= 0.3.5, base, array >= 0.1, bytestring >= 0.9.1, binary
+Build-Type:     Simple
+Tested-with:    GHC==6.8.3
+
+ -- Data-files:     README
+Executable:     flower
+Main-Is:        Flower.hs
+Hs-Source-Dirs: src
+Ghc-Options:    
diff --git a/src/Flower.hs b/src/Flower.hs
new file mode 100644
--- /dev/null
+++ b/src/Flower.hs
@@ -0,0 +1,101 @@
+-- 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]] 
