content: import Data.List       (group, sort)
  import Data.List.Split (chunksOf)
  import Text.Printf     (printf, IsChar(..), PrintfArg(..), fmtChar, fmtPrecision, formatString)
  
  data DNABase = A | C | G | T deriving (Show, Read, Eq, Ord)
  type DNASequence = [DNABase]
  
  instance IsChar DNABase where
    toChar = head . show
    fromChar = read . pure
  
  instance PrintfArg DNABase where
    formatArg x fmt = formatString (show x) (fmt { fmtChar = 's', fmtPrecision = Nothing })
  
  test :: DNASequence
  test = read . pure <$> concat
    [ "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"
    , "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"
    , "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"
    , "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"
    , "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"
    , "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"
    , "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"
    , "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"
    , "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"
    , "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT" ]
  
  chunkedDNASequence :: DNASequence -> [(Int, [DNABase])]
  chunkedDNASequence = zip [50,100..] . chunksOf 50
  
  baseCounts :: DNASequence -> [(DNABase, Int)]
  baseCounts = fmap ((,) . head <*> length) . group . sort
  
  main :: IO ()
  main = do
    putStrLn "Sequence:"
    mapM_ (uncurry (printf "%3d: %s\n")) $ chunkedDNASequence test
    putStrLn "\nBase Counts:"
    mapM_ (uncurry (printf "%2s: %2d\n")) $ baseCounts test
    putStrLn (replicate 8 '-') >> printf " Σ: %d\n\n" (length test)
  