packages feed

barchart-0.1: src/barchart.hs

{-# LANGUAGE NamedFieldPuns, RecordWildCards, DeriveDataTypeable #-}

import Graphics.BarChart hiding ( Intervals )
import Graphics.BarChart.Types ( readColor )

import Data.Char ( toLower )
import Data.Maybe ( fromMaybe )

import Control.Monad ( when, forM_ )

import System.FilePath

import System.Console.CmdArgs

import qualified Graphics.Rendering.Diagrams as D

data ExecMode
  = Blocks      { out_file, title, xlabel, ylabel :: String,
                  file_type :: FileType,
                  division, colors :: String,
                  width, height, label_size :: Int,
                  bar_width :: Double,
                  in_files :: [String] }
  | Intervals   { out_file, title, xlabel, ylabel :: String,
                  file_type :: FileType,
                  division, colors :: String,
                  width, height, label_size :: Int,
                  bar_width :: Double,
                  in_files :: [String] }
  | Criterion   { breakdown :: Breakdown,

                  out_file, title, xlabel, ylabel :: String,
                  file_type :: FileType,
                  division, colors :: String,
                  width, height, label_size :: Int,
                  bar_width :: Double,
                  in_files :: [String] }
  | Progression { breakdown :: Breakdown,

                  out_file, title, xlabel, ylabel :: String,
                  file_type :: FileType,
                  division, colors :: String,
                  width, height, label_size :: Int,
                  bar_width :: Double,
                  in_files :: [String] }
 deriving (Show,Data,Typeable)

data FileType = Guess_File_Type | PNG | SVG | PDF | PS
 deriving (Eq,Show,Data,Typeable)

suffixOf :: FileType -> String
suffixOf PNG = "png"
suffixOf SVG = "svg"
suffixOf PDF = "pdf"
suffixOf PS  = "ps"
suffixOf _   = ""

fromFileType :: FileType -> D.OutputType
fromFileType PNG = D.PNG
fromFileType SVG = D.SVG
fromFileType PDF = D.PDF
fromFileType PS  = D.PS
fromFileType _   = error "fromFileType: cannot convert guessed file type"

replaceUnknownFileType :: FileType -> FileType -> FileType
replaceUnknownFileType Guess_File_Type t = t
replaceUnknownFileType t               _ = t

data Breakdown = Summary | Summary_Comparison | Benchmark_Comparison
 deriving (Eq,Show,Data,Typeable)

blocksMode =
  Blocks {
    out_file   = outFile conf
                    &= text "Name of generated file"
                    &  typFile,
    file_type  = Guess_File_Type `enum`
                    [Guess_File_Type
                         &= text "Guess output file type by name (default)",
                     PNG &= text "Generate .png file",
                     SVG &= text "Generate .svg file",
                     PDF &= text "Generate .pdf file",
                     PS  &= text "Generate .ps file"],
    title      = caption conf
                    &= text "Title of bar chart"
                    &  typString,
    xlabel     = xLabel conf
                    &= text "Label of x axis"
                    &  typString,
    ylabel     = xLabel conf
                    &= text "Label of y axis"
                    &  typString,
    division  = "" &= text "Labels separated by whitespace"
                    &  typStrings,
    colors     = "" &= text "Color names separated by whitespace"
                    &  typStrings,
    width      = width
                    &= text "Width of generated bar chart"
                    &  typ "NUM",
    height     = height
                    &= text "Height of generated bar chart"
                    &  typ "NUM",
    label_size = 12 &= text "Font size used for labels"
                    &  typ "NUM",
    bar_width  = barRatio conf
                    &= text "Bar width between 0 and 1"
                    &  flag "W"
                    &  typ "FLOAT",
    in_files   = [] &= text "CVS files with data to visualise"
                    &  args }
 where (width,height) = dimensions conf

-- cannot reuse flag attributes in cmdargs :(
intervalsMode =
  Intervals {
    out_file   = outFile conf
                    &= text "Name of generated file"
                    &  typFile,
    file_type  = Guess_File_Type `enum`
                    [Guess_File_Type
                         &= text "Guess output file type by name (default)",
                     PNG &= text "Generate .png file",
                     SVG &= text "Generate .svg file",
                     PDF &= text "Generate .pdf file",
                     PS  &= text "Generate .ps file"],
    title      = caption conf
                    &= text "Title of bar chart"
                    &  typString,
    xlabel     = xLabel conf
                    &= text "Label of x axis"
                    &  typString,
    ylabel     = xLabel conf
                    &= text "Label of y axis"
                    &  typString,
    division  = "" &= text "Labels separated by whitespace"
                    &  typStrings,
    colors     = "" &= text "Color names separated by whitespace"
                    &  typStrings,
    width      = width
                    &= text "Width of generated bar chart"
                    &  typ "NUM",
    height     = height
                    &= text "Height of generated bar chart"
                    &  typ "NUM",
    label_size = 12 &= text "Font size used for labels"
                    &  typ "NUM",
    bar_width  = barRatio conf
                    &= text "Bar width between 0 and 1"
                    &  flag "W"
                    &  typ "FLOAT",
    in_files   = [] &= text "CVS files with data to visualise"
                    &  args }
 where (width,height) = dimensions conf

criterionMode =
  Criterion {
    out_file   = outFile conf
                    &= text "Name of generated file"
                    &  typFile,
    file_type  = Guess_File_Type `enum`
                    [Guess_File_Type
                         &= text "Guess output file type by name (default)",
                     PNG &= text "Generate .png file",
                     SVG &= text "Generate .svg file",
                     PDF &= text "Generate .pdf file",
                     PS  &= text "Generate .ps file"],
    title      = caption conf
                    &= text "Title of bar chart"
                    &  typString,
    xlabel     = xLabel conf
                    &= text "Label of x axis"
                    &  typString,
    ylabel     = xLabel conf
                    &= text "Label of y axis"
                    &  typString,
    division  = "" &= text "Labels separated by whitespace"
                    &  typStrings,
    colors     = "" &= text "Color names separated by whitespace"
                    &  typStrings,
    width      = width
                    &= text "Width of generated bar chart"
                    &  typ "NUM",
    height     = height
                    &= text "Height of generated bar chart"
                    &  typ "NUM",
    label_size = 12 &= text "Font size used for labels"
                    &  typ "NUM",
    bar_width  = barRatio conf
                    &= text "Bar width between 0 and 1"
                    &  flag "W"
                    &  typ "FLOAT",
    in_files   = [] &= text "CVS files with data to visualise"
                    &  args,
    breakdown  = Summary `enum`
                  [Summary
                    &= text "Show benchmark summary (default)",
                   Summary_Comparison
                    &= text "Compare different benchmark summaries"
                    &  flag "s",
                   Benchmark_Comparison
                    &= text "Compare different benchmarks"
                    &  flag "b"] }
 where (width,height) = dimensions conf

progressionMode =
  Progression {
    out_file   = outFile conf
                    &= text "Name of generated file"
                    &  typFile,
    file_type  = Guess_File_Type `enum`
                    [Guess_File_Type
                         &= text "Guess output file type by name (default)",
                     PNG &= text "Generate .png file",
                     SVG &= text "Generate .svg file",
                     PDF &= text "Generate .pdf file",
                     PS  &= text "Generate .ps file"],
    title      = caption conf
                    &= text "Title of bar chart"
                    &  typString,
    xlabel     = xLabel conf
                    &= text "Label of x axis"
                    &  typString,
    ylabel     = xLabel conf
                    &= text "Label of y axis"
                    &  typString,
    division  = "" &= text "Labels separated by whitespace"
                    &  typStrings,
    colors     = "" &= text "Color names separated by whitespace"
                    &  typStrings,
    width      = width
                    &= text "Width of generated bar chart"
                    &  typ "NUM",
    height     = height
                    &= text "Height of generated bar chart"
                    &  typ "NUM",
    label_size = 12 &= text "Font size used for labels"
                    &  typ "NUM",
    bar_width  = barRatio conf
                    &= text "Bar width between 0 and 1"
                    &  flag "W"
                    &  typ "FLOAT",
    in_files   = [] &= text "CVS files with data to visualise"
                    &  args,
    breakdown  = Summary_Comparison `enum`
                  [Summary_Comparison
                    &= text "Breakdown chart by benchmark summary (default)"
                    & flag "s",
                   Benchmark_Comparison
                    &= text "Breakdown chart by benchmarks"
                    &  flag "b"] }
 where (width,height) = dimensions conf

typString  = typ "STRING"
typStrings = typ "STRINGS"

execModes = [blocksMode &= defMode,
             intervalsMode, criterionMode, progressionMode]

exitIf msg cond = when cond (error msg)

main = do execMode <- cmdArgs "Bar Chart 0.0" (map mode execModes)
          exitIf "no input files given" $ null (in_files execMode)
          dispatch execMode

dispatch mode@Blocks{..} =
  forM_ in_files $ \in_file ->
    writeMultiBarChart
      (config (guessDefaults in_file mode))
      in_file
      (words division)

dispatch mode@Intervals{..} =
  forM_ in_files $ \in_file ->
    writeMultiBarIntervalChart
      (config (guessDefaults in_file mode))
      in_file
      (words division)

dispatch mode@Criterion{..} =
  case breakdown of
    Summary ->
      forM_ in_files $ \in_file ->
        writeCriterionChart
          (config (guessBenchmarkDefaults in_file mode))
          in_file
    Summary_Comparison ->
      writeComparisonChart False
        (config (guessBenchmarkDefaults "summaries.csv" mode))
        in_files
    Benchmark_Comparison ->
      writeComparisonChart True
        (config (guessBenchmarkDefaults "benchmarks.csv" mode))
        in_files

dispatch mode@Progression{..} =
  case breakdown of
    Summary_Comparison ->
      forM_ in_files $ \in_file ->
        writeProgressionChart True
          (config (guessBenchmarkDefaults in_file mode))
          in_file
          (words division)
    Benchmark_Comparison ->
      forM_ in_files $ \in_file ->
        writeProgressionChart False
          (config (guessBenchmarkDefaults in_file mode))
          in_file
          (words division)

guessDefaults :: FilePath -> ExecMode -> ExecMode
guessDefaults in_file = guessColors . guessTitle . guessFileType . guessOutFile
 where
  guessOutFile mode =
    mode { out_file = out_file mode ? replaceExtension in_file suffix }
   where suffix = suffixOf (file_type mode) ? ".png"

  guessFileType mode =
    mode { file_type = replaceUnknownFileType (file_type mode) $
                         fromMaybe (error $ "unsupported type: " ++ suffix) $
                           lookup suffix fileTypes }
   where suffix    = map toLower $ takeExtension (out_file mode)
         fileTypes = [(".png",PNG),(".svg",SVG),(".pdf",PDF),(".ps",PS)]

  guessTitle mode =
    mode { title = title mode ? takeBaseName (out_file mode) }

  guessColors mode =
    mode { colors = colors mode ? "forestgreen firebrick midnightblue" }

guessBenchmarkDefaults in_file = guessAxis . guessDefaults in_file
 where
  guessAxis mode = mode { xlabel = xlabel mode ? "benchmark",
                          ylabel = ylabel mode ? "run time" }

config mode = Config {
  outFile = out_file mode,
  outputType = fromFileType $ file_type mode,
  caption = title mode, xLabel = xlabel mode, yLabel = ylabel mode,
  barColors = map readColor . words $ colors mode,
  dimensions = (width mode,height mode),
  ratio = 1.0,
  fontSize = fromIntegral $ label_size mode,
  barRatio = bar_width mode }

(?) :: String -> String -> String
"" ? s = s
s  ? _ = s