diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2012 David Sorokin <david.sorokin@gmail.com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/Simulation/Aivika/Experiment.hs b/Simulation/Aivika/Experiment.hs
new file mode 100644
--- /dev/null
+++ b/Simulation/Aivika/Experiment.hs
@@ -0,0 +1,485 @@
+
+{-# LANGUAGE FlexibleInstances #-}
+
+-- |
+-- Module     : Simulation.Aivika.Experiment
+-- Copyright  : Copyright (c) 2012, David Sorokin <david.sorokin@gmail.com>
+-- License    : BSD3
+-- Maintainer : David Sorokin <david.sorokin@gmail.com>
+-- Stability  : experimental
+-- Tested with: GHC 7.4.1
+--
+-- The module defines the simulation experiments. They automate
+-- the process of generating and analyzing the results. Moreover,
+-- this module is open to extensions, allowing you to define
+-- your own output views for the simulations results, for example,
+-- such views that would allow saving the results in PDF or as
+-- charts. To decrease the number of dependencies, such possible  
+-- extenstions are not included in this package, although simple
+-- views are provided.
+--
+
+module Simulation.Aivika.Experiment
+       (Experiment(..),
+        defaultExperiment,
+        runExperiment,
+        ExperimentData(..),
+        experimentDataInStartTime,
+        experimentSeriesProviders,
+        experimentMixedSignal,
+        Series(..),
+        SeriesEntity(..),
+        SeriesProvider(..),
+        View(..),
+        Generator(..),
+        Reporter(..),
+        DirectoryName(..),
+        resolveDirectoryName,
+        FileName(..),
+        resolveFileName) where
+
+import Control.Monad
+import Control.Monad.State
+
+import qualified Data.Map as M
+import Data.Array
+import Data.Maybe
+import Data.Monoid
+import Data.String.Utils (replace)
+
+import System.IO
+import System.Directory
+import System.FilePath (combine)
+
+import Simulation.Aivika.Dynamics
+import Simulation.Aivika.Dynamics.Simulation
+import Simulation.Aivika.Dynamics.Signal
+import Simulation.Aivika.Dynamics.Ref
+import Simulation.Aivika.Dynamics.Var
+import Simulation.Aivika.Dynamics.UVar
+import Simulation.Aivika.Dynamics.EventQueue
+import Simulation.Aivika.Dynamics.Parameter
+
+import Simulation.Aivika.Experiment.HtmlWriter
+
+-- | It defines the simulation experiment.
+data Experiment = 
+  Experiment { experimentSpecs         :: Specs,
+               -- ^ The simulation specs for the experiment.
+               experimentRunCount      :: Int,
+               -- ^ How many simulation runs should be launched.
+               experimentDirectoryName :: DirectoryName,
+               -- ^ The directory in which the output results should be saved.
+               experimentTitle         :: String,
+               -- ^ The experiment title.
+               experimentDescription   :: String,
+               -- ^ The experiment description.
+               experimentVerbose       :: Bool,
+               -- ^ Whether the process of generating the results is verbose.
+               experimentGenerators    :: [Generator], 
+               -- ^ The experiment generators.
+               experimentIndexHtml     :: Experiment -> [Reporter] -> FilePath -> IO ()
+               -- ^ Create the @index.html@ file after the simulation is finished
+               -- in the specified directory. 
+             }
+
+-- | The default experiment.
+defaultExperiment :: Experiment
+defaultExperiment =
+  Experiment { experimentSpecs         = Specs 0 10 0.01 RungeKutta4,
+               experimentRunCount      = 1,
+               experimentDirectoryName = UniqueDirectoryName "experiment",
+               experimentTitle         = "Simulation Experiment",
+               experimentDescription   = "",
+               experimentVerbose       = True,
+               experimentGenerators    = [], 
+               experimentIndexHtml     = createIndexHtml }
+
+-- | This is a generator of the reporter.                     
+data Generator = 
+  Generator { generateReporter :: Experiment -> FilePath -> IO Reporter 
+              -- ^ Generate a reporter for the specified directory,
+              -- where the @index.html@ file will be saved for the 
+              -- current simulation experiment.
+            }
+
+-- | Defines a view in which the simulation results should be saved.
+-- You should extend this type class to define your own views such
+-- as the PDF document.
+class View v where
+  
+  -- | Create a generator of the reporter.
+  outputView :: v -> Generator
+  
+-- | Represents the series. It is usually something, or
+-- an array of something, or a list of such values which
+-- can be simulated.
+class Series s where
+  
+  -- | Return the simulatable entity with the specified name
+  -- for the given series.
+  seriesEntity :: String -> s -> SeriesEntity
+  
+-- | Defines the simulatable entity.
+data SeriesEntity =
+  SeriesEntity { seriesProviders :: [SeriesProvider]
+                 -- ^ Return the providers for the entity.
+               }
+  
+-- | This is provider of the simulatable data.
+data SeriesProvider =
+  SeriesProvider { providerName :: String,
+                   -- ^ Return the name.
+                   providerToDouble :: Maybe (Dynamics Double),
+                   -- ^ Try to return the data as double values.
+                   providerToInt :: Maybe (Dynamics Int),
+                   -- ^ Try to return the data as integers.
+                   providerToString :: Maybe (Dynamics String),
+                   -- ^ Try to return the data as strings.
+                   providerSignal :: Maybe (Signal ())
+                   -- ^ Try to get a signal for the data, which
+                   -- is actual for the 'Ref' references and 
+                   -- the 'Var' variables. You should not provide
+                   -- such a signal if the data are calculated
+                   -- only in the integration time points, which
+                   -- is true for the integrals, for example.
+                 }
+
+-- | It describes the source simulation data used in the experiment.
+data ExperimentData =
+  ExperimentData { experimentQueue :: EventQueue,
+                   -- ^ Return the event queue.
+                   experimentSignalInIntegTimes :: Signal Double,
+                   -- ^ The signal triggered in the integration time points.
+                   experimentSignalInStartTime :: Signal Double,
+                   -- ^ The signal triggered in the start time.
+                   experimentSignalInStopTime :: Signal Double,
+                   -- ^ The signal triggered in the stop time.
+                   experimentSeries :: M.Map String SeriesEntity
+                   -- ^ The simulation entitities with labels as keys.
+                 }
+
+-- | Prepare data for the simulation experiment in start time from the series 
+-- with the specified labels.
+experimentDataInStartTime :: EventQueue -> [(String, SeriesEntity)] -> Simulation ExperimentData
+experimentDataInStartTime q m = runDynamicsInStartTime d where
+  d = do signalInIntegTimes <- newSignalInIntegTimes q
+         signalInStartTime  <- newSignalInStartTime q
+         signalInStopTime   <- newSignalInStopTime q
+         let series = M.fromList m
+         return ExperimentData { experimentQueue              = q,
+                                 experimentSignalInIntegTimes = signalInIntegTimes,
+                                 experimentSignalInStartTime  = signalInStartTime,
+                                 experimentSignalInStopTime   = signalInStopTime,
+                                 experimentSeries             = series }
+
+-- | Get a mixed signal for the specified providers based on 
+-- the experimental data. This signal is triggered when 
+-- the provided signals are triggered. The mixed signal is 
+-- also triggered in the integration time points if there is 
+-- at least one provider without signal.
+experimentMixedSignal :: ExperimentData -> [SeriesProvider] -> Signal ()
+experimentMixedSignal expdata providers =
+  let xs0 = map providerSignal providers
+      xs1 = filter isJust xs0
+      xs2 = filter isNothing xs0
+      signal1 = mconcat $ map fromJust xs1
+      signal2 = if null xs2 
+                then signal3 <> signal4
+                else signal5
+      signal3 = void $ experimentSignalInStartTime expdata
+      signal4 = void $ experimentSignalInStopTime expdata
+      signal5 = void $ experimentSignalInIntegTimes expdata
+  in signal1 <> signal2
+
+-- | Return the 'SeriesProvider' values from the experiment data by the specified labels.
+experimentSeriesProviders :: ExperimentData -> [String] -> [SeriesProvider]
+experimentSeriesProviders expdata labels =
+  join $ flip map labels $ \label ->
+  case M.lookup label (experimentSeries expdata) of
+    Nothing -> 
+      error $ 
+      "There is no series with label " ++ label ++ 
+      ": experimentSeriesProviders"
+    Just entity -> 
+      seriesProviders entity
+
+-- | Defines what creates the simulation reports.
+data Reporter =
+  Reporter { reporterInitialise :: IO (),
+             -- ^ Initialise the reporting before 
+             -- the simulation runs are started.
+             reporterFinalise   :: IO (),
+             -- ^ Finalise the reporting after
+             -- all simulation runs are finished.
+             reporterSimulate   :: ExperimentData -> 
+                                   Dynamics (Dynamics ()),
+             -- ^ Start the simulation run in the start time
+             -- and return a finalizer that will be called 
+             -- in the stop time after the last signal is 
+             -- triggered and processed.
+             reporterTOCHtml :: Int -> HtmlWriter (),
+             -- ^ Return a TOC (Table of Contents) item for 
+             -- the HTML index file after the finalisation 
+             -- function is called, i.e. in the very end. 
+             -- The agument specifies the ordered number of 
+             -- the item.
+             --
+             -- You should wrap your HTML in 'writeHtmlListItem'.
+             reporterHtml :: Int -> HtmlWriter ()
+             -- ^ Return an HTML code for the index file
+             -- after the finalisation function is called,
+             -- i.e. in the very end. The agument specifies
+             -- the ordered number of the item.
+           }
+
+-- | Run the simulation experiment sequentially. For example, 
+-- it can be a Monte-Carlo simulation dependentent on the external
+-- 'Parameter' values.
+runExperiment :: Experiment -> Simulation ExperimentData -> IO ()
+runExperiment e simulation = 
+  do let specs      = experimentSpecs e
+         runCount   = experimentRunCount e
+         dirName    = experimentDirectoryName e
+         generators = experimentGenerators e
+     path <- resolveDirectoryName Nothing dirName M.empty
+     when (experimentVerbose e) $
+       do putStr "Using directory " 
+          putStrLn path
+     createDirectoryIfMissing True path
+     reporters <- mapM (\x -> generateReporter x e path)
+                  generators
+     forM_ reporters reporterInitialise
+     let simulate :: Simulation ()
+         simulate =
+           do d  <- simulation
+              fs <- runDynamicsInStartTime $
+                    forM reporters $ \reporter ->
+                    reporterSimulate reporter d
+              runDynamicsInStopTime $
+                do updateSignal $ 
+                     experimentMixedSignal d $
+                     join $ map seriesProviders $
+                     M.elems $ experimentSeries d
+                   sequence_ fs
+     sequence_ $ runSimulations simulate specs runCount
+     forM_ reporters reporterFinalise
+     experimentIndexHtml e e reporters path
+     return ()
+     
+-- | Create an index HTML file.     
+createIndexHtml :: Experiment -> [Reporter] -> FilePath -> IO ()
+createIndexHtml e reporters path = 
+  do let html :: HtmlWriter ()
+         html = 
+           writeHtmlDocumentWithTitle (experimentTitle e) $
+             do writeHtmlList $
+                  forM_ (zip [1..] reporters) $ \(i, reporter) -> 
+                  reporterTOCHtml reporter i
+                writeHtmlBreak
+                unless (null $ experimentDescription e) $
+                  writeHtmlParagraph $
+                  writeHtmlText $ experimentDescription e
+                forM_ (zip [1..] reporters) $ \(i, reporter) ->
+                  reporterHtml reporter i
+     ((), contents) <- runHtmlWriter html id
+     writeFile (path ++ "/index.html") (contents [])
+     when (experimentVerbose e) $
+       do putStr "Generated file "
+          putStr path
+          putStrLn "/index.html"
+
+-- | Specifies the directory name, unique or writable.
+data DirectoryName = WritableDirectoryName String
+                     -- ^ The directory which is overwritten in 
+                     -- case if it existed before.
+                   | UniqueDirectoryName String
+                     -- ^ The directory which is always unique,
+                     -- when a prefix is added to the name
+                     -- in case of need.
+                
+-- | Specifies the file name, unique or writable.
+data FileName = WritableFileName String String
+                -- ^ The file which is overwritten in 
+                -- case if it existed before. The first
+                -- field defines a name or its prototype.
+                -- The second field is the file extension.
+              | UniqueFileName String String
+                -- ^ The file which is always unique,
+                -- when a prefix is added to the name
+                -- in case of need. The first field
+                -- defines a name or its prototype.
+                -- The second field is the file exension.
+                
+-- | Resolve the directory name relative to the passed in directory 
+-- as the first argument, replacing the specified strings according the map. 
+resolveDirectoryName :: Maybe FilePath -> DirectoryName -> M.Map String String -> IO String
+resolveDirectoryName dir (WritableDirectoryName name) map = 
+  return $ replaceName (combineName dir name) map
+resolveDirectoryName dir (UniqueDirectoryName name) map =
+  let x = replaceName name map
+      loop y i =
+        do let n = combineName dir y
+           f1 <- doesFileExist n
+           f2 <- doesDirectoryExist n
+           if f1 || f2
+             then loop (x ++ "(" ++ show i ++ ")") (i + 1)
+             else return n
+  in loop x 2
+     
+-- | Resolve the file name relative to the passed in directory 
+-- as the first argument, replacing the specified strings according the map. 
+resolveFileName :: Maybe FilePath -> FileName -> M.Map String String -> IO String
+resolveFileName dir (WritableFileName name ext) map = 
+  return $ replaceName (combineName dir name ++ ext) map
+resolveFileName dir (UniqueFileName name ext) map =
+  let x = replaceName name map
+      loop y i =
+        do let n = combineName dir y ++ ext
+           f1 <- doesFileExist n
+           f2 <- doesDirectoryExist n
+           if f1 || f2
+             then loop (x ++ "(" ++ show i ++ ")") (i + 1)
+             else return n
+  in loop x 2
+     
+-- | Replace the name according the specified table.
+replaceName :: String -> M.Map String String -> String     
+replaceName name map = name' where
+  ((), name') = flip runState name $
+                forM_ (M.assocs map) $ \(k, v) ->
+                do a <- get
+                   put $ replace k v a
+                   
+-- | Combine the file name with the directory name.
+combineName :: Maybe String -> String -> String                   
+combineName dir name =
+  case dir of
+    Nothing  -> name
+    Just dir -> combine dir name
+
+instance Series (Dynamics Double) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just s,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ fmap show s,
+                                        providerSignal   = Nothing }] }
+
+instance Series (Dynamics Int) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ fmap (fromInteger . toInteger) s,
+                                        providerToInt    = Just s,
+                                        providerToString = Just $ fmap show s,
+                                        providerSignal   = Nothing }] }
+
+instance Series (Dynamics String) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Nothing,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just s,
+                                        providerSignal   = Nothing }] }
+
+instance Series (Ref Double) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ readRef s,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ fmap show (readRef s),
+                                        providerSignal   = Just $ refChanged_ s }] }
+
+instance Series (Ref Int) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ fmap (fromInteger . toInteger) (readRef s),
+                                        providerToInt    = Just $ readRef s,
+                                        providerToString = Just $ fmap show (readRef s),
+                                        providerSignal   = Just $ refChanged_ s }] }
+
+instance Series (Ref String) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Nothing,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ readRef s,
+                                        providerSignal   = Just $ refChanged_ s }] }
+
+instance Series (Var Double) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ readVar s,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ fmap show (readVar s),
+                                        providerSignal   = Just $ varChanged_ s }] }
+
+instance Series (Var Int) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ fmap (fromInteger . toInteger) (readVar s),
+                                        providerToInt    = Just $ readVar s,
+                                        providerToString = Just $ fmap show (readVar s),
+                                        providerSignal   = Just $ varChanged_ s }] }
+
+instance Series (Var String) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Nothing,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ readVar s,
+                                        providerSignal   = Just $ varChanged_ s }] }
+
+instance Series (UVar Double) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ readUVar s,
+                                        providerToInt    = Nothing,
+                                        providerToString = Just $ fmap show (readUVar s),
+                                        providerSignal   = Just $ uvarChanged_ s }] }
+
+instance Series (UVar Int) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      [SeriesProvider { providerName     = name,
+                                        providerToDouble = Just $ fmap (fromInteger . toInteger) (readUVar s),
+                                        providerToInt    = Just $ readUVar s,
+                                        providerToString = Just $ fmap show (readUVar s),
+                                        providerSignal   = Just $ uvarChanged_ s }] }
+    
+instance Series s => Series [s] where
+  
+  seriesEntity name s = 
+    SeriesEntity { seriesProviders = 
+                      join $ forM (zip [1..] s) $ \(i, s) ->
+                      let name' = name ++ "[" ++ show i ++ "]"
+                      in seriesProviders $ seriesEntity name' s }
+    
+instance (Show i, Ix i, Series s) => Series (Array i s) where
+  
+  seriesEntity name s =
+    SeriesEntity { seriesProviders =
+                      join $ forM (assocs s) $ \(i, s) ->
+                      let name' = name ++ "[" ++ show i ++ "]"
+                      in seriesProviders $ seriesEntity name' s }
diff --git a/Simulation/Aivika/Experiment/HtmlWriter.hs b/Simulation/Aivika/Experiment/HtmlWriter.hs
new file mode 100644
--- /dev/null
+++ b/Simulation/Aivika/Experiment/HtmlWriter.hs
@@ -0,0 +1,384 @@
+
+-- |
+-- Module     : Simulation.Aivika.Experiment.HtmlWriter
+-- Copyright  : Copyright (c) 2012, David Sorokin <david.sorokin@gmail.com>
+-- License    : BSD3
+-- Maintainer : David Sorokin <david.sorokin@gmail.com>
+-- Stability  : experimental
+-- Tested with: GHC 7.4.1
+--
+-- This is an utility module that provides an HTML writer.
+--
+
+module Simulation.Aivika.Experiment.HtmlWriter 
+       (HtmlWriter,
+        runHtmlWriter,
+        composeHtml,
+        writeHtml,
+        writeHtmlLn,
+        writeHtmlText,
+        writeHtmlParagraph,
+        writeHtmlParagraphWithId,
+        writeHtmlHeader1,
+        writeHtmlHeader1WithId,
+        writeHtmlHeader2,
+        writeHtmlHeader2WithId,
+        writeHtmlHeader3,
+        writeHtmlHeader3WithId,
+        writeHtmlHeader4,
+        writeHtmlHeader4WithId,
+        writeHtmlHeader5,
+        writeHtmlHeader5WithId,
+        writeHtmlHeader6,
+        writeHtmlHeader6WithId,
+        writeHtmlBreak,
+        writeHtmlLink,
+        writeHtmlImage,
+        writeHtmlList,
+        writeHtmlListItem,
+        writeHtmlDocumentWithTitle,
+        encodeHtmlText) where
+
+import Control.Monad
+import Control.Monad.Trans
+
+import Network.URI
+
+-- | It writes fast an HTML code.
+newtype HtmlWriter a = 
+  HtmlWriter { runHtmlWriter :: ShowS -> IO (a, ShowS)
+               -- ^ Run the HTML writer monad.
+             }
+
+instance Monad HtmlWriter where
+  
+  return a = HtmlWriter $ \f -> return (a, f)
+  
+  (HtmlWriter m) >>= k = HtmlWriter $ \f ->
+    do (a, f') <- m f
+       let HtmlWriter m' = k a
+       m' f'
+       
+instance MonadIO HtmlWriter where       
+  
+  liftIO m = HtmlWriter $ \f ->
+    do x <- m
+       return (x, f)
+       
+-- | Write the HTML code.
+writeHtml :: String -> HtmlWriter ()
+writeHtml code = 
+  HtmlWriter $ \f -> return ((), f . (code ++))
+                     
+-- | Write the HTML code.
+writeHtmlLn :: String -> HtmlWriter ()
+writeHtmlLn code = 
+  do writeHtml code
+     writeHtml "\n"
+                     
+-- | Write the text in HTML.                     
+writeHtmlText :: String -> HtmlWriter ()                     
+writeHtmlText text =
+  HtmlWriter $ \f -> return ((), f . (encodeHtmlText text ++))
+                     
+-- | Compose the HTML applying the corresponded transformation.                     
+composeHtml :: ShowS -> HtmlWriter ()                     
+composeHtml g =
+  HtmlWriter $ \f -> return ((), f . g)
+
+-- | Write the HTML link with the specified URI and contents.
+writeHtmlLink :: String -> HtmlWriter () -> HtmlWriter ()
+writeHtmlLink uri inner =
+  do writeHtml "<a href=\""
+     writeHtml $ escapeURIString isUnescapedInURI uri
+     writeHtml "\">"
+     inner
+     writeHtml "</a>"
+     
+-- | Write the HTML image with the specified URI.
+writeHtmlImage :: String -> HtmlWriter ()
+writeHtmlImage uri =
+  do writeHtml "<img src=\""
+     writeHtml $ escapeURIString isUnescapedInURI uri
+     writeHtml "\" />"
+
+-- | Write the @\<p\>@ element with the specified contents.     
+writeHtmlParagraph :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlParagraph inner =
+  do writeHtml "<p>"
+     inner
+     writeHtml "</p>"
+     
+-- | Write the @\<h1\>@ element with the specified contents.     
+writeHtmlHeader1 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader1 inner =
+  do writeHtml "<h1>"
+     inner
+     writeHtml "</h1>"
+     
+-- | Write the @\<h2\>@ element with the specified contents.     
+writeHtmlHeader2 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader2 inner =
+  do writeHtml "<h2>"
+     inner
+     writeHtml "</h2>"
+     
+-- | Write the @\<h3\>@ element with the specified contents.     
+writeHtmlHeader3 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader3 inner =
+  do writeHtml "<h3>"
+     inner
+     writeHtml "</h3>"
+     
+-- | Write the @\<h4\>@ element with the specified contents.     
+writeHtmlHeader4 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader4 inner =
+  do writeHtml "<h4>"
+     inner
+     writeHtml "</h4>"
+     
+-- | Write the @\<h5\>@ element with the specified contents.     
+writeHtmlHeader5 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader5 inner =
+  do writeHtml "<h5>"
+     inner
+     writeHtml "</h5>"
+     
+-- | Write the @\<h6\>@ element with the specified contents.     
+writeHtmlHeader6 :: HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader6 inner =
+  do writeHtml "<h6>"
+     inner
+     writeHtml "</h6>"
+     
+-- | Write the @\<p\>@ element with the specified id and contents.     
+writeHtmlParagraphWithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlParagraphWithId id inner =
+  do writeHtml "<p id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</p>"
+     
+-- | Write the @\<h1\>@ element with the specified id and contents.     
+writeHtmlHeader1WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader1WithId id inner =
+  do writeHtml "<h1 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h1>"
+     
+-- | Write the @\<h2\>@ element with the specified id and contents.     
+writeHtmlHeader2WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader2WithId id inner =
+  do writeHtml "<h2 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h2>"
+     
+-- | Write the @\<h3\>@ element with the specified id and contents.     
+writeHtmlHeader3WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader3WithId id inner =
+  do writeHtml "<h3 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h3>"
+     
+-- | Write the @\<h4\>@ element with the specified id and contents.     
+writeHtmlHeader4WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader4WithId id inner =
+  do writeHtml "<h4 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h4>"
+     
+-- | Write the @\<h5\>@ element with the specified id and contents.     
+writeHtmlHeader5WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader5WithId id inner =
+  do writeHtml "<h5 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h5>"
+     
+-- | Write the @\<h6\>@ element with the specified id and contents.     
+writeHtmlHeader6WithId :: String -> HtmlWriter () -> HtmlWriter ()     
+writeHtmlHeader6WithId id inner =
+  do writeHtml "<h6 id=\""
+     writeHtml id
+     writeHtml "\">"
+     inner
+     writeHtml "</h6>"
+     
+-- | Write the @\<br\>@ element.
+writeHtmlBreak :: HtmlWriter ()     
+writeHtmlBreak =
+  writeHtml "<br />"
+     
+-- | Write the list of items wrapped in @\<ul\>@.  
+writeHtmlList :: HtmlWriter () -> HtmlWriter ()
+writeHtmlList inner =
+  do writeHtml "<ul>"
+     inner
+     writeHtml "</ul>"
+
+-- | Write the item list wrapped in @\<li\>@.  
+writeHtmlListItem :: HtmlWriter () -> HtmlWriter ()
+writeHtmlListItem inner =
+  do writeHtml "<li>"
+     inner
+     writeHtml "</li>"
+
+-- | Write the HTML document with the specified title and contents
+writeHtmlDocumentWithTitle :: String -> HtmlWriter () -> HtmlWriter ()
+writeHtmlDocumentWithTitle title inner =
+  do writeHtml "<html>"
+     writeHtml "<head>"
+     writeHtml "<title>"
+     writeHtmlText title
+     writeHtml "</title>"
+     writeHtmlCss 
+     writeHtml "</head>"
+     writeHtml "<body>"
+     writeHtmlHeader1 $ 
+       writeHtmlText title
+     writeHtml "</h1>"
+     inner
+     writeHtml "<br /><p><font size=\"-1\">Automatically generated by "
+     writeHtml "<a href=\"https://github.com/dsorokin/aivika-experiment\">"
+     writeHtml "Aivika Experiment</a>"
+     writeHtml "</font></p>"
+     writeHtml "</body>"
+     writeHtml "</html>"
+
+-- | Escape special HTML characters in the 'String'.
+-- It is based on one function from package Web-Encodings,
+-- which is licensed under BSD3 but obsolete now.
+encodeHtmlText :: String -> String
+encodeHtmlText x = join $ map encodeHtmlChar x
+
+-- | Escape a character.
+encodeHtmlChar :: Char -> String
+encodeHtmlChar '<'  = "&lt;"
+encodeHtmlChar '>'  = "&gt;"
+encodeHtmlChar '&'  = "&amp;"
+encodeHtmlChar '"'  = "&quot;"
+encodeHtmlChar '\'' = "&#39;"
+encodeHtmlChar c    = [c]
+
+-- | Write the CSS styles
+writeHtmlCss :: HtmlWriter ()
+writeHtmlCss =
+  do writeHtmlLn "<style type=\"text/css\">"
+     writeHtmlLn "* { margin: 0; padding: 0 }"
+     writeHtmlLn ""
+     writeHtmlLn "html {"
+     writeHtmlLn "  background-color: white;"
+     writeHtmlLn "  width: 100%;"
+     writeHtmlLn "  height: 100%;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "body {"
+     writeHtmlLn "  background: white;"
+     writeHtmlLn "  color: black;"
+     writeHtmlLn "  text-align: left;"
+     writeHtmlLn "  min-height: 100%;"
+     writeHtmlLn "  position: relative;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "p {"
+     writeHtmlLn "  margin: 0.8em 0;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "ul, ol {"
+     writeHtmlLn "  margin: 0.8em 0 0.8em 2em;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "dl {"
+     writeHtmlLn "  margin: 0.8em 0;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "dt {"
+     writeHtmlLn "  font-weight: bold;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "dd {"
+     writeHtmlLn "  margin-left: 2em;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "a { text-decoration: none; }"
+     writeHtmlLn "a[href]:link { color: rgb(196,69,29); }"
+     writeHtmlLn "a[href]:visited { color: rgb(171,105,84); }"
+     writeHtmlLn "a[href]:hover { text-decoration:underline; }"
+     writeHtmlLn ""
+     writeHtmlLn "body {"
+     writeHtmlLn "  font:13px/1.4 sans-serif;"
+     writeHtmlLn "  *font-size:small; /* for IE */"
+     writeHtmlLn "  *font:x-small; /* for IE in quirks mode */"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "h1 { font-size: 146.5%; /* 19pt */ } "
+     writeHtmlLn "h2 { font-size: 131%;   /* 17pt */ }"
+     writeHtmlLn "h3 { font-size: 116%;   /* 15pt */ }"
+     writeHtmlLn "h4 { font-size: 100%;   /* 13pt */ }"
+     writeHtmlLn "h5 { font-size: 100%;   /* 13pt */ }"
+     writeHtmlLn ""
+     writeHtmlLn "select, input, button, textarea {"
+     writeHtmlLn "  font:99% sans-serif;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "table {"
+     writeHtmlLn "  font-size:inherit;"
+     writeHtmlLn "  font:100%;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "pre, code, kbd, samp, tt, .src {"
+     writeHtmlLn "  font-family:monospace;"
+     writeHtmlLn "  *font-size:108%;"
+     writeHtmlLn "  line-height: 124%;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn ".links, .link {"
+     writeHtmlLn "  font-size: 85%; /* 11pt */"
+     writeHtmlLn "}"
+     writeHtmlLn ".info  {"
+     writeHtmlLn "  font-size: 85%; /* 11pt */"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn ".caption, h1, h2, h3, h4, h5, h6 { "
+     writeHtmlLn "  font-weight: bold;"
+     writeHtmlLn "  color: rgb(78,98,114);"
+     writeHtmlLn "  margin: 0.8em 0 0.4em;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "* + h1, * + h2, * + h3, * + h4, * + h5, * + h6 {"
+     writeHtmlLn "  margin-top: 2em;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6 {"
+     writeHtmlLn "  margin-top: inherit;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "ul.links {"
+     writeHtmlLn "  list-style: none;"
+     writeHtmlLn "  text-align: left;"
+     writeHtmlLn "  float: right;"
+     writeHtmlLn "  display: inline-table;"
+     writeHtmlLn "  margin: 0 0 0 1em;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "ul.links li {"
+     writeHtmlLn "  display: inline;"
+     writeHtmlLn "  border-left: 1px solid #d5d5d5; "
+     writeHtmlLn "  white-space: nowrap;"
+     writeHtmlLn "  padding: 0;"
+     writeHtmlLn "}"
+     writeHtmlLn ""
+     writeHtmlLn "ul.links li a {"
+     writeHtmlLn "  padding: 0.2em 0.5em;"
+     writeHtmlLn "}"
+     writeHtmlLn "</style>"
diff --git a/Simulation/Aivika/Experiment/LastValueView.hs b/Simulation/Aivika/Experiment/LastValueView.hs
new file mode 100644
--- /dev/null
+++ b/Simulation/Aivika/Experiment/LastValueView.hs
@@ -0,0 +1,178 @@
+
+-- |
+-- Module     : Simulation.Aivika.Experiment.LastValueView
+-- Copyright  : Copyright (c) 2012, David Sorokin <david.sorokin@gmail.com>
+-- License    : BSD3
+-- Maintainer : David Sorokin <david.sorokin@gmail.com>
+-- Stability  : experimental
+-- Tested with: GHC 7.4.1
+--
+-- The module defines 'LastValueView' that shows the last values
+-- for the simulation variables.
+--
+
+module Simulation.Aivika.Experiment.LastValueView 
+       (LastValueView(..),
+        defaultLastValueView) where
+
+import Control.Monad
+import Control.Monad.Trans
+
+import qualified Data.Map as M
+import Data.IORef
+import Data.Maybe
+
+import Data.String.Utils (replace)
+
+import Simulation.Aivika.Experiment
+import Simulation.Aivika.Experiment.HtmlWriter
+
+import Simulation.Aivika.Dynamics
+import Simulation.Aivika.Dynamics.Simulation
+import Simulation.Aivika.Dynamics.Signal
+import Simulation.Aivika.Dynamics.EventQueue
+import Simulation.Aivika.Dynamics.Base
+
+-- | Defines the 'View' that shows the last values of the simulation
+-- variables.
+data LastValueView =
+  LastValueView { lastValueTitle       :: String,
+                  -- ^ The title for the view.
+                  lastValueRunTitle    :: String,
+                  -- ^ The run title for the view. It may include
+                  -- special variables @$RUN_INDEX@, @$RUN_COUNT@ and 
+                  -- @$TITLE@.
+                  --
+                  -- An example is 
+                  --
+                  -- @
+                  --   lastValueRunTitle = \"$TITLE / Run $RUN_INDEX of $RUN_COUNT\"
+                  -- @
+                  lastValueDescription :: String,
+                  -- ^ The description for the view.
+                  lastValueFormatter   :: ShowS,
+                  -- ^ It transforms data before they will be shown.
+                  lastValueSeries      :: [String] 
+                  -- ^ It contains the labels of the observed series.
+                }
+  
+-- | This is the default view.
+defaultLastValueView :: LastValueView
+defaultLastValueView =  
+  LastValueView { lastValueTitle       = "The Last Values",
+                  lastValueRunTitle    = "$TITLE / Run $RUN_INDEX of $RUN_COUNT",
+                  lastValueDescription = [],
+                  lastValueFormatter   = id,
+                  lastValueSeries      = [] }
+  
+instance View LastValueView where  
+  
+  outputView v = 
+    let reporter exp dir =
+          do st <- newLastValues v exp
+             return Reporter { reporterInitialise = return (),
+                               reporterFinalise   = return (),
+                               reporterSimulate   = simulateLastValues st,
+                               reporterTOCHtml    = lastValueTOCHtml st,
+                               reporterHtml       = lastValueHtml st }
+    in Generator { generateReporter = reporter }
+  
+-- | The state of the view.
+data LastValueViewState =
+  LastValueViewState { lastValueView       :: LastValueView,
+                       lastValueExperiment :: Experiment,
+                       lastValueMap        :: M.Map Int (IORef [(String, String)]) }
+  
+-- | Create a new state of the view.
+newLastValues :: LastValueView -> Experiment -> IO LastValueViewState
+newLastValues view exp =
+  do let n = experimentRunCount exp
+     rs <- forM [0..(n - 1)] $ \i -> newIORef []    
+     let m = M.fromList $ zip [0..(n - 1)] rs
+     return LastValueViewState { lastValueView       = view,
+                                 lastValueExperiment = exp,
+                                 lastValueMap        = m }
+       
+-- | Get the last values during the simulation.
+simulateLastValues :: LastValueViewState -> ExperimentData -> Dynamics (Dynamics ())
+simulateLastValues st expdata =
+  do let labels = lastValueSeries $ lastValueView st
+         input  =
+           flip map (experimentSeriesProviders expdata labels) $ \provider ->
+           case providerToString provider of
+             Nothing -> error $
+                        "Cannot represent series " ++
+                        providerName provider ++ 
+                        " as a string: simulateLastValues"
+             Just input -> (providerName provider, input)
+     i <- liftSimulation simulationIndex
+     t <- time
+     enqueue (experimentQueue expdata) t $
+       -- we must subscribe through the event queue;
+       -- otherwise, we will loose a signal in the start time,
+       -- because the handleSignal_ function checks the event queue
+       handleSignal_ (experimentSignalInStopTime expdata) $ \t ->
+       do let r = fromJust $ M.lookup (i - 1) (lastValueMap st)
+          output <- forM input $ \(name, input) ->
+            do x <- input
+               return (name, x)
+          liftIO $ writeIORef r output
+     return $ return ()
+     
+-- | Get the HTML code.     
+lastValueHtml :: LastValueViewState -> Int -> HtmlWriter ()     
+lastValueHtml st index =
+  let n = experimentRunCount $ lastValueExperiment st
+  in if n == 1
+     then lastValueHtmlSingle st index
+     else lastValueHtmlMultiple st index
+     
+-- | Get the HTML code for a single run.
+lastValueHtmlSingle :: LastValueViewState -> Int -> HtmlWriter ()
+lastValueHtmlSingle st index =
+  do header st index
+     let r = fromJust $ M.lookup 0 (lastValueMap st)
+     pairs <- liftIO $ readIORef r
+     forM_ pairs $ \pair ->
+       formatPair pair (lastValueFormatter $ lastValueView st)
+
+-- | Get the HTML code for multiple runs
+lastValueHtmlMultiple :: LastValueViewState -> Int -> HtmlWriter ()
+lastValueHtmlMultiple st index =
+  do header st index
+     let n = experimentRunCount $ lastValueExperiment st
+     forM_ [0..(n - 1)] $ \i ->
+       do let subtitle = 
+                replace "$RUN_INDEX" (show $ i + 1) $
+                replace "$RUN_COUNT" (show n) $
+                replace "$TITLE" (lastValueTitle $ lastValueView st)
+                (lastValueRunTitle $ lastValueView st)
+          writeHtmlHeader4 $
+            writeHtmlText subtitle
+          let r = fromJust $ M.lookup i (lastValueMap st)
+          pairs <- liftIO $ readIORef r
+          forM_ pairs $ \pair ->
+            formatPair pair (lastValueFormatter $ lastValueView st)
+
+header :: LastValueViewState -> Int -> HtmlWriter ()
+header st index =
+  do writeHtmlHeader3WithId ("id" ++ show index) $
+       writeHtmlText (lastValueTitle $ lastValueView st)
+     let description = lastValueDescription $ lastValueView st
+     unless (null description) $
+       writeHtmlParagraph $
+       writeHtmlText description
+
+formatPair :: (String, String) -> ShowS -> HtmlWriter ()
+formatPair (name, value) formatter =
+  writeHtmlParagraph $ 
+  do writeHtmlText name
+     writeHtmlText " = "
+     writeHtmlText $ formatter value
+          
+-- | Get the TOC item     
+lastValueTOCHtml :: LastValueViewState -> Int -> HtmlWriter ()
+lastValueTOCHtml st index =
+  writeHtmlListItem $
+  writeHtmlLink ("#id" ++ show index) $
+  writeHtmlText (lastValueTitle $ lastValueView st)
diff --git a/Simulation/Aivika/Experiment/TableView.hs b/Simulation/Aivika/Experiment/TableView.hs
new file mode 100644
--- /dev/null
+++ b/Simulation/Aivika/Experiment/TableView.hs
@@ -0,0 +1,245 @@
+
+-- |
+-- Module     : Simulation.Aivika.Experiment.TableView
+-- Copyright  : Copyright (c) 2012, David Sorokin <david.sorokin@gmail.com>
+-- License    : BSD3
+-- Maintainer : David Sorokin <david.sorokin@gmail.com>
+-- Stability  : experimental
+-- Tested with: GHC 7.4.1
+--
+-- The module defines 'TableView' that saves the simulation
+-- results in the CSV file(s).
+--
+
+module Simulation.Aivika.Experiment.TableView 
+       (TableView(..), 
+        defaultTableView) where
+
+import Control.Monad
+import Control.Monad.Trans
+
+import qualified Data.Map as M
+import Data.IORef
+import Data.Maybe
+
+import System.IO
+import System.FilePath
+
+import Data.String.Utils (replace)
+
+import Simulation.Aivika.Experiment
+import Simulation.Aivika.Experiment.HtmlWriter
+
+import Simulation.Aivika.Dynamics
+import Simulation.Aivika.Dynamics.Simulation
+import Simulation.Aivika.Dynamics.Signal
+import Simulation.Aivika.Dynamics.EventQueue
+import Simulation.Aivika.Dynamics.Base
+
+-- | Defines the 'View' that saves the simulation results
+-- in the CSV file(s).
+data TableView =
+  TableView { tableTitle       :: String,
+              -- ^ This is a title used in HTML.
+              tableDescription :: String,
+              -- ^ This is a description in the HTML.
+              tableLinkText    :: String,
+              -- ^ It specifies the text for the link 
+              -- which is displayed in the HTML page
+              -- if there is only one simulation run. 
+              -- The link downloads the corresponded 
+              -- CSV file in the browser. If there are
+              -- more simulation runs, then this link 
+              -- is not shown.
+              --
+              -- An example is
+              --
+              -- @
+              --   tableLinkText = \"Download the CSV file\"
+              -- @
+              tableRunLinkText :: String,
+              -- ^ It specifies the link text which is 
+              -- displayed in the HTML page if there are 
+              -- many simulation runs. Such a link downloads 
+              -- the CSV file for the corresponded run. 
+              -- To define the text, you can use special 
+              -- variables @$LINK@, @$RUN_INDEX@ and @$RUN_COUNT@.
+              --
+              -- An example is 
+              -- 
+              -- @
+              --   tableRunLinkText = \"$LINK / Run $RUN_INDEX of $RUN_COUNT\"
+              -- @
+              -- 
+              -- If there is only one run, then the link of 
+              -- this kind is not displayed. Instead, only one 
+              -- link is shown, which text is defined by the 
+              -- 'tableLinkText' field.
+              tableFileName    :: FileName,
+              -- ^ It defines the file name for each CSV file. 
+              -- It may include special variables @$TITLE@, 
+              -- @$RUN_INDEX@ and @$RUN_COUNT@.
+              --
+              -- An example is
+              --
+              -- @
+              --   tableFileName = UniqueFileName \"$TITLE - $RUN_INDEX\", \".csv\"
+              -- @
+              tableSeparator   :: String,
+              -- ^ It defines the separator for the view. 
+              -- It delimits the cells in the rows of the CSV file.
+              tableFormatter   :: ShowS,
+              -- ^ It defines the formatter which is applied
+              -- to all values before they will be written
+              -- in the CSV file(s).
+              tablePredicate   :: Dynamics Bool,
+              -- ^ It specifies the predicate that defines
+              -- when we can save data in the table.
+              tableSeries      :: [String] 
+              -- ^ It contains the labels of data saved
+              -- in the CSV file(s).
+            }
+  
+-- | The default table view.  
+defaultTableView :: TableView
+defaultTableView = 
+  TableView { tableTitle       = "Table",
+              tableDescription = [],
+              tableLinkText    = "Download the CSV file",
+              tableRunLinkText = "$LINK / Run $RUN_INDEX of $RUN_COUNT",
+              tableFileName    = UniqueFileName "$TITLE - $RUN_INDEX" ".csv",
+              tableSeparator   = ",",
+              tableFormatter   = id,
+              tablePredicate   = return True,
+              tableSeries      = [] }
+  
+instance View TableView where
+  
+  outputView v = 
+    let reporter exp dir =
+          do st <- newTable v exp dir
+             return Reporter { reporterInitialise = return (),
+                               reporterFinalise   = return (),
+                               reporterSimulate   = simulateTable st,
+                               reporterTOCHtml    = tableTOCHtml st,
+                               reporterHtml       = tableHtml st }
+    in Generator { generateReporter = reporter }
+  
+-- | The state of the view.
+data TableViewState =
+  TableViewState { tableView       :: TableView,
+                   tableExperiment :: Experiment,
+                   tableDir        :: FilePath, 
+                   tableMap        :: M.Map Int FilePath }
+  
+-- | Create a new state of the view.
+newTable :: TableView -> Experiment -> FilePath -> IO TableViewState
+newTable view exp dir =
+  do let n = experimentRunCount exp
+     fs <- forM [0..(n - 1)] $ \i -> 
+       resolveFileName (Just dir) (tableFileName view) $
+       M.fromList [("$TITLE", tableTitle view),
+                   ("$RUN_INDEX", show $ i + 1),
+                   ("$RUN_COUNT", show n)]
+     let m = M.fromList $ zip [0..(n - 1)] fs
+     return TableViewState { tableView       = view,
+                             tableExperiment = exp,
+                             tableDir          = dir, 
+                             tableMap          = m }
+       
+-- | Write the tables during the simulation.
+simulateTable :: TableViewState -> ExperimentData -> Dynamics (Dynamics ())
+simulateTable st expdata =
+  do let labels = tableSeries $ tableView st
+         providers = experimentSeriesProviders expdata labels
+         input =
+           flip map providers $ \provider ->
+           case providerToString provider of
+             Nothing -> error $
+                        "Cannot represent series " ++
+                        providerName provider ++ 
+                        " as a string: simulateTable"
+             Just input -> input
+         separator = tableSeparator $ tableView st
+         formatter = tableFormatter $ tableView st
+         predicate = tablePredicate $ tableView st
+     i <- liftSimulation simulationIndex
+     -- create a new file
+     let f = fromJust $ M.lookup (i - 1) (tableMap st)
+     h <- liftIO $ openFile f WriteMode
+     -- write a header
+     liftIO $
+       do forM_ (zip [0..] providers) $ \(column, provider) ->
+            do when (column > 0) $ 
+                 hPutStr h separator
+               hPutStr h $ providerName provider
+          hPutStrLn h ""
+     t <- time
+     enqueue (experimentQueue expdata) t $
+       -- we must subscribe through the event queue;
+       -- otherwise, we will loose a signal in the start time,
+       -- because the handleSignal_ function checks the event queue
+       handleSignal_ (experimentMixedSignal expdata providers) $ \t ->
+       do p <- predicate
+          when p $
+            do forM_ (zip [0..] input) $ \(column, input) ->  -- write the row
+                 do x <- input                                -- write the column
+                    liftIO $
+                      do when (column > 0) $ 
+                           hPutStr h separator
+                         hPutStr h $ formatter x
+               liftIO $ hPutStrLn h ""
+     return $ 
+       liftIO $
+       do when (experimentVerbose $ tableExperiment st) $
+            putStr "Generated file " >> putStrLn f
+          hClose h  -- close the file
+     
+-- | Get the HTML code.     
+tableHtml :: TableViewState -> Int -> HtmlWriter ()     
+tableHtml st index =
+  let n = experimentRunCount $ tableExperiment st
+  in if n == 1
+     then tableHtmlSingle st index
+     else tableHtmlMultiple st index
+     
+-- | Get the HTML code for a single run.
+tableHtmlSingle :: TableViewState -> Int -> HtmlWriter ()
+tableHtmlSingle st index =
+  do header st index
+     let f = fromJust $ M.lookup 0 (tableMap st)
+     writeHtmlParagraph $
+       writeHtmlLink (makeRelative (tableDir st) f) $
+       writeHtmlText (tableLinkText $ tableView st)
+
+-- | Get the HTML code for multiple runs
+tableHtmlMultiple :: TableViewState -> Int -> HtmlWriter ()
+tableHtmlMultiple st index =
+  do header st index
+     let n = experimentRunCount $ tableExperiment st
+     forM_ [0..(n - 1)] $ \i ->
+       do let f = fromJust $ M.lookup i (tableMap st)
+              sublink = 
+                replace "$RUN_INDEX" (show $ i + 1) $
+                replace "$RUN_COUNT" (show n) $
+                replace "$LINK" (tableLinkText $ tableView st)
+                (tableRunLinkText $ tableView st)
+          writeHtmlParagraph $
+            writeHtmlLink (makeRelative (tableDir st) f) $
+            writeHtmlText sublink
+
+header :: TableViewState -> Int -> HtmlWriter ()
+header st index =
+  do writeHtmlHeader3WithId ("id" ++ show index) $ 
+       writeHtmlText (tableTitle $ tableView st)
+     let description = tableDescription $ tableView st
+     unless (null description) $
+       writeHtmlParagraph $ 
+       writeHtmlText description
+
+-- | Get the TOC item     
+tableTOCHtml :: TableViewState -> Int -> HtmlWriter ()
+tableTOCHtml st index =
+  writeHtmlListItem $
+  writeHtmlLink ("#id" ++ show index) $
+  writeHtmlText (tableTitle $ tableView st)
diff --git a/Simulation/Aivika/Experiment/Utils.hs b/Simulation/Aivika/Experiment/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Simulation/Aivika/Experiment/Utils.hs
@@ -0,0 +1,23 @@
+
+-- |
+-- Module     : Simulation.Aivika.Experiment.Utils
+-- Copyright  : Copyright (c) 2012, David Sorokin <david.sorokin@gmail.com>
+-- License    : BSD3
+-- Maintainer : David Sorokin <david.sorokin@gmail.com>
+-- Stability  : experimental
+-- Tested with: GHC 7.4.1
+--
+-- It defines utility functions missed in the standard library.
+--
+
+module Simulation.Aivika.Experiment.Utils
+       (divideBy) where
+
+-- | Divide into the groups removing those elements
+-- that satisfy the predicate.
+divideBy :: (a -> Bool) -> [a] -> [[a]]
+divideBy p xs =
+  case dropWhile p xs of
+    []  -> []
+    xs' -> ys : divideBy p xs''
+           where (ys, xs'') = break p xs'
diff --git a/aivika-experiment.cabal b/aivika-experiment.cabal
new file mode 100644
--- /dev/null
+++ b/aivika-experiment.cabal
@@ -0,0 +1,45 @@
+name:            aivika-experiment
+version:         0.1
+synopsis:        Simulation experiments for the Aivika library
+description:
+    This package allows defining simulation experiments for the Aivika
+    package. Such experiments define in declarative manner what should be
+    simulated and in which view the simulation results should be 
+    generated. It can be charts, tables and so on.
+    .
+    The library is extensible and you can add new views for the results.
+    .
+category:        Simulation
+license:         BSD3
+license-file:    LICENSE
+copyright:       (c) 2012. David Sorokin <david.sorokin@gmail.com>
+author:          David Sorokin
+maintainer:      David Sorokin <david.sorokin@gmail.com>
+homepage:        http://github.com/dsorokin/aivika-experiment
+cabal-version:   >= 1.2.0
+build-type:      Simple
+tested-with:     GHC == 7.4.1
+
+extra-source-files:  examples/MachRep3.hs
+
+library
+
+    exposed-modules: Simulation.Aivika.Experiment
+                     Simulation.Aivika.Experiment.HtmlWriter
+                     Simulation.Aivika.Experiment.LastValueView
+                     Simulation.Aivika.Experiment.TableView
+                     Simulation.Aivika.Experiment.Utils
+                     
+    build-depends:   base >= 3 && < 6,
+                     mtl >= 1.1.0.2,
+                     array >= 0.3.0.0,
+                     containers >= 0.4.0.0,
+                     directory >= 1.1.0.2,
+                     filepath >= 1.3.0.0,
+                     MissingH >= 1.2.0.0,
+                     network >= 2.4.0.1,
+                     aivika >= 0.4.3
+
+    extensions:      FlexibleInstances
+                     
+    ghc-options:     -O2
diff --git a/examples/MachRep3.hs b/examples/MachRep3.hs
new file mode 100644
--- /dev/null
+++ b/examples/MachRep3.hs
@@ -0,0 +1,127 @@
+
+-- It corresponds to model MachRep3 described in document 
+-- Introduction to Discrete-Event Simulation and the SimPy Language
+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf]. 
+-- SimPy is available on [http://simpy.sourceforge.net/].
+--   
+-- The model description is as follows.
+--
+-- Variation of models MachRep1, MachRep2. Two machines, but
+-- sometimes break down. Up time is exponentially distributed with mean
+-- 1.0, and repair time is exponentially distributed with mean 0.5. In
+-- this example, there is only one repairperson, and she is not summoned
+-- until both machines are down. We find the proportion of up time. It
+-- should come out to about 0.45.
+
+module MachRep3Model (model) where
+
+import System.Random
+import Control.Monad
+import Control.Monad.Trans
+
+import Simulation.Aivika.Dynamics
+import Simulation.Aivika.Dynamics.Simulation
+import Simulation.Aivika.Dynamics.Base
+import Simulation.Aivika.Dynamics.EventQueue
+import Simulation.Aivika.Dynamics.Ref
+import Simulation.Aivika.Dynamics.Resource
+import Simulation.Aivika.Dynamics.Process
+
+import Simulation.Aivika.Experiment
+import Simulation.Aivika.Experiment.LastValueView
+import Simulation.Aivika.Experiment.TableView
+
+specs = Specs { spcStartTime = 0.0,
+                spcStopTime = 1000.0,
+                spcDT = 1.0,
+                spcMethod = RungeKutta4 }
+
+description =
+  "Variation of models MachRep1, MachRep2. Two machines, but " ++
+  "sometimes break down. Up time is exponentially distributed with mean " ++
+  "1.0, and repair time is exponentially distributed with mean 0.5. In " ++
+  "this example, there is only one repairperson, and she is not summoned " ++
+  "until both machines are down. We find the proportion of up time. It " ++
+  "should come out to about 0.45."
+
+experiment :: Experiment
+experiment =
+  defaultExperiment {
+    experimentSpecs = specs,
+    experimentRunCount = 3,
+    experimentDescription = description,
+    experimentGenerators =
+      [outputView $ defaultLastValueView {
+          lastValueDescription = 
+             "It shows the last value of " ++
+             "the proportion of up time.",
+          lastValueSeries = ["x"] },
+       outputView $ defaultTableView {
+         tableDescription = 
+            "These are tables for " ++
+            "the proportion of up time.",
+         tableSeries = ["x"] } ] }
+
+upRate = 1.0 / 1.0       -- reciprocal of mean up time
+repairRate = 1.0 / 0.5   -- reciprocal of mean repair time
+
+exprnd :: Double -> IO Double
+exprnd lambda =
+  do x <- getStdRandom random
+     return (- log x / lambda)
+     
+model :: Simulation ExperimentData
+model =
+  do queue <- newQueue
+     
+     -- number of machines currently up
+     nUp <- newRef queue 2
+     
+     -- total up time for all machines
+     totalUpTime <- newRef queue 0.0
+     
+     repairPerson <- newResource queue 1
+     
+     pid1 <- newProcessID queue
+     pid2 <- newProcessID queue
+     
+     let machine :: ProcessID -> Process ()
+         machine pid =
+           do startUpTime <- liftDynamics time
+              upTime <- liftIO $ exprnd upRate
+              holdProcess upTime
+              finishUpTime <- liftDynamics time
+              liftDynamics $ modifyRef totalUpTime 
+                (+ (finishUpTime - startUpTime))
+                
+              liftDynamics $ modifyRef nUp $ \a -> a - 1
+              nUp' <- liftDynamics $ readRef nUp
+              if nUp' == 1
+                then passivateProcess
+                else do n <- liftDynamics $ 
+                             resourceCount repairPerson
+                        when (n == 1) $ 
+                          liftDynamics $ reactivateProcess pid
+              
+              requestResource repairPerson
+              repairTime <- liftIO $ exprnd repairRate
+              holdProcess repairTime
+              liftDynamics $ modifyRef nUp $ \a -> a + 1
+              releaseResource repairPerson
+              
+              machine pid
+
+     runDynamicsInStartTime $
+       do t0 <- starttime
+          runProcess (machine pid2) pid1 t0
+          runProcess (machine pid1) pid2 t0
+     
+     let result = 
+           do x <- readRef totalUpTime
+              y <- time
+              return $ x / (2 * y)          
+              
+     experimentDataInStartTime queue $
+       [("x", seriesEntity "The proportion of up time" result)]
+
+main = runExperiment experiment model
