diff --git a/.gitignore b/.gitignore
index 123b1269fb2ebc06bed691f06374ef2193c0e681..51c973c9ecf8a861cff8eee6d5596bfb1c9b85ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ build*
 CMakeFiles
 CMakeCache.txt
 stream/sequence_setup/include/sequences/ConfiguredSequence.cuh
+*pyc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c1598d0fa4f0235b62ee5f6f5b12454c12859b0..43e6c1fbab1cffbf730605d495253433ef40178a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
 project(cu_hlt C CXX)
 
 list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}) # for find_package
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) # for find_package
 
 # Deal with build type
 if(NOT CMAKE_BUILD_TYPE)
@@ -23,16 +24,32 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
 set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
 set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG")
 
-#include_directories("/cvmfs/lhcb.cern.ch/lib/lcg/releases/ROOT/6.08.06-d7e12/x86_64-centos7-gcc62-opt/include/")
-#include_directories("/cvmfs/lhcb.cern.ch/lib/lcg/releases/tbb/44_20160413-f254c/x86_64-centos7-gcc7-opt/include")
-#include_directories("/usr/local/root/include")
+find_package(TBB REQUIRED)
+
+find_package(ZLIB REQUIRED)
+
+option(USE_LZMA OFF)
+if(USE_LZMA)
+  find_package(LibLZMA REQUIRED)
+else(USE_LZMA)
+  set(LZMA_FOUND OFF)
+endif(USE_LZMA)
+
+option(USE_LZ4 OFF)
+if(USE_LZ4)
+  find_package(PkgConfig REQUIRED)
+  pkg_check_modules(LZ4 REQUIRED liblz4)
+else(USE_LZ4)
+  set(LZ4_FOUND OFF)
+endif(USE_LZ4)
 
 find_package(CUDA REQUIRED)
 #set(CUDA_HOST_COMPILER "g++")
 set(CUDA_SEPARABLE_COMPILATION ON)
 option(CUDA_PROPAGATE_HOST_FLAGS OFF)
 
-if ( EXISTS $ENV{ROOTSYS} )
+option(USE_ROOT OFF)
+if ((EXISTS $ENV{ROOTSYS}) AND (USE_ROOT))
    if(EXISTS $ENV{ROOTSYS}/cmake/ROOTConfig.cmake) # ROOT was compiled with cmake
      list(APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS})
    else() # ROOT was compiled with configure/make
@@ -65,6 +82,7 @@ add_subdirectory(checker)
 add_subdirectory(x86/UT/PrVeloUT)
 add_subdirectory(x86/SciFi)
 add_subdirectory(x86/velo/clustering)
+add_subdirectory(mdf)
 
 # Include directories
 include_directories(main/include)
@@ -87,38 +105,38 @@ include_directories(stream/sequence/include)
 include_directories(x86/SciFi/include)
 include_directories(cuda/SciFi/PrForward/include)
 include_directories(cuda/UT/UTDecoding/include)
-  
-# Files from source directories
-file(GLOB main_sources "main/src/*")
+include_directories(mdf/include)
+
+file(GLOB common_sources "main/src/*")
+
+# Remove main.cpp from common_sources
+get_filename_component(main_cpp_path ${CMAKE_CURRENT_SOURCE_DIR}/main/src/main.cpp ABSOLUTE)
+list(REMOVE_ITEM common_sources "${main_cpp_path}")
 
-cuda_add_executable(cu_hlt ${main_sources})
+# common library
+cuda_add_library(Common ${common_sources})
+target_link_libraries(Common mdf)
 
-if ( ROOT_FOUND )
+# main executable
+cuda_add_executable(cu_hlt ${main_cpp_path})
+
+target_include_directories(cu_hlt PUBLIC ${TBB_INCLUDE_DIRS})
+
+target_link_libraries(cu_hlt
+  ${TBB_LIBRARIES}
+  Common
+  Utils
+  Velo
+  UT
+  SciFi
+  x86VeloUT
+  x86Forward
+  Stream
+  x86Clustering
+  TrackChecking
+  CheckClustering)
+
+if (ROOT_FOUND)
   target_compile_definitions(cu_hlt PUBLIC WITH_ROOT)
-  target_link_libraries(cu_hlt
-    tbb
-    Utils
-    Velo
-    UT
-    SciFi
-    x86VeloUT
-    x86Forward
-    Stream
-    x86Clustering
-    TrackChecking
-    CheckClustering
-    ${ROOT_LIBRARIES})
-else()
-  target_link_libraries(cu_hlt
-    tbb
-    Utils
-    Velo
-    UT
-    SciFi
-    x86VeloUT
-    x86Forward
-    Stream
-    x86Clustering
-    TrackChecking
-    CheckClustering)
+  target_link_libraries(cu_hlt ${ROOT_LIBRARIES})
 endif()
diff --git a/checker/tracking/CMakeLists.txt b/checker/tracking/CMakeLists.txt
index 2591d0d776c4ee7925db362716ae5b2dbc92cfac..7d292d76a8212823f024b08206bd02a3ccf14c8b 100644
--- a/checker/tracking/CMakeLists.txt
+++ b/checker/tracking/CMakeLists.txt
@@ -2,8 +2,14 @@ include_directories(include)
 include_directories(../../main/include)
 include_directories(../../cuda/UT/PrVeloUT/include)
 
+
+
 file(GLOB tracking_checker_sources "src/*cpp")
 
 add_library(TrackChecking SHARED
   ${tracking_checker_sources}
 )
+
+if ( ROOT_FOUND )
+  target_compile_definitions(TrackChecking PUBLIC WITH_ROOT)
+endif()
diff --git a/checker/tracking/include/MCParticle.h b/checker/tracking/include/MCParticle.h
index f8490e9f3bcd424b5ae701002ae06a2acce28b64..ecdc6dcb11fb6d6e13c0a253017776fe6624f912 100644
--- a/checker/tracking/include/MCParticle.h
+++ b/checker/tracking/include/MCParticle.h
@@ -29,6 +29,7 @@ struct MCParticle {
   bool fromCharmDecay;
   bool fromStrangeDecay;
   uint32_t numHits;
+  uint32_t nPV; // # of reconstructible primary vertices in event
   std::vector<uint32_t> hits;
 
   bool isElectron() const { return 11 == std::abs(pid); };
diff --git a/checker/tracking/include/TrackChecker.h b/checker/tracking/include/TrackChecker.h
index f805bab4ac10d5ee2cbaf7cd2fd4a84b36608883..17f1a0f343c5a0fbcffa8a52fead7e298086db8b 100644
--- a/checker/tracking/include/TrackChecker.h
+++ b/checker/tracking/include/TrackChecker.h
@@ -7,114 +7,170 @@
  * @author Manuel Schiller
  * @date 2018-02-19
  *
- * 2018-07 Dorothea vom Bruch: updated to run over different track types, 
- * euse exact same categories as PrChecker2, 
+ * 2018-07 Dorothea vom Bruch: updated to run over different track types,
+ * use exact same categories as PrChecker2,
  * take input from Renato Quagliani's TrackerDumper
  */
 
 #pragma once
 
+#include <functional>
 #include <set>
 #include <string>
 #include <vector>
-#include <functional>
 
-#include "Tracks.h"
-#include "MCAssociator.h"
 #include "Logger.h"
+#include "MCAssociator.h"
+#include "Tracks.h"
+
+#ifdef WITH_ROOT
+#include "TDirectory.h"
+#include "TFile.h"
+#include "TH1D.h"
+#endif
+
+class TrackChecker {
+protected:
+  using AcceptFn = std::function<bool(MCParticles::const_reference &)>;
+  struct TrackEffReport {
+    std::string m_name;
+    AcceptFn m_accept;
+    std::size_t m_naccept = 0;
+    std::size_t m_nfound = 0;
+    std::size_t m_nacceptperevt = 0;
+    std::size_t m_nfoundperevt = 0;
+    std::size_t m_nclones = 0;
+    std::size_t m_nevents = 0;
+    float m_effperevt = 0.f;
+    float m_hitpur = 0.f;
+    float m_hiteff = 0.f;
+    std::set<uint32_t> m_keysseen;
+
+    /// no default construction
+    TrackEffReport() = delete;
+    /// usual copy construction
+    TrackEffReport(const TrackEffReport &) = default;
+    /// usual move construction
+    TrackEffReport(TrackEffReport &&) = default;
+    /// usual copy assignment
+    TrackEffReport &operator=(const TrackEffReport &) = default;
+    /// usual move assignment
+    TrackEffReport &operator=(TrackEffReport &&) = default;
+    /// construction from name and accept criterion for eff. denom.
+    template <typename F>
+    TrackEffReport(const std::string &name, const F &accept)
+        : m_name(name), m_accept(accept) {}
+    /// construction from name and accept criterion for eff. denom.
+    template <typename F>
+    TrackEffReport(std::string &&name, F &&accept)
+        : m_name(std::move(name)), m_accept(std::move(accept)) {}
+    /// register MC particles
+    void operator()(const MCParticles &mcps);
+    /// register track and its MC association
+    void operator()(trackChecker::Tracks::const_reference &track,
+                    MCParticles::const_reference &mcp, const float weight);
+    /// notify of end of event
+    void evtEnds();
+    /// free resources, and print result
+    ~TrackEffReport();
+  };
 
-class TrackChecker
-{
-   protected:
-        using AcceptFn = std::function<bool (MCParticles::const_reference&)>;
-        struct TrackEffReport {
-            std::string m_name;
-            AcceptFn m_accept;
-            std::size_t m_naccept = 0;
-            std::size_t m_nfound = 0;
-            std::size_t m_nacceptperevt = 0;
-            std::size_t m_nfoundperevt = 0;
-            std::size_t m_nclones = 0;
-            std::size_t m_nevents = 0;
-            float m_effperevt = 0.f;
-            float m_hitpur = 0.f;
-            float m_hiteff = 0.f;
-            std::set<uint32_t> m_keysseen;
-
-            /// no default construction
-            TrackEffReport() = delete;
-            /// usual copy construction
-            TrackEffReport(const TrackEffReport&) = default;
-            /// usual move construction
-            TrackEffReport(TrackEffReport&&) = default;
-            /// usual copy assignment
-            TrackEffReport& operator=(const TrackEffReport&) = default;
-            /// usual move assignment
-            TrackEffReport& operator=(TrackEffReport&&) = default;
-            /// construction from name and accept criterion for eff. denom.
-            template <typename F>
-            TrackEffReport(const std::string& name, const F& accept) :
-                m_name(name), m_accept(accept)
-            {}
-            /// construction from name and accept criterion for eff. denom.
-            template <typename F>
-            TrackEffReport(std::string&& name, F&& accept) :
-                m_name(std::move(name)), m_accept(std::move(accept))
-            {}
-            /// register MC particles
-            void operator()(const MCParticles& mcps);
-            /// register track and its MC association
-	  void operator()(trackChecker::Tracks::const_reference& track,
-                    MCParticles::const_reference& mcp,
-                    const float weight);
-            /// notify of end of event
-            void evtEnds();
-            /// free resources, and print result
-            ~TrackEffReport();
-        };
-
-        const float m_minweight = 0.7f;
-        std::vector<TrackEffReport> m_categories;
-
-        std::size_t m_nevents = 0;
-        std::size_t m_ntracks = 0;
-        std::size_t m_nghosts = 0;
-        float m_ghostperevent = 0.f;
-
-    public:
-        TrackChecker() {};
-        ~TrackChecker();
-        void operator()(const trackChecker::Tracks& tracks,
-                const MCAssociator& mcassoc,
-                const MCParticles& mcps);
+  struct HistoCategory {
+    std::string m_name;
+    AcceptFn m_accept;
+    std::set<uint32_t> m_keysseen;
+
+    /// construction from name and accept criterion for eff. denom.
+    template <typename F>
+    HistoCategory(const std::string &name, const F &accept)
+        : m_name(name), m_accept(accept) {}
+    /// construction from name and accept criterion for eff. denom.
+    template <typename F>
+    HistoCategory(std::string &&name, F &&accept)
+        : m_name(std::move(name)), m_accept(std::move(accept)) {}
+    /// notify of end of event
+    void evtEnds();
+  };
+
+  std::vector<TrackEffReport> m_categories;
+  std::vector<HistoCategory> m_histo_categories;
+  std::string m_trackerName = "";
+
+  struct Histos {
+#ifdef WITH_ROOT
+    std::map<std::string, TH1D> h_reconstructible_eta;
+    std::map<std::string, TH1D> h_reconstructible_p;
+    std::map<std::string, TH1D> h_reconstructible_pt;
+    std::map<std::string, TH1D> h_reconstructible_phi;
+    std::map<std::string, TH1D> h_reconstructible_nPV;
+    std::map<std::string, TH1D> h_reconstructed_eta;
+    std::map<std::string, TH1D> h_reconstructed_p;
+    std::map<std::string, TH1D> h_reconstructed_pt;
+    std::map<std::string, TH1D> h_reconstructed_phi;
+    std::map<std::string, TH1D> h_reconstructed_nPV;
+
+    TH1D h_ghost_nPV;
+    TH1D h_total_nPV;
+#endif
+    void initHistos(const std::vector<HistoCategory>& histo_categories);
+    void fillReconstructibleHistos(const MCParticles &mcps,
+                                   const HistoCategory &category);
+    void fillReconstructedHistos(const MCParticle &mcp,
+                                 HistoCategory &category);
+    void fillTotalHistos(const MCParticle &mcp);
+    void fillGhostHistos(const MCParticle &mcp);
+  };
+ 
+
+  const float m_minweight = 0.7f;
+  std::size_t m_nevents = 0;
+  std::size_t m_ntracks = 0;
+  std::size_t m_nghosts = 0;
+  float m_ghostperevent = 0.f;
+
+  virtual void SetHistoCategories() = 0;
+  virtual void SetCategories() = 0;
+
+public:
+  TrackChecker(){};
+  ~TrackChecker();
+  void operator()(const trackChecker::Tracks &tracks,
+                  const MCAssociator &mcassoc, const MCParticles &mcps);
+  const std::vector<HistoCategory>& histo_categories() {
+    return m_histo_categories;
+  }
+   Histos histos;
 };
 
-class TrackCheckerVelo : public TrackChecker
-{
-  public:
-      void SetCategories();
-      TrackCheckerVelo() {
-        SetCategories();
-      };
-  
+class TrackCheckerVelo : public TrackChecker {
+public:
+  void SetCategories();
+  void SetHistoCategories();
+  TrackCheckerVelo() {
+    SetCategories();
+    SetHistoCategories();
+    m_trackerName = "Velo";
+  };
 };
 
-class TrackCheckerVeloUT : public TrackChecker
-{
-  public:
-      void SetCategories();
-      TrackCheckerVeloUT() {
-        SetCategories();
-      };
-  
+class TrackCheckerVeloUT : public TrackChecker {
+public:
+  void SetCategories();
+  void SetHistoCategories();
+  TrackCheckerVeloUT() {
+    SetCategories();
+    SetHistoCategories();
+    m_trackerName = "VeloUT";
+  };
 };
 
-class TrackCheckerForward : public TrackChecker
-{
-  public:
-      void SetCategories();
-      TrackCheckerForward() {
-        SetCategories();
-      };
-  
-}; 
+class TrackCheckerForward : public TrackChecker {
+public:
+  void SetCategories();
+  void SetHistoCategories();
+  TrackCheckerForward() {
+    SetCategories();
+    SetHistoCategories();
+    m_trackerName = "Forward";
+  };
+};
diff --git a/checker/tracking/include/Tracks.h b/checker/tracking/include/Tracks.h
index 2ffb69a5668b76aaaf961ed6e4ef4f5892ebf171..979a871e14257345cb800e9373a1d3ea163cc7a8 100644
--- a/checker/tracking/include/Tracks.h
+++ b/checker/tracking/include/Tracks.h
@@ -23,7 +23,7 @@ namespace trackChecker {
   public:
     SomeLHCbIDs  allids;
     std::size_t n_matched_total = 0;
-    
+        
     void addId ( LHCbID id ) {
       allids.push_back(id);
     }
diff --git a/checker/tracking/include/velopix-input-reader.h b/checker/tracking/include/velopix-input-reader.h
index 2f3b464b5713f2a3e26b8bc63497cd0870347420..e13567d280aed9a9c9c768e95d8e2518202fd0df 100644
--- a/checker/tracking/include/velopix-input-reader.h
+++ b/checker/tracking/include/velopix-input-reader.h
@@ -7,92 +7,97 @@
  * @author Manuel Schiller
  * @date 2018-02-18
  *
- * 2018-07 Dorothea vom Bruch: updated to run over different track types, 
+ * 2018-07 Dorothea vom Bruch: updated to run over different track types,
  * take input from Renato Quagliani's TrackerDumper
  */
 
 #pragma once
 
-#include <string>
-#include <cstdint>
-#include <vector>
-#include <algorithm>
-#include <tuple>
-#include "MCParticle.h"
 #include "Common.h"
-#include "Logger.h"
 #include "InputTools.h"
-#include "TrackChecker.h"
+#include "Logger.h"
 #include "MCParticle.h"
+#include "TrackChecker.h"
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <tuple>
+#include <vector>
 
 class VelopixEvent {
 private:
-    template<class T>
-    static std::string strVector(const T v, const uint vSize, const uint numberOfElements = 5) {
-        std::string s = "";
-        auto n = std::min(vSize, numberOfElements);
-        for (size_t i=0; i<n; ++i) {
-            s += std::to_string(v[i]);
-            if (i != n-1) s += ", ";
-            else if (i == vSize-1) s += "";
-            else s += "...";
-        }
-        return s;
+  template <class T>
+  static std::string strVector(const T v, const uint vSize,
+                               const uint numberOfElements = 5) {
+    std::string s = "";
+    auto n = std::min(vSize, numberOfElements);
+    for (size_t i = 0; i < n; ++i) {
+      s += std::to_string(v[i]);
+      if (i != n - 1)
+        s += ", ";
+      else if (i == vSize - 1)
+        s += "";
+      else
+        s += "...";
     }
+    return s;
+  }
 
 public:
-    uint32_t size;
-    MCParticles mcps;
+  uint32_t size;
+  MCParticles mcps;
 
-    // Constructor
-    VelopixEvent() {};
-    VelopixEvent(const std::vector<char>& _event, const std::string& trackType, const bool checkFile = true);
+  // Constructor
+  VelopixEvent(){};
+  VelopixEvent(const std::vector<char> &_event, const std::string &trackType,
+               const bool checkFile = true);
 
-    void print() const;
+  void print() const;
 
-    MCParticles mcparticles() const;
+  MCParticles mcparticles() const;
 };
 
-std::tuple<bool, std::vector<VelopixEvent>> read_mc_folder(
-  const std::string& foldername,
-  const std::string& trackType,
-  uint number_of_files,
-  const uint start_event_offset,
-  const bool checkEvents = false
-);
- 
-template<typename t_checker>
-void call_pr_checker_impl(
-  const std::vector< trackChecker::Tracks >& all_tracks,
-  const std::string& folder_name_MC,
-  const uint start_event_offset,
-  const std::string& trackType
-) {
-   /* MC information */
+std::tuple<bool, std::vector<VelopixEvent>>
+read_mc_folder(const std::string &foldername, const std::string &trackType,
+               uint number_of_files, const uint start_event_offset,
+               const bool checkEvents = false);
+
+template <typename t_checker>
+void call_pr_checker_impl(const std::vector<trackChecker::Tracks> &all_tracks,
+                          const std::string &folder_name_MC,
+                          const uint start_event_offset,
+                          const std::string &trackType) {
+  /* MC information */
   int n_events = all_tracks.size();
-  const auto mc_folder_contents = read_mc_folder(folder_name_MC, trackType, n_events, start_event_offset, true );
+  const auto mc_folder_contents = read_mc_folder(
+      folder_name_MC, trackType, n_events, start_event_offset, true);
 
   if (std::get<0>(mc_folder_contents)) {
-    const std::vector<VelopixEvent>& events = std::get<1>(mc_folder_contents);
-    t_checker trackChecker {};
-    uint64_t evnum = 0; 
+    const std::vector<VelopixEvent> &events = std::get<1>(mc_folder_contents);
+    t_checker trackChecker{};
+#ifdef WITH_ROOT
+    trackChecker.histos.initHistos(trackChecker.histo_categories());
+#endif
+    uint64_t evnum = 0;
 
-    for (const auto& ev: events) {
-      const auto& mcps = ev.mcparticles();
-      const std::vector<MCParticle>& mcps_vector = ev.mcps;
+    for (const auto &ev : events) {
+      const auto &mcps = ev.mcparticles();
+      const std::vector<MCParticle> &mcps_vector = ev.mcps;
       MCAssociator mcassoc(mcps);
 
       trackChecker(all_tracks[evnum], mcassoc, mcps);
 
       // Check all tracks for duplicate LHCb IDs
       uint i_track = 0;
-      for (auto& track : all_tracks[evnum]) {
+      for (auto &track : all_tracks[evnum]) {
         auto ids = track.ids();
         std::sort(std::begin(ids), std::end(ids));
-        bool containsDuplicates = (std::unique(std::begin(ids), std::end(ids))) != std::end(ids);
+        bool containsDuplicates =
+            (std::unique(std::begin(ids), std::end(ids))) != std::end(ids);
 
         if (containsDuplicates) {
-          warning_cout << "WARNING: Track #" << std::dec << i_track << " contains duplicate LHCb IDs" << std::endl;
+          warning_cout << "WARNING: Track #" << std::dec << i_track
+                       << " contains duplicate LHCb IDs" << std::endl;
           for (auto id : ids) {
             warning_cout << std::hex << "0x" << id << ", ";
           }
@@ -100,7 +105,7 @@ void call_pr_checker_impl(
         }
         i_track++;
       }
-      
+
       ++evnum;
     }
   }
diff --git a/checker/tracking/python_scripts/ConfigHistos.py b/checker/tracking/python_scripts/ConfigHistos.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8ad4a227f416e5469b9799aeeaf7b99eea3759e
--- /dev/null
+++ b/checker/tracking/python_scripts/ConfigHistos.py
@@ -0,0 +1,89 @@
+from collections import defaultdict
+
+def efficiencyHistoDict() :
+    basedict = {
+        "eta" : {},
+        "p" : {},
+        "pt" : {},
+        "phi" : {},
+        "nPV" : {}
+        }
+    
+    basedict["eta"]["xTitle"] = "#eta"
+    basedict["eta"]["variable"] = "Eta"
+
+    basedict["p"]["xTitle"] = "p [MeV]"
+    basedict["p"]["variable"] = "P"
+
+    basedict["pt"]["xTitle"] = "p_{T} [MeV]"
+    basedict["pt"]["variable"] = "Pt"
+
+    basedict["phi"]["xTitle"] = "#phi [rad]"
+    basedict["phi"]["variable"] = "Phi"
+
+    basedict["nPV"]["xTitle"] = "# of PVs"
+    basedict["nPV"]["variable"] = "nPV"
+    
+    return basedict
+
+def ghostHistoDict() :
+    basedict = {
+        "eta" : {},
+        "nPV" : {}
+        }
+
+    basedict["eta"]["xTitle"] = "#eta"
+    basedict["eta"]["variable"] = "Eta"
+
+    basedict["nPV"]["xTitle"] = "# of PVs"
+    basedict["nPV"]["variable"] = "nPV"
+    
+    return basedict
+
+def getCuts() :
+     basedict = {
+        "Velo" : {},
+        "Upstream" : {},
+        "Forward" : {} 
+        }
+    
+     basedict["Velo"] = ["VeloTracks", "VeloTracks_eta25", "LongFromB_eta25", "LongFromD_eta25", "LongStrange_eta25"]
+     basedict["Upstream"] = ["VeloUTTracks_eta25", "LongFromB_eta25", "LongFromD_eta25", "LongStrange_eta25"]
+     basedict["Forward"] = ["Long_eta25", "LongFromB_eta25", "LongFromD_eta25", "LongStrange_eta25"]
+    
+     return basedict
+
+def categoriesDict() :
+    basedict = defaultdict(lambda : defaultdict(dict))
+
+    basedict["Velo"]["VeloTracks"]["title"]        = "Velo"
+    basedict["Velo"]["VeloTracks_eta25"]["title"]  = "Velo, 2 < eta < 5"
+    basedict["Velo"]["LongFromB_eta25"]["title"]   = "Long from B, 2 < eta < 5"
+    basedict["Velo"]["LongFromD_eta25"]["title"]   = "Long from D, 2 < eta < 5"
+    basedict["Velo"]["LongStrange_eta25"]["title"] = "Long strange, 2 < eta < 5"
+    basedict["Velo"]["VeloTracks"]["plotElectrons"]        = False
+    basedict["Velo"]["VeloTracks_eta25"]["plotElectrons"]  = True
+    basedict["Velo"]["LongFromB_eta25"]["plotElectrons"]   = False
+    basedict["Velo"]["LongFromD_eta25"]["plotElectrons"]   = False
+    basedict["Velo"]["LongStrange_eta25"]["plotElectrons"] = False
+
+    basedict["Upstream"]["VeloUTTracks_eta25"]["title"] = "veloUT, 2 < eta < 5"
+    basedict["Upstream"]["LongFromB_eta25"]["title"]    = "Long from B, 2 < eta < 5"
+    basedict["Upstream"]["LongFromD_eta25"]["title"]    = "Long from D, 2 < eta < 5"
+    basedict["Upstream"]["LongStrange_eta25"]["title"]  = "Long strange, 2 < eta < 5"
+    basedict["Upstream"]["VeloUTTracks_eta25"]["plotElectrons"] = False
+    basedict["Upstream"]["LongFromB_eta25"]["plotElectrons"]    = False
+    basedict["Upstream"]["LongFromD_eta25"]["plotElectrons"]    = False
+    basedict["Upstream"]["LongStrange_eta25"]["plotElectrons"]  = False
+
+    basedict["Forward"]["Long_eta25"]["title"]        = "Long, 2 < eta < 5"
+    basedict["Forward"]["LongFromB_eta25"]["title"]   = "Long from B, 2 < eta < 5"
+    basedict["Forward"]["LongFromD_eta25"]["title"]   = "Long from D, 2 < eta < 5"
+    basedict["Forward"]["LongStrange_eta25"]["title"] = "Long strange, 2 < eta < 5"
+    basedict["Forward"]["Long_eta25"]["plotElectrons"]        = True
+    basedict["Forward"]["LongFromB_eta25"]["plotElectrons"]   = False
+    basedict["Forward"]["LongFromD_eta25"]["plotElectrons"]   = False
+    basedict["Forward"]["LongStrange_eta25"]["plotElectrons"] = False
+    
+    
+    return basedict
diff --git a/checker/tracking/python_scripts/LHCbStyle.py b/checker/tracking/python_scripts/LHCbStyle.py
new file mode 100644
index 0000000000000000000000000000000000000000..e57b3fe3876b456dc79701776ae106967f21ec67
--- /dev/null
+++ b/checker/tracking/python_scripts/LHCbStyle.py
@@ -0,0 +1,104 @@
+from ROOT import gStyle
+from ROOT import gROOT
+from ROOT import TStyle
+
+def setLHCbStyle() :
+    global lhcbStyle
+
+    lhcbFont     = 132
+    lhcbTSize    = 0.06
+    lhcbWidth    = 2
+    
+    lhcbStyle= TStyle("lhcbStyle","LHCb plots style");
+    lhcbStyle.SetFillColor(1)
+    lhcbStyle.SetFillStyle(1001)   # solid
+    lhcbStyle.SetFrameFillColor(0)
+    lhcbStyle.SetFrameBorderMode(0)
+    lhcbStyle.SetPadBorderMode(0)
+    lhcbStyle.SetPadColor(0)
+    lhcbStyle.SetCanvasBorderMode(0)
+    lhcbStyle.SetCanvasColor(0)
+    lhcbStyle.SetStatColor(0)
+    lhcbStyle.SetLegendBorderSize(0)
+    lhcbStyle.SetLegendFont(132)
+
+    # use large fonts
+    lhcbStyle.SetTextFont(lhcbFont)
+    lhcbStyle.SetTitleFont(lhcbFont)
+    lhcbStyle.SetTextSize(lhcbTSize)
+    lhcbStyle.SetLabelFont(lhcbFont,"x")
+    lhcbStyle.SetLabelFont(lhcbFont,"y")
+    lhcbStyle.SetLabelFont(lhcbFont,"z")
+    lhcbStyle.SetLabelSize(lhcbTSize,"x")
+    lhcbStyle.SetLabelSize(lhcbTSize,"y")
+    lhcbStyle.SetLabelSize(lhcbTSize,"z")
+    lhcbStyle.SetTitleFont(lhcbFont)
+    lhcbStyle.SetTitleFont(lhcbFont,"x")
+    lhcbStyle.SetTitleFont(lhcbFont,"y")
+    lhcbStyle.SetTitleFont(lhcbFont,"z")
+    lhcbStyle.SetTitleSize(1.2*lhcbTSize,"x")
+    lhcbStyle.SetTitleSize(1.2*lhcbTSize,"y")
+    lhcbStyle.SetTitleSize(1.2*lhcbTSize,"z")
+
+    # set the paper & margin sizes
+    lhcbStyle.SetPaperSize(20,26)
+    lhcbStyle.SetPadTopMargin(0.05)
+    lhcbStyle.SetPadRightMargin(0.05) # increase for colz plots
+    lhcbStyle.SetPadBottomMargin(0.16)
+    lhcbStyle.SetPadLeftMargin(0.14)
+
+    # use medium bold lines and thick markers
+    lhcbStyle.SetLineWidth(lhcbWidth);
+    lhcbStyle.SetFrameLineWidth(lhcbWidth);
+    lhcbStyle.SetHistLineWidth(lhcbWidth);
+    lhcbStyle.SetFuncWidth(lhcbWidth);
+    lhcbStyle.SetGridWidth(lhcbWidth);
+    lhcbStyle.SetLineStyleString(2,"[12 12]"); # postscript dashes
+    lhcbStyle.SetMarkerStyle(20);
+    lhcbStyle.SetMarkerSize(1.0);
+    
+    # label offsets
+    lhcbStyle.SetLabelOffset(0.010,"X");
+    lhcbStyle.SetLabelOffset(0.010,"Y");
+    
+    # by default, do not display histogram decorations:
+    lhcbStyle.SetOptStat(0)  
+    #lhcbStyle.SetOptStat("emr")  # show only nent -e , mean - m , rms -r
+    # full opts at http:#root.cern.ch/root/html/TStyle.html#TStyle:SetOptStat
+    lhcbStyle.SetStatFormat("6.3g") # specified as c printf options
+    lhcbStyle.SetOptTitle(0)
+    lhcbStyle.SetOptFit(0)
+    #lhcbStyle.SetOptFit(1011) # order is probability, Chi2, errors, parameters
+    #titles
+    lhcbStyle.SetTitleOffset(0.85,"X")
+    lhcbStyle.SetTitleOffset(0.85,"Y")
+    lhcbStyle.SetTitleOffset(1.2,"Z")
+    lhcbStyle.SetTitleFillColor(0)
+    lhcbStyle.SetTitleStyle(0)
+    lhcbStyle.SetTitleBorderSize(0)
+    lhcbStyle.SetTitleFont(lhcbFont,"title")
+    lhcbStyle.SetTitleX(0.0)
+    lhcbStyle.SetTitleY(1.0) 
+    lhcbStyle.SetTitleW(1.0)
+    lhcbStyle.SetTitleH(0.05)
+  
+    # look of the statistics box:
+    lhcbStyle.SetStatBorderSize(0)
+    lhcbStyle.SetStatFont(lhcbFont)
+    lhcbStyle.SetStatFontSize(0.05)
+    lhcbStyle.SetStatX(0.9)
+    lhcbStyle.SetStatY(0.9)
+    lhcbStyle.SetStatW(0.25)
+    lhcbStyle.SetStatH(0.15)
+    
+    # put tick marks on top and RHS of plots
+    lhcbStyle.SetPadTickX(1)
+    lhcbStyle.SetPadTickY(1)
+    
+    # histogram divisions: only 5 in x to avoid label overlaps
+    lhcbStyle.SetNdivisions(505,"x")
+    lhcbStyle.SetNdivisions(510,"y")
+    
+    gROOT.SetStyle("lhcbStyle")
+    return
+ 
diff --git a/checker/tracking/python_scripts/Legend.py b/checker/tracking/python_scripts/Legend.py
new file mode 100644
index 0000000000000000000000000000000000000000..6366403950605fae2ed603dfd5f55dfa602c2ea8
--- /dev/null
+++ b/checker/tracking/python_scripts/Legend.py
@@ -0,0 +1,114 @@
+import ROOT
+import itertools
+
+# Some convenience function to easily iterate over the parts of the collections
+
+
+# Needed if importing this script from another script in case TMultiGraphs are used
+#ROOT.SetMemoryPolicy(ROOT.kMemoryStrict)
+
+
+# Start a bit right of the Yaxis and above the Xaxis to not overlap with the ticks
+start, stop = 0.18, 0.89
+x_width, y_width = 0.3, 0.2
+PLACES = [(start, stop - y_width, start + x_width, stop),  # top left opt
+          (start, start, start + x_width, start + y_width),  # bottom left opt
+          (stop - x_width, stop - y_width, stop, stop),  # top right opt
+          (stop - x_width, start, stop, start + y_width),  # bottom right opt
+          (stop - x_width, 0.5 - y_width / 2, stop, 0.5 + y_width / 2),  # right
+          (start, 0.5 - y_width / 2, start + x_width, 0.5 + y_width / 2)]  # left
+
+
+def transform_to_user(canvas, x1, y1, x2, y2):
+    """
+    Transforms from Pad coordinates to User coordinates.
+
+    This can probably be replaced by using the built-in conversion commands.
+    """
+    xstart = canvas.GetX1()
+    xlength = canvas.GetX2() - xstart
+    xlow = xlength * x1 + xstart
+    xhigh = xlength * x2 + xstart
+    if canvas.GetLogx():
+        xlow = 10**xlow
+        xhigh = 10**xhigh
+
+    ystart = canvas.GetY1()
+    ylength = canvas.GetY2() - ystart
+    ylow = ylength * y1 + ystart
+    yhigh = ylength * y2 + ystart
+    if canvas.GetLogy():
+        ylow = 10**ylow
+        yhigh = 10**yhigh
+
+    return xlow, ylow, xhigh, yhigh
+
+
+def overlap_h(hist, x1, y1, x2, y2):
+    xlow = hist.FindFixBin(x1)
+    xhigh = hist.FindFixBin(x2)
+    for i in range(xlow, xhigh + 1):
+        val = hist.GetBinContent(i)
+        # Values
+        if y1 <= val <= y2:
+            return True
+        # Errors
+        if val + hist.GetBinErrorUp(i) > y1 and val - hist.GetBinErrorLow(i) < y2:
+            # print "Overlap with histo", hist.GetName(), "at bin", i
+            return True
+    return False
+
+
+def overlap_rect(rect1, rect2):
+    """Do the two rectangles overlap?"""
+    if rect1[0] > rect2[2] or rect1[2] < rect2[0]:
+        return False
+    if rect1[1] > rect2[3] or rect1[3] < rect2[1]:
+        return False
+    return True
+
+def overlap_g(graph, x1, y1, x2, y2):
+    x_values = list(graph.GetX())
+    y_values = list(graph.GetY())
+    x_err = list(graph.GetEX()) or [0] * len(x_values)
+    y_err = list(graph.GetEY()) or [0] * len(y_values)
+
+    for x, ex, y, ey in zip(x_values, x_err, y_values, y_err):
+        # Could maybe be less conservative
+        if overlap_rect((x1, y1, x2, y2), (x - ex, y - ey, x + ex, y + ey)):
+            # print "Overlap with graph", graph.GetName(), "at point", (x, y)
+            return True
+    return False
+
+def place_legend(canvas, x1=None, y1=None, x2=None, y2=None, header="", option="LP"):
+    # If position is specified, use that
+    if all(x is not None for x in (x1, x2, y1, y2)):
+        return canvas.BuildLegend(x1, y1, x2, y2, header, option)
+
+    # Make sure all objects are correctly registered
+    canvas.Update()
+
+    # Build a list of objects to check for overlaps
+    objects = []
+    for x in canvas.GetListOfPrimitives():
+        if isinstance(x, ROOT.TH1) or isinstance(x, ROOT.TGraph):
+            objects.append(x)
+        elif isinstance(x, ROOT.THStack) or isinstance(x, ROOT.TMultiGraph):
+            objects.extend(x)
+
+    for place in PLACES:
+        place_user = canvas.PadtoU(*place)
+        # Make sure there are no overlaps
+        if any(obj.Overlap(*place_user) for obj in objects):
+            continue
+        return canvas.BuildLegend(place[0], place[1], place[2], place[3], header, option)
+    # As a fallback, use the default values, taken from TCanvas::BuildLegend
+    return canvas.BuildLegend(0.5, 0.67, 0.88, 0.88, header, option)
+
+# Monkey patch ROOT objects to make it all work
+ROOT.THStack.__iter__ = lambda self: iter(self.GetHists())
+ROOT.TMultiGraph.__iter__ = lambda self: iter(self.GetListOfGraphs())
+ROOT.TH1.Overlap = overlap_h
+ROOT.TGraph.Overlap = overlap_g
+ROOT.TPad.PadtoU = transform_to_user
+ROOT.TPad.PlaceLegend = place_legend
diff --git a/checker/tracking/python_scripts/efficiency_plots.py b/checker/tracking/python_scripts/efficiency_plots.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0a6cb8baedd1a0a4b0c49bb11f43ee61d5276d3
--- /dev/null
+++ b/checker/tracking/python_scripts/efficiency_plots.py
@@ -0,0 +1,147 @@
+#!/usr/bin/python
+
+# Script for accessing histograms of reconstructible and
+# reconstructed tracks for different tracking categories
+# created by PrChecker2
+#
+# The efficency is calculated usig TGraphAsymmErrors
+# and Bayesian error bars
+#
+# author: Dorothea vom Bruch (dorothea.vom.bruch@cern.ch)
+# date:   10/2018
+# 
+
+import os,sys 
+import argparse 
+import ROOT
+from ROOT import *
+from ROOT import gStyle
+from ROOT import gROOT
+from ROOT import TStyle
+from ROOT import gPad
+
+from LHCbStyle import *
+from ConfigHistos import *
+from Legend import *
+
+
+def getEfficiencyHistoNames() :
+    return ["eta", "p", "pt", "phi", "nPV"]
+    
+def getTrackers() :
+    return ["Velo", "Upstream", "Forward"]
+    
+def getGhostHistoNames() :
+    #return ["eta", "nPV"] # currently no eta information available from track
+    return ["nPV"]
+
+f = ROOT.TFile.Open("../../../output/PrCheckerPlots.root", "read")
+outputfile = ROOT.TFile( "efficiency_plots.root", "recreate" )
+
+setLHCbStyle()
+
+efficiencyHistoDict = efficiencyHistoDict()
+efficiencyHistos    = getEfficiencyHistoNames()
+ghostHistos         = getGhostHistoNames()
+ghostHistoDict      = ghostHistoDict()
+categories          = categoriesDict()
+cuts                = getCuts()
+trackers            = getTrackers()
+
+for tracker in trackers :
+    outputfile.cd()
+    trackerDir = outputfile.mkdir(tracker)
+    trackerDir.cd()
+    
+    for cut in cuts[tracker]:
+        cutDir = trackerDir.mkdir(cut)
+        cutDir.cd()
+        histoBaseName = tracker + "/" + cut + "_"
+
+        # calculate efficiency
+        for histo in efficiencyHistos:
+            title = "efficiency vs. " + histo + ", " + categories[tracker][cut]["title"]
+            name = "efficiency vs. " + histo
+            canvas = ROOT.TCanvas(name, title)
+            ROOT.gPad.SetTicks()
+            # get efficiency for not electrons category
+            histoName       = histoBaseName + "notElectrons_" + efficiencyHistoDict[histo]["variable"]
+            print "not electrons: " + histoName
+            numeratorName   = histoName + "_reconstructed"
+            numerator       = f.Get(numeratorName)
+            denominatorName = histoName + "_reconstructible"
+            denominator     = f.Get(denominatorName)
+            print numerator.GetEntries()
+            print denominator.GetEntries()
+            if numerator.GetEntries() == 0 or denominator.GetEntries() == 0 :
+                continue
+            numerator.Sumw2()
+            denominator.Sumw2()
+        
+            g_efficiency_notElectrons = ROOT.TGraphAsymmErrors()
+            g_efficiency_notElectrons.Divide(numerator, denominator, "cl=0.683 b(1,1) mode")
+            g_efficiency_notElectrons.SetTitle("not electrons")
+           
+            # get efficiency for electrons category
+            if categories[tracker][cut]["plotElectrons"] : 
+                histoName       = histoBaseName + "electrons_" + efficiencyHistoDict[histo]["variable"]
+                print "electrons: " + histoName
+                numeratorName   = histoName + "_reconstructed"
+                numerator       = f.Get(numeratorName)
+                denominatorName = histoName + "_reconstructible"
+                denominator     = f.Get(denominatorName)
+                if numerator.GetEntries() == 0 or denominator.GetEntries() == 0 :
+                     continue
+                numerator.Sumw2()
+                denominator.Sumw2()
+                
+                g_efficiency_electrons = ROOT.TGraphAsymmErrors()
+                g_efficiency_electrons.Divide(numerator, denominator, "cl=0.683 b(1,1) mode")
+                g_efficiency_electrons.SetTitle("electrons")
+                g_efficiency_electrons.SetMarkerColor(kAzure-3)
+                g_efficiency_electrons.SetLineColor(kAzure-3)
+                
+            # draw them both
+            mg = TMultiGraph()
+            mg.Add(g_efficiency_notElectrons)
+            if categories[tracker][cut]["plotElectrons"] :
+                mg.Add(g_efficiency_electrons)
+                        
+            mg.Draw("ap")
+            xtitle = efficiencyHistoDict[histo]["xTitle"]
+            mg.GetXaxis().SetTitle(xtitle)
+            mg.GetYaxis().SetTitle("efficiency")
+            mg.GetYaxis().SetRangeUser(0,1)
+            
+            if categories[tracker][cut]["plotElectrons"] :
+                canvas.PlaceLegend()
+            canvas.Write()
+
+    # calculate ghost rate
+    histoBaseName = tracker + "/"
+    for histo in ghostHistos :
+        trackerDir.cd()
+        title = "ghost rate vs " + histo
+        canvas = ROOT.TCanvas(title, title)
+        ROOT.gPad.SetTicks()
+        numeratorName   = histoBaseName + ghostHistoDict[histo]["variable"] + "_Ghosts"
+        denominatorName = histoBaseName + ghostHistoDict[histo]["variable"] + "_Total"
+        print "ghost histo: " + histoBaseName
+        numerator       = f.Get(numeratorName)
+        denominator     = f.Get(denominatorName)
+        numerator.Sumw2()
+        denominator.Sumw2()
+        
+        g_efficiency = ROOT.TGraphAsymmErrors()
+        g_efficiency.Divide(numerator, denominator, "cl=0.683 b(1,1) mode")
+        
+        xtitle = ghostHistoDict[histo]["xTitle"]
+        g_efficiency.GetXaxis().SetTitle(xtitle)
+        g_efficiency.GetYaxis().SetTitle("ghost rate")
+        g_efficiency.Draw("ap")
+        
+        canvas.Write()
+
+outputfile.Write()
+outputfile.Close()
+f.Close()
diff --git a/checker/tracking/python_scripts/efficiency_plots.root b/checker/tracking/python_scripts/efficiency_plots.root
new file mode 100644
index 0000000000000000000000000000000000000000..ed4af0c572e464768dbf38ac85cc15afeb771ec6
Binary files /dev/null and b/checker/tracking/python_scripts/efficiency_plots.root differ
diff --git a/checker/tracking/src/CategoriesTrackChecker.cpp b/checker/tracking/src/CategoriesTrackChecker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d14003cf8cdeca6c192193480d23d7472ac8404
--- /dev/null
+++ b/checker/tracking/src/CategoriesTrackChecker.cpp
@@ -0,0 +1,395 @@
+#include <cstdio>
+
+#include "TrackChecker.h"
+
+
+void TrackCheckerVelo::SetCategories() {
+  m_categories = {{ // define which categories to monitor
+     TrackEffReport({ "Electrons long eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Electrons long fromB eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Electrons long fromB eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Electrons long fromB eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Electrons long fromB eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Electrons long fromD eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Electrons long fromD eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Electrons long fromD eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Electrons long fromD eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Electrons long eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Electrons long eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Electrons long eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Electrons long strange eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Electrons long strange eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Electrons long strange eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Electrons long strange eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Electrons Velo",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && mcp.isElectron(); },
+        }),
+     TrackEffReport({ "Electrons Velo backward",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && mcp.isElectron() && mcp.eta < 0; },
+        }),
+     TrackEffReport({ "Electrons Velo forward",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && mcp.isElectron() && mcp.eta > 0; },
+        }),
+     TrackEffReport({ "Electrons Velo eta25",
+        [] (const MCParticles::const_reference& mcp)
+          { return mcp.hasVelo && mcp.isElectron() && mcp.inEta2_5(); },
+        }), 
+     TrackEffReport({ "Not electron long eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Not electron long fromB eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Not electron long fromB eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Not electron long fromB eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Not electron long fromB eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Not electron long fromD eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Not electron long fromD eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Not electron long fromD eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Not electron long fromD eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Not electron long eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Not electron long eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Not electron long eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Not electron long strange eta25",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+     TrackEffReport({ "Not electron long strange eta25 p<5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
+        }),
+     TrackEffReport({ "Not electron long strange eta25 p>3GeV pt>400MeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
+        }),
+     TrackEffReport({ "Not electron long strange eta25 p>5GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
+        }),
+     TrackEffReport({ "Not electron Velo",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && !mcp.isElectron(); },
+        }),
+     TrackEffReport({ "Not electron Velo backward",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && !mcp.isElectron() && mcp.eta < 0; },
+        }),
+     TrackEffReport({ "Not electron Velo forward",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && !mcp.isElectron() && mcp.eta > 0; },
+        }),
+     TrackEffReport({ "Not electron Velo eta25",
+        [] (const MCParticles::const_reference& mcp)
+          { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
+        })
+    }};
+};
+ 
+void TrackCheckerVelo::SetHistoCategories() {
+  m_histo_categories = {{ // define which categories to create histograms for
+     HistoCategory({ "VeloTracks_electrons",
+        [] (const MCParticles::const_reference& mcp)
+          { return mcp.hasVelo && mcp.isElectron(); },
+           }) ,
+     HistoCategory({ "VeloTracks_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongFromB_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongFromD_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongStrange_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "VeloTracks_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+          { return mcp.hasVelo && !mcp.isElectron(); },
+           }) ,
+     HistoCategory({ "VeloTracks_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongFromB_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongFromD_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+           }),
+     HistoCategory({ "LongStrange_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+           })
+    }};
+};
+
+void TrackCheckerVeloUT::SetCategories() {
+  m_categories = {{ // define which categories to monitor
+    TrackEffReport({ "Velo",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+    TrackEffReport({ "Velo+UT",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+    TrackEffReport({ "Velo+UT, p > 5 GeV",
+	[] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	}),
+    TrackEffReport({ "Velo, not long",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && !mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+    TrackEffReport({ "Velo+UT, not long",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && !mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Velo+UT, not long, p > 5 GeV",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && !mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long, p > 5 GeV",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long from B",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long from B, p > 5 GeV",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    TrackEffReport({ "Long from B electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    TrackEffReport({ "Long from B electrons, p > 5 GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
+          }),
+    TrackEffReport({ "Long from B, p > 3 GeV, pt > 0.5 GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 3e3 && mcp.pt > 0.5e3 && mcp.inEta2_5(); },
+          })
+    }}; 
+}; 
+
+void TrackCheckerVeloUT::SetHistoCategories() {
+  m_histo_categories = {{ // define which categories to create histograms for 
+    HistoCategory({ "VeloUTTracks_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && mcp.isElectron() && mcp.inEta2_5(); },
+        }),
+    HistoCategory({ "LongFromB_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "LongFromD_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "LongStrange_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "VeloUTTracks_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+	  { return mcp.hasVelo && mcp.hasUT && !mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "LongFromB_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "LongFromD_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    HistoCategory({ "LongStrange_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+          })
+    }};
+};
+
+void TrackCheckerForward::SetCategories() {
+  m_categories = {{ // define which categories to monitor
+      TrackEffReport({ "Long",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long, p > 5 GeV",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long strange",
+          [] (const MCParticles::const_reference& mcp)
+            { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+          }),
+    TrackEffReport({ "Long strange, p > 5 GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
+        }),
+    TrackEffReport({ "Long from B",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long from B, p > 5 GeV",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long electrons",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long electrons from B",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long electrons from B, p > 5 GeV",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+    TrackEffReport({ "Long from B, p > 3 GeV, pt > 0.5 GeV",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 3e3 && mcp.pt > 0.5e3 && mcp.inEta2_5(); },
+          })
+    }}; 
+};   
+
+void TrackCheckerForward::SetHistoCategories() {
+  m_histo_categories = {{ // define which categories to create histograms for
+      HistoCategory({ "Long_eta25_electrons",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
+	  }),
+      HistoCategory({ "LongFromB_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "LongFromD_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "LongStrange_eta25_electrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "Long_eta25_notElectrons",
+	  [] (const MCParticles::const_reference& mcp)
+	    { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "LongFromB_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "LongFromD_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+            }),
+      HistoCategory({ "LongStrange_eta25_notElectrons",
+        [] (const MCParticles::const_reference& mcp)
+        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
+            })
+    }};
+};
+ 
diff --git a/checker/tracking/src/TrackChecker.cpp b/checker/tracking/src/TrackChecker.cpp
index 9f99f3ed3e89b65a61f6867d8331f850e035b2da..cab9248ead8d8fc2eb7e5c0d7a5762fb0dfb6cc9 100644
--- a/checker/tracking/src/TrackChecker.cpp
+++ b/checker/tracking/src/TrackChecker.cpp
@@ -7,8 +7,8 @@
  * @author Manuel Schiller
  * @date 2018-02-19
  *
- * 2018-07 Dorothea vom Bruch: updated to run over different track types, 
- * use exact same categories as PrChecker2, 
+ * 2018-07 Dorothea vom Bruch: updated to run over different track types,
+ * use exact same categories as PrChecker2,
  * take input from Renato Quagliani's TrackerDumper
  */
 
@@ -16,465 +16,272 @@
 
 #include "TrackChecker.h"
 
-void TrackCheckerVelo::SetCategories() {
-  m_categories = {{ // define which categories to monitor
-    // Renato's categories
-     TrackEffReport({ "Electrons long eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Electrons long fromB eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Electrons long fromB eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Electrons long fromB eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Electrons long fromB eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Electrons long fromD eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Electrons long fromD eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Electrons long fromD eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Electrons long fromD eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Electrons long eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Electrons long eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Electrons long eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Electrons long strange eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Electrons long strange eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Electrons long strange eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Electrons long strange eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Electrons Velo",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && mcp.isElectron(); },
-        }),
-     TrackEffReport({ "Electrons Velo backward",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && mcp.isElectron() && mcp.eta < 0; },
-        }),
-     TrackEffReport({ "Electrons Velo forward",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && mcp.isElectron() && mcp.eta > 0; },
-        }),
-     TrackEffReport({ "Electrons Velo eta25",
-        [] (const MCParticles::const_reference& mcp)
-          { return mcp.hasVelo && mcp.isElectron() && mcp.inEta2_5(); },
-        }), 
-     TrackEffReport({ "Not electron long eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Not electron long fromB eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Not electron long fromB eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Not electron long fromB eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Not electron long fromB eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5() &&  mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Not electron long fromD eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Not electron long fromD eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Not electron long fromD eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Not electron long fromD eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromCharmDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Not electron long eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Not electron long eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Not electron long eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Not electron long strange eta25",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-     TrackEffReport({ "Not electron long strange eta25 p<5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p < 5e3; },
-        }),
-     TrackEffReport({ "Not electron long strange eta25 p>3GeV pt>400MeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 3e3 && mcp.pt > 400; },
-        }),
-     TrackEffReport({ "Not electron long strange eta25 p>5GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5() && mcp.p > 5e3; },
-        }),
-     TrackEffReport({ "Not electron Velo",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && !mcp.isElectron(); },
-        }),
-     TrackEffReport({ "Not electron Velo backward",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && !mcp.isElectron() && mcp.eta < 0; },
-        }),
-     TrackEffReport({ "Not electron Velo forward",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.hasVelo && !mcp.isElectron() && mcp.eta > 0; },
-        }),
-     TrackEffReport({ "Not electron Velo eta25",
-        [] (const MCParticles::const_reference& mcp)
-          { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
-        })
-     
-
-    
-    // currently implemented in PrChecker2 (master branch)
-    // TrackEffReport({ "Velo",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long strange",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long strange, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long from B",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long from B, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //       }),
-    // TrackEffReport({ "Long electrons",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
-    //       }),
-    // TrackEffReport({ "Long from B electrons",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
-    //       }),
-    // TrackEffReport({ "Long from B electrons, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //       }),
-    // TrackEffReport({ "Long from B, p > 3 GeV, pt > 0.5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 3e3 && mcp.pt > 0.5e3 && mcp.inEta2_5(); },
-    //       })
-
-
-
-    
-    // TrackEffReport({ "Long from D",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.isFromD && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Long from D, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isLong && mcp.isFromD && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Down",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isDown && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Down, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isDown && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Down strange",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isStrangeDown && !mcp.isElectron() && mcp.inEta2_5(); },
-    //     }),
-    // TrackEffReport({ "Down strange, p > 5 GeV",
-    //     [] (const MCParticles::const_reference& mcp)
-    //     { return mcp.isStrangeDown && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-    // 	})
-    }};
-};
- 
-
-void TrackCheckerVeloUT::SetCategories() {
-  m_categories = {{ // define which categories to monitor
-    TrackEffReport({ "Velo",
-        [] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-    TrackEffReport({ "Velo+UT",
-        [] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && mcp.hasUT && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-    TrackEffReport({ "Velo+UT, p > 5 GeV",
-	[] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && mcp.hasUT && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	}),
-    TrackEffReport({ "Velo, not long",
-        [] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && !mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-        }),
-    TrackEffReport({ "Velo+UT, not long",
-        [] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && mcp.hasUT && !mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Velo+UT, not long, p > 5 GeV",
-        [] (const MCParticles::const_reference& mcp)
-	  { return mcp.hasVelo && mcp.hasUT && !mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long, p > 5 GeV",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long from B",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long from B, p > 5 GeV",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long electrons",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
-          }),
-    TrackEffReport({ "Long from B electrons",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
-          }),
-    TrackEffReport({ "Long from B electrons, p > 5 GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-          }),
-    TrackEffReport({ "Long from B, p > 3 GeV, pt > 0.5 GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 3e3 && mcp.pt > 0.5e3 && mcp.inEta2_5(); },
-          })
-    }}; 
-}; 
-
-
-void TrackCheckerForward::SetCategories() {
-  m_categories = {{ // define which categories to monitor
-      TrackEffReport({ "Long",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long, p > 5 GeV",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long strange",
-          [] (const MCParticles::const_reference& mcp)
-            { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-          }),
-    TrackEffReport({ "Long strange, p > 5 GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromStrangeDecay && !mcp.isElectron() && mcp.p > 5e3 && mcp.inEta2_5(); },
-        }),
-    TrackEffReport({ "Long from B",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long from B, p > 5 GeV",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && !mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long electrons",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long electrons from B",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long electrons from B, p > 5 GeV",
-	  [] (const MCParticles::const_reference& mcp)
-	    { return mcp.isLong && mcp.fromBeautyDecay && mcp.p > 5e3 && mcp.isElectron() && mcp.inEta2_5(); },
-	  }),
-    TrackEffReport({ "Long from B, p > 3 GeV, pt > 0.5 GeV",
-        [] (const MCParticles::const_reference& mcp)
-        { return mcp.isLong && mcp.fromBeautyDecay && !mcp.isElectron() && mcp.p > 3e3 && mcp.pt > 0.5e3 && mcp.inEta2_5(); },
-          })
-    }}; 
-};   
-
-TrackChecker::~TrackChecker()
-{
+TrackChecker::~TrackChecker() {
   std::printf("%-50s: %9lu/%9lu %6.2f%% (%6.2f%%) ghosts\n",
-      "TrackChecker output",
-      m_nghosts, m_ntracks,
-      100.f * float(m_nghosts) / float(m_ntracks),
-      100.f * m_ghostperevent);
+              "TrackChecker output", m_nghosts, m_ntracks,
+              100.f * float(m_nghosts) / float(m_ntracks),
+              100.f * m_ghostperevent);
   m_categories.clear();
   std::printf("\n");
+
+  // write histograms to file
+#ifdef WITH_ROOT
+  const std::string name = "../output/PrCheckerPlots.root";
+  TFile *f = new TFile(name.c_str(), "UPDATE");
+  std::string dirName = m_trackerName;
+  if (m_trackerName == "VeloUT")
+    dirName = "Upstream";
+  TDirectory *trackerDir = f->mkdir(dirName.c_str());
+  trackerDir->cd();
+  for (auto histo : histos.h_reconstructible_eta)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructible_p)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructible_pt)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructible_phi)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructible_nPV)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructed_eta)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructed_p)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructed_pt)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructed_phi)
+    histo.second.Write();
+  for (auto histo : histos.h_reconstructed_nPV)
+    histo.second.Write();
+  histos.h_ghost_nPV.Write();
+  histos.h_total_nPV.Write();
+
+  f->Write();
+  f->Close();
+#endif
 }
 
-void TrackChecker::TrackEffReport::operator()(const MCParticles& mcps)
-{
-  for (auto mcp: mcps) {
+void TrackChecker::TrackEffReport::operator()(const MCParticles &mcps) {
+  // find number of MCPs within category
+  for (auto mcp : mcps) {
     if (m_accept(mcp)) {
       ++m_naccept, ++m_nacceptperevt;
     }
   }
 }
 
-void TrackChecker::TrackEffReport::operator()(
-					    const trackChecker::Tracks::const_reference& track,
-					    const MCParticles::const_reference& mcp,
-					    const float weight)
-{
+void TrackChecker::TrackEffReport::
+operator()(const trackChecker::Tracks::const_reference &track,
+           const MCParticles::const_reference &mcp, const float weight) {
 
-  if (!m_accept(mcp)) return;
+  if (!m_accept(mcp))
+    return;
   if (!m_keysseen.count(mcp.key)) {
     ++m_nfound, ++m_nfoundperevt;
     m_keysseen.insert(mcp.key);
   } else {
     ++m_nclones;
   }
-  
+
   // update purity
   m_hitpur *= float(m_nfound + m_nclones - 1) / float(m_nfound + m_nclones);
   m_hitpur += weight / float(m_nfound + m_nclones);
   // update hit efficiency
-  //auto hiteff = track.numHits * weight / float(mcp.numHits);
   auto hiteff = track.n_matched_total * weight / float(mcp.numHits);
   m_hiteff *= float(m_nfound + m_nclones - 1) / float(m_nfound + m_nclones);
   m_hiteff += hiteff / float(m_nfound + m_nclones);
-
 }
 
-void TrackChecker::TrackEffReport::evtEnds()
-{
+void TrackChecker::TrackEffReport::evtEnds() {
   m_keysseen.clear();
   if (m_nacceptperevt) {
     m_effperevt *= float(m_nevents) / float(m_nevents + 1);
     ++m_nevents;
-    m_effperevt += (float(m_nfoundperevt) / float(m_nacceptperevt)) / float(m_nevents);
+    m_effperevt +=
+        (float(m_nfoundperevt) / float(m_nacceptperevt)) / float(m_nevents);
   }
   m_nfoundperevt = m_nacceptperevt = 0;
 }
 
-TrackChecker::TrackEffReport::~TrackEffReport()
-{
+TrackChecker::TrackEffReport::~TrackEffReport() {
   auto clonerate = 0.f, eff = 0.f;
-  if (m_nfound) clonerate = float(m_nclones) / float(m_nfound + m_nfound);
-  if (m_naccept) eff = float(m_nfound) / float(m_naccept);
+  if (m_nfound)
+    clonerate = float(m_nclones) / float(m_nfound + m_nfound);
+  if (m_naccept)
+    eff = float(m_nfound) / float(m_naccept);
 
   if (m_naccept > 0) {
     std::printf("%-50s: %9lu/%9lu %6.2f%% (%6.2f%%), "
-        "%9lu (%6.2f%%) clones, hit eff %6.2f%% pur %6.2f%%\n",
-        m_name.c_str(), m_nfound, m_naccept,
-        100.f * eff,
-        100.f * m_effperevt, m_nclones,
-        100.f * clonerate,
-        100.f * m_hiteff, 100.f * m_hitpur);
+                "%9lu (%6.2f%%) clones, hit eff %6.2f%% pur %6.2f%%\n",
+                m_name.c_str(), m_nfound, m_naccept, 100.f * eff,
+                100.f * m_effperevt, m_nclones, 100.f * clonerate,
+                100.f * m_hiteff, 100.f * m_hitpur);
   }
 }
 
-void TrackChecker::operator()(const trackChecker::Tracks& tracks,
-    const MCAssociator& mcassoc, const MCParticles& mcps)
+void TrackChecker::HistoCategory::evtEnds() {
+  m_keysseen.clear();
+}
+
+void TrackChecker::Histos::initHistos(
+  const std::vector<HistoCategory>& histo_categories)
 {
+#ifdef WITH_ROOT
+  // histos for efficiency
+  for (auto histoCat : histo_categories) {
+    const std::string &category = histoCat.m_name;
+    std::string name = category + "_Eta_reconstructible";
+    if (category.find("eta25") != std::string::npos) {
+      h_reconstructible_eta[name] =
+          TH1D(name.c_str(), name.c_str(), 50, 0., 7.);
+      name = category + "_Eta_reconstructed";
+      h_reconstructed_eta[name] =
+          TH1D(name.c_str(), name.c_str(), 50, 0., 7.);
+    } else {
+      h_reconstructible_eta[name] =
+          TH1D(name.c_str(), name.c_str(), 100, -7., 7.);
+      name = category + "_Eta_reconstructed";
+      h_reconstructed_eta[name] =
+          TH1D(name.c_str(), name.c_str(), 100, -7., 7.);
+    }
+    name = category + "_P_reconstructible";
+    h_reconstructible_p[name] =
+        TH1D(name.c_str(), name.c_str(), 50, 0., 100000.);
+    name = category + "_Pt_reconstructible";
+    h_reconstructible_pt[name] =
+        TH1D(name.c_str(), name.c_str(), 50, 0., 100000.);
+    name = category + "_Phi_reconstructible";
+    h_reconstructible_phi[name] =
+        TH1D(name.c_str(), name.c_str(), 25, -3.142, 3.142);
+    name = category + "_nPV_reconstructible";
+    h_reconstructible_nPV[name] =
+        TH1D(name.c_str(), name.c_str(), 21, -0.5, 20.5);
+    name = category + "_P_reconstructed";
+    h_reconstructed_p[name] =
+        TH1D(name.c_str(), name.c_str(), 50, 0., 100000.);
+    name = category + "_Pt_reconstructed";
+    h_reconstructed_pt[name] =
+        TH1D(name.c_str(), name.c_str(), 50, 0., 100000.);
+    name = category + "_Phi_reconstructed";
+    h_reconstructed_phi[name] =
+        TH1D(name.c_str(), name.c_str(), 25, -3.142, 3.142);
+    name = category + "_nPV_reconstructed";
+    h_reconstructed_nPV[name] =
+        TH1D(name.c_str(), name.c_str(), 21, -0.5, 20.5);
+  }
+
+  // histos for ghost rate
+  h_ghost_nPV = TH1D("nPV_Ghosts", "nPV_Ghosts", 21, -0.5, 20.5);
+  h_total_nPV = TH1D("nPV_Total", "nPV_Total", 21, -0.5, 20.5);
+#endif
+}
+
+void TrackChecker::Histos::fillReconstructibleHistos(
+    const MCParticles &mcps, const HistoCategory &category) {
+#ifdef WITH_ROOT
+  const std::string eta_name = category.m_name + "_Eta_reconstructible";
+  const std::string p_name = category.m_name + "_P_reconstructible";
+  const std::string pt_name = category.m_name + "_Pt_reconstructible";
+  const std::string phi_name = category.m_name + "_Phi_reconstructible";
+  const std::string nPV_name = category.m_name + "_nPV_reconstructible";
+  for (auto mcp : mcps) {
+    if (category.m_accept(mcp)) {
+      h_reconstructible_eta[eta_name].Fill(mcp.eta);
+      h_reconstructible_p[p_name].Fill(mcp.p);
+      h_reconstructible_pt[pt_name].Fill(mcp.pt);
+      h_reconstructible_phi[phi_name].Fill(mcp.phi);
+      h_reconstructible_nPV[nPV_name].Fill(mcp.nPV);
+    }
+  }
+#endif
+}
+
+void TrackChecker::Histos::fillReconstructedHistos(const MCParticle &mcp,
+                                                   HistoCategory &category) {
+#ifdef WITH_ROOT
+  if (!(category.m_accept(mcp)))
+    return;
+  if ((category.m_keysseen).count(mcp.key))
+    return;                              // clone track
+  (category.m_keysseen).insert(mcp.key); // not clone track, mark as matched
+
+  const std::string eta_name = category.m_name + "_Eta_reconstructed";
+  const std::string p_name = category.m_name + "_P_reconstructed";
+  const std::string pt_name = category.m_name + "_Pt_reconstructed";
+  const std::string phi_name = category.m_name + "_Phi_reconstructed";
+  const std::string nPV_name = category.m_name + "_nPV_reconstructed";
+  h_reconstructed_eta[eta_name].Fill(mcp.eta);
+  h_reconstructed_p[p_name].Fill(mcp.p);
+  h_reconstructed_pt[pt_name].Fill(mcp.pt);
+  h_reconstructed_phi[phi_name].Fill(mcp.phi);
+  h_reconstructed_nPV[nPV_name].Fill(mcp.nPV);
+#endif
+}
+
+void TrackChecker::Histos::fillTotalHistos(const MCParticle &mcp) {
+#ifdef WITH_ROOT
+  h_total_nPV.Fill(mcp.nPV);
+#endif
+}
+
+void TrackChecker::Histos::fillGhostHistos(const MCParticle &mcp) {
+#ifdef WITH_ROOT
+  h_ghost_nPV.Fill(mcp.nPV);
+#endif
+}
+
+void TrackChecker::operator()(const trackChecker::Tracks &tracks,
+                              const MCAssociator &mcassoc,
+                              const MCParticles &mcps) {
   // register MC particles
-  for (auto& report: m_categories) report(mcps);
+  for (auto &report : m_categories)
+    report(mcps);
+  // fill histograms of reconstructible MC particles in various categories
+  for (auto &histo_cat : m_histo_categories) {
+    histos.fillReconstructibleHistos(mcps, histo_cat);
+  }
+
   // go through tracks
   const std::size_t ntracksperevt = tracks.size();
   std::size_t nghostsperevt = 0;
-  for (auto track: tracks) {
+  for (auto track : tracks) {
+    histos.fillTotalHistos(mcps[0]);
     // check LHCbIDs for MC association
-    const auto& ids = track.ids();
+    const auto &ids = track.ids();
     const auto assoc = mcassoc(ids.begin(), ids.end(), track.n_matched_total);
     if (!assoc) {
       ++nghostsperevt;
-	  continue;
+      histos.fillGhostHistos(mcps[0]);
+      continue;
     }
     // have MC association, check weight
     const auto weight = assoc.front().second;
-	if (weight < m_minweight) {
+    if (weight < m_minweight) {
       ++nghostsperevt;
+      histos.fillGhostHistos(mcps[0]);
       continue;
     }
     // okay, sufficient to proceed...
     const auto mcp = assoc.front().first;
     // add to various categories
-    for (auto& report: m_categories) {
+    for (auto &report : m_categories) {
       report(track, mcp, weight);
-      
+    }
+    // fill histograms of reconstructible MC particles in various categories
+    for (auto &histo_cat : m_histo_categories) {
+      histos.fillReconstructedHistos(mcp, histo_cat);
     }
   }
   // almost done, notify of end of event...
   ++m_nevents;
-  for (auto& report: m_categories) report.evtEnds();
+  for (auto &report : m_categories)
+    report.evtEnds();
+  for (auto &histo_cat : m_histo_categories)
+    histo_cat.evtEnds();
   m_ghostperevent *= float(m_nevents - 1) / float(m_nevents);
   if (ntracksperevt) {
-    m_ghostperevent += (float(nghostsperevt) / float(ntracksperevt)) / float(m_nevents);
+    m_ghostperevent +=
+        (float(nghostsperevt) / float(ntracksperevt)) / float(m_nevents);
   }
   m_nghosts += nghostsperevt, m_ntracks += ntracksperevt;
 }
diff --git a/checker/tracking/src/velopix-input-reader.cpp b/checker/tracking/src/velopix-input-reader.cpp
index 900e878d4614e36074d1a8e5df1134c52ad1c9b6..b239aaa252e1b321dcf3cc6061d0f8d5a188def3 100644
--- a/checker/tracking/src/velopix-input-reader.cpp
+++ b/checker/tracking/src/velopix-input-reader.cpp
@@ -22,7 +22,6 @@ VelopixEvent::VelopixEvent(const std::vector<char>& event, const std::string& tr
   uint8_t* input = (uint8_t*) event.data();
 
   uint32_t number_mcp = *((uint32_t*)  input); input += sizeof(uint32_t);
-  //debug_cout << "num MCPs = " << number_mcp << std::endl;
   for (uint32_t i=0; i<number_mcp; ++i) {
     MCParticle p;
     p.key               = *((uint32_t*)  input); input += sizeof(uint32_t);
@@ -30,6 +29,7 @@ VelopixEvent::VelopixEvent(const std::vector<char>& event, const std::string& tr
     p.p                 = *((float*)  input); input += sizeof(float);
     p.pt                = *((float*)  input); input += sizeof(float);
     p.eta               = *((float*)  input); input += sizeof(float);
+    p.phi               = *((float*)  input); input += sizeof(float);
     p.isLong            = (bool) *((int8_t*)  input); input += sizeof(int8_t);
     p.isDown            = (bool) *((int8_t*)  input); input += sizeof(int8_t);
     p.hasVelo           = (bool) *((int8_t*)  input); input += sizeof(int8_t);
@@ -38,13 +38,14 @@ VelopixEvent::VelopixEvent(const std::vector<char>& event, const std::string& tr
     p.fromBeautyDecay   = (bool) *((int8_t*)  input); input += sizeof(int8_t);
     p.fromCharmDecay    = (bool) *((int8_t*)  input); input += sizeof(int8_t);
     p.fromStrangeDecay  = (bool) *((int8_t*)  input); input += sizeof(int8_t);
-
+    p.nPV               = *((uint32_t*) input); input += sizeof(uint32_t);
    
     int num_Velo_hits = *((uint32_t*)  input); input += sizeof(uint32_t);
     std::vector<uint32_t> velo_hits;
     std::copy_n((uint32_t*) input, num_Velo_hits, std::back_inserter(velo_hits));
     input += sizeof(uint32_t) * num_Velo_hits;
     int num_UT_hits = *((uint32_t*)  input); input += sizeof(uint32_t);
+    
     std::vector<uint32_t> UT_hits;
     std::copy_n((uint32_t*) input, num_UT_hits, std::back_inserter(UT_hits));
     input += sizeof(uint32_t) * num_UT_hits;
@@ -52,11 +53,7 @@ VelopixEvent::VelopixEvent(const std::vector<char>& event, const std::string& tr
     std::vector<uint32_t> SciFi_hits;
     std::copy_n((uint32_t*) input, num_SciFi_hits, std::back_inserter(SciFi_hits));
     input += sizeof(uint32_t) * num_SciFi_hits;
-
-    // if ( trackType == "Velo" && !p.hasVelo ) continue;
-    // if ( trackType == "VeloUT" && !(p.hasVelo && p.hasUT) ) continue;
-    // if ( trackType == "Forward" && !(p.hasVelo && p.hasUT && p.hasSciFi) ) continue;
-   
+    
     /* Only save the hits relevant for the track type we are checking
        -> get denominator of efficiency right
      */
@@ -142,7 +139,7 @@ std::tuple<bool, std::vector<VelopixEvent>> read_mc_folder (
   const bool checkEvents
 ) {
   std::vector<std::string> folderContents = list_folder(foldername);
-  
+
   uint requestedFiles = number_of_files==0 ? folderContents.size() : number_of_files;
   verbose_cout << "Requested " << requestedFiles << " files" << std::endl;
 
diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..5e3477be1d8380211c28461b96eb7f0aca63cc38
--- /dev/null
+++ b/cmake/FindTBB.cmake
@@ -0,0 +1,292 @@
+# Module for locating Intel's Threading Building Blocks (TBB).
+#
+# Customizable variables:
+#   TBB_ROOT_DIR
+#     Specifies TBB's root directory.
+#
+# Read-only variables:
+#   TBB_FOUND
+#     Indicates whether the library has been found.
+#
+#   TBB_INCLUDE_DIRS
+#      Specifies TBB's include directory.
+#
+#   TBB_LIBRARIES
+#     Specifies TBB libraries that should be passed to target_link_libararies.
+#
+#   TBB_<COMPONENT>_LIBRARIES
+#     Specifies the libraries of a specific <COMPONENT>.
+#
+#   TBB_<COMPONENT>_FOUND
+#     Indicates whether the specified <COMPONENT> was found.
+#
+#
+# Copyright (c) 2012 Sergiu Dotenco
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTTBBLAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+INCLUDE (FindPackageHandleStandardArgs)
+
+IF (CMAKE_VERSION VERSION_GREATER 2.8.7)
+  SET (_TBB_CHECK_COMPONENTS FALSE)
+ELSE (CMAKE_VERSION VERSION_GREATER 2.8.7)
+  SET (_TBB_CHECK_COMPONENTS TRUE)
+ENDIF (CMAKE_VERSION VERSION_GREATER 2.8.7)
+
+FIND_PATH (TBB_ROOT_DIR
+  NAMES include/tbb/tbb.h
+  PATHS ENV TBBROOT
+        ENV TBB40_INSTALL_DIR
+        ENV TBB30_INSTALL_DIR
+        ENV TBB22_INSTALL_DIR
+        ENV TBB21_INSTALL_DIR
+        ENV TBB_ROOT_DIR
+  DOC "TBB root directory")
+
+FIND_PATH (TBB_INCLUDE_DIR
+  NAMES tbb/tbb.h
+  HINTS ${TBB_ROOT_DIR}
+  PATH_SUFFIXES "include" "../include"
+  DOC "TBB include directory" NO_DEFAULT_PATH) #Allow default tbb makefile layout
+
+FIND_PATH (TBB_INCLUDE_DIR
+  NAMES tbb/tbb.h
+  HINTS ${TBB_ROOT_DIR}
+  PATH_SUFFIXES include
+  DOC "TBB include directory" )
+
+IF (MSVC11)
+  SET (_TBB_COMPILER vc11)
+ELSEIF (MSVC10)
+  SET (_TBB_COMPILER vc10)
+ELSEIF (MSVC90)
+  SET (_TBB_COMPILER vc9)
+ELSEIF (MSVC80)
+  SET (_TBB_COMPILER vc8)
+ELSEIF (WIN32)
+  SET (_TBB_COMPILER vc_mt)
+ENDIF (MSVC11)
+
+IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
+  SET (_TBB_POSSIBLE_LIB_SUFFIXES lib/intel64/${_TBB_COMPILER})
+  SET (_TBB_POSSIBLE_BIN_SUFFIXES bin/intel64/${_TBB_COMPILER})
+ELSE (CMAKE_SIZEOF_VOID_P EQUAL 8)
+  SET (_TBB_POSSIBLE_LIB_SUFFIXES lib/ia32/${_TBB_COMPILER})
+  SET (_TBB_POSSIBLE_BIN_SUFFIXES bin/ia32/${_TBB_COMPILER})
+ENDIF (CMAKE_SIZEOF_VOID_P EQUAL 8)
+
+LIST (APPEND _TBB_POSSIBLE_LIB_SUFFIXES lib/$ENV{TBB_ARCH_PLATFORM})
+
+FIND_LIBRARY (TBB_LIBRARY_RELEASE
+  NAMES tbb
+  HINTS ${TBB_ROOT_DIR}
+  PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
+  DOC "TBB release library")
+
+FIND_LIBRARY (TBB_LIBRARY_DEBUG
+  NAMES tbb_debug
+  HINTS ${TBB_ROOT_DIR}
+  PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
+  DOC "TBB debug library")
+
+IF (TBB_LIBRARY_RELEASE AND TBB_LIBRARY_DEBUG)
+  IF (NOT TBB_LIBRARY)
+    SET (TBB_LIBRARY optimized ${TBB_LIBRARY_RELEASE} debug ${TBB_LIBRARY_DEBUG}
+      CACHE DOC "TBB library" FORCE)
+  ENDIF (NOT TBB_LIBRARY)
+ELSEIF (TBB_LIBRARY_RELEASE)
+  IF (NOT TBB_LIBRARY)
+    SET (TBB_LIBRARY ${TBB_LIBRARY_RELEASE} CACHE DOC "TBB library" FORCE)
+  ENDIF (NOT TBB_LIBRARY)
+ENDIF (TBB_LIBRARY_RELEASE AND TBB_LIBRARY_DEBUG)
+
+IF (TBB_LIBRARY_DEBUG)
+  LIST (APPEND _TBB_ALL_LIBS ${TBB_LIBRARY_DEBUG})
+ENDIF (TBB_LIBRARY_DEBUG)
+
+IF (TBB_LIBRARY_RELEASE)
+  LIST (APPEND _TBB_ALL_LIBS ${TBB_LIBRARY_RELEASE})
+ENDIF (TBB_LIBRARY_RELEASE)
+
+FOREACH (_TBB_COMPONENT ${TBB_FIND_COMPONENTS})
+  STRING (TOUPPER ${_TBB_COMPONENT} _TBB_COMPONENT_UPPER)
+  SET (_TBB_LIBRARY_BASE TBB_${_TBB_COMPONENT_UPPER}_LIBRARY)
+
+  IF (${_TBB_COMPONENT} STREQUAL preview)
+    SET (_TBB_LIBRARY_NAME tbb_${_TBB_COMPONENT})
+  ELSE (${_TBB_COMPONENT} STREQUAL preview)
+    SET (_TBB_LIBRARY_NAME tbb${_TBB_COMPONENT})
+  ENDIF (${_TBB_COMPONENT} STREQUAL preview)
+
+  FIND_LIBRARY (${_TBB_LIBRARY_BASE}_RELEASE
+    NAMES ${_TBB_LIBRARY_NAME}
+    HINTS ${TBB_ROOT_DIR}
+    PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
+    DOC "TBB ${_TBB_COMPONENT} release library")
+
+  FIND_LIBRARY (${_TBB_LIBRARY_BASE}_DEBUG
+    NAMES ${_TBB_LIBRARY_NAME}_debug
+    HINTS ${TBB_ROOT_DIR}
+    PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
+    DOC "TBB ${_TBB_COMPONENT} debug library")
+
+  MARK_AS_ADVANCED (${_TBB_LIBRARY_BASE} ${_TBB_LIBRARY_BASE}_DEBUG)
+
+  SET (TBB_${_TBB_COMPONENT_UPPER}_FOUND TRUE)
+
+  IF (${_TBB_LIBRARY_BASE}_DEBUG AND ${_TBB_LIBRARY_BASE}_RELEASE)
+    SET (${_TBB_LIBRARY_BASE}
+      debug ${${_TBB_LIBRARY_BASE}_DEBUG}
+      optimized ${${_TBB_LIBRARY_BASE}_RELEASE} CACHE DOC
+      "TBB ${_TBB_COMPONENT} library")
+  ELSEIF (${_TBB_LIBRARY_BASE}_DEBUG)
+    SET (${_TBB_LIBRARY_BASE} ${${_TBB_LIBRARY_BASE}_DEBUG})
+  ELSEIF (${_TBB_LIBRARY_BASE}_RELEASE)
+    SET (${_TBB_LIBRARY_BASE} ${${_TBB_LIBRARY_BASE}_RELEASE}
+      CACHE DOC "TBB ${_TBB_COMPONENT} library")
+  ELSE (${_TBB_LIBRARY_BASE}_DEBUG AND ${_TBB_LIBRARY_BASE}_RELEASE)
+    # Component missing: record it for a later report
+    LIST (APPEND _TBB_MISSING_COMPONENTS ${_TBB_COMPONENT})
+    SET (TBB_${_TBB_COMPONENT_UPPER}_FOUND FALSE)
+  ENDIF (${_TBB_LIBRARY_BASE}_DEBUG AND ${_TBB_LIBRARY_BASE}_RELEASE)
+
+  IF (${_TBB_LIBRARY_BASE}_DEBUG)
+    LIST (APPEND _TBB_ALL_LIBS ${${_TBB_LIBRARY_BASE}_DEBUG})
+  ENDIF (${_TBB_LIBRARY_BASE}_DEBUG)
+
+  IF (${_TBB_LIBRARY_BASE}_RELEASE)
+    LIST (APPEND _TBB_ALL_LIBS ${${_TBB_LIBRARY_BASE}_RELEASE})
+  ENDIF (${_TBB_LIBRARY_BASE}_RELEASE)
+
+  SET (TBB_${_TBB_COMPONENT}_FOUND ${TBB_${_TBB_COMPONENT_UPPER}_FOUND})
+
+  IF (${_TBB_LIBRARY_BASE})
+    # setup the TBB_<COMPONENT>_LIBRARIES variable
+    SET (TBB_${_TBB_COMPONENT_UPPER}_LIBRARIES ${${_TBB_LIBRARY_BASE}})
+    LIST (APPEND TBB_LIBRARIES ${${_TBB_LIBRARY_BASE}})
+  ELSE (${_TBB_LIBRARY_BASE})
+    LIST (APPEND _TBB_MISSING_LIBRARIES ${_TBB_LIBRARY_BASE})
+  ENDIF (${_TBB_LIBRARY_BASE})
+ENDFOREACH (_TBB_COMPONENT ${TBB_FIND_COMPONENTS})
+
+LIST (APPEND TBB_LIBRARIES ${TBB_LIBRARY})
+SET (TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR})
+
+IF (DEFINED _TBB_MISSING_COMPONENTS AND _TBB_CHECK_COMPONENTS)
+  IF (NOT TBB_FIND_QUIETLY)
+    MESSAGE (STATUS "One or more TBB components were not found:")
+    # Display missing components indented, each on a separate line
+    FOREACH (_TBB_MISSING_COMPONENT ${_TBB_MISSING_COMPONENTS})
+      MESSAGE (STATUS "  " ${_TBB_MISSING_COMPONENT})
+    ENDFOREACH (_TBB_MISSING_COMPONENT ${_TBB_MISSING_COMPONENTS})
+  ENDIF (NOT TBB_FIND_QUIETLY)
+ENDIF (DEFINED _TBB_MISSING_COMPONENTS AND _TBB_CHECK_COMPONENTS)
+
+# Determine library's version
+
+SET (_TBB_VERSION_HEADER ${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h)
+
+IF (EXISTS ${_TBB_VERSION_HEADER})
+  FILE (READ ${_TBB_VERSION_HEADER} _TBB_VERSION_CONTENTS)
+
+  STRING (REGEX REPLACE ".*#define TBB_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1"
+    TBB_VERSION_MAJOR "${_TBB_VERSION_CONTENTS}")
+  STRING (REGEX REPLACE ".*#define TBB_VERSION_MINOR[ \t]+([0-9]+).*" "\\1"
+    TBB_VERSION_MINOR "${_TBB_VERSION_CONTENTS}")
+
+  SET (TBB_VERSION ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR})
+  SET (TBB_VERSION_COMPONENTS 2)
+ENDIF (EXISTS ${_TBB_VERSION_HEADER})
+
+IF (WIN32)
+  FIND_PROGRAM (LIB_EXECUTABLE NAMES lib
+    HINTS "$ENV{VS110COMNTOOLS}/../../VC/bin"
+          "$ENV{VS100COMNTOOLS}/../../VC/bin"
+          "$ENV{VS90COMNTOOLS}/../../VC/bin"
+          "$ENV{VS71COMNTOOLS}/../../VC/bin"
+          "$ENV{VS80COMNTOOLS}/../../VC/bin"
+    DOC "Library manager")
+
+  MARK_AS_ADVANCED (LIB_EXECUTABLE)
+ENDIF (WIN32)
+
+MACRO (GET_LIB_REQUISITES LIB REQUISITES)
+  IF (LIB_EXECUTABLE)
+    GET_FILENAME_COMPONENT (_LIB_PATH ${LIB_EXECUTABLE} PATH)
+
+    IF (MSVC)
+      # Do not redirect the output
+      UNSET (ENV{VS_UNICODE_OUTPUT})
+    ENDIF (MSVC)
+
+    EXECUTE_PROCESS (COMMAND ${LIB_EXECUTABLE} /nologo /list ${LIB}
+      WORKING_DIRECTORY ${_LIB_PATH}/../../Common7/IDE
+      OUTPUT_VARIABLE _LIB_OUTPUT ERROR_QUIET)
+
+    STRING (REPLACE "\n" ";" "${REQUISITES}" "${_LIB_OUTPUT}")
+    LIST (REMOVE_DUPLICATES ${REQUISITES})
+  ENDIF (LIB_EXECUTABLE)
+ENDMACRO (GET_LIB_REQUISITES)
+
+IF (_TBB_ALL_LIBS)
+  # collect lib requisites using the lib tool
+  FOREACH (_TBB_COMPONENT ${_TBB_ALL_LIBS})
+    GET_LIB_REQUISITES (${_TBB_COMPONENT} _TBB_REQUISITES)
+  ENDFOREACH (_TBB_COMPONENT)
+ENDIF (_TBB_ALL_LIBS)
+
+IF (NOT TBB_BINARY_DIR)
+  SET (_TBB_UPDATE_BINARY_DIR TRUE)
+ELSE (NOT TBB_BINARY_DIR)
+  SET (_TBB_UPDATE_BINARY_DIR FALSE)
+ENDIF (NOT TBB_BINARY_DIR)
+
+SET (_TBB_BINARY_DIR_HINTS ${_TBB_POSSIBLE_BIN_SUFFIXES})
+
+IF (_TBB_REQUISITES)
+  FIND_FILE (TBB_BINARY_DIR NAMES ${_TBB_REQUISITES}
+    HINTS ${TBB_ROOT_DIR}
+    PATH_SUFFIXES ${_TBB_BINARY_DIR_HINTS} NO_DEFAULT_PATH)
+ENDIF (_TBB_REQUISITES)
+
+IF (TBB_BINARY_DIR AND _TBB_UPDATE_BINARY_DIR)
+  SET (_TBB_BINARY_DIR ${TBB_BINARY_DIR})
+  UNSET (TBB_BINARY_DIR CACHE)
+
+  IF (_TBB_BINARY_DIR)
+    GET_FILENAME_COMPONENT (TBB_BINARY_DIR ${_TBB_BINARY_DIR} PATH)
+  ENDIF (_TBB_BINARY_DIR)
+ENDIF (TBB_BINARY_DIR AND _TBB_UPDATE_BINARY_DIR)
+
+SET (TBB_BINARY_DIR ${TBB_BINARY_DIR} CACHE PATH "TBB binary directory")
+
+MARK_AS_ADVANCED (TBB_INCLUDE_DIR TBB_LIBRARY TBB_LIBRARY_RELEASE
+  TBB_LIBRARY_DEBUG TBB_BINARY_DIR)
+
+IF (NOT _TBB_CHECK_COMPONENTS)
+ SET (_TBB_FPHSA_ADDITIONAL_ARGS HANDLE_COMPONENTS)
+ENDIF (NOT _TBB_CHECK_COMPONENTS)
+
+IF (CMAKE_VERSION VERSION_GREATER 2.8.2)
+  LIST (APPEND _TBB_FPHSA_ADDITIONAL_ARGS VERSION_VAR TBB_VERSION)
+ENDIF (CMAKE_VERSION VERSION_GREATER 2.8.2)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS (TBB REQUIRED_VARS TBB_ROOT_DIR
+  TBB_INCLUDE_DIR TBB_LIBRARY ${_TBB_MISSING_LIBRARIES}
+  ${_TBB_FPHSA_ADDITIONAL_ARGS})
diff --git a/input/minbias/MC_info/6718861_6001.bin b/input/minbias/MC_info/6718861_6001.bin
index 5d391b35a18087b43617890bbebeedda0ca3c2b3..3f6c4b12080309258e09518277770df98c625b6e 100644
Binary files a/input/minbias/MC_info/6718861_6001.bin and b/input/minbias/MC_info/6718861_6001.bin differ
diff --git a/input/minbias/MC_info/6718861_6002.bin b/input/minbias/MC_info/6718861_6002.bin
index 1b1e6e7c877af292b608c280cf56f102f78a94ce..1c79af2b16a2bb344f1dfa971f6cdadf3570f8f4 100644
Binary files a/input/minbias/MC_info/6718861_6002.bin and b/input/minbias/MC_info/6718861_6002.bin differ
diff --git a/input/minbias/MC_info/6718861_6003.bin b/input/minbias/MC_info/6718861_6003.bin
index 5df02b41f3a66bac7ae6e189387ffed12c0f5f8c..446723c40cbd977a758b841a5d984236444d213e 100644
Binary files a/input/minbias/MC_info/6718861_6003.bin and b/input/minbias/MC_info/6718861_6003.bin differ
diff --git a/input/minbias/MC_info/6718861_6004.bin b/input/minbias/MC_info/6718861_6004.bin
index 5a6d2d153a53d17060f132e3661ddedd02163fbd..48f0472d7731c2f4dc7687adbe1d79d2d4fa9b34 100644
Binary files a/input/minbias/MC_info/6718861_6004.bin and b/input/minbias/MC_info/6718861_6004.bin differ
diff --git a/input/minbias/MC_info/6718861_6005.bin b/input/minbias/MC_info/6718861_6005.bin
index 3e7e5b0980f2c373cc5c501f7082cc0ed8a83082..b6503596bfdb7d9c34b87985368f39d26b1047e5 100644
Binary files a/input/minbias/MC_info/6718861_6005.bin and b/input/minbias/MC_info/6718861_6005.bin differ
diff --git a/input/minbias/MC_info/6718861_6006.bin b/input/minbias/MC_info/6718861_6006.bin
index 9b104ca967e07fc634e966bab9f36da11f00b6e2..2cec8a9549c9beb909d109cb4a05f4ea5db1e17a 100644
Binary files a/input/minbias/MC_info/6718861_6006.bin and b/input/minbias/MC_info/6718861_6006.bin differ
diff --git a/input/minbias/MC_info/6718861_6007.bin b/input/minbias/MC_info/6718861_6007.bin
index a9ee4c45946ea2e90e7aa2d99d89e81450d48a00..471ccdf39b6c7f2dc603da2f3f1b1a1b8a23c3f7 100644
Binary files a/input/minbias/MC_info/6718861_6007.bin and b/input/minbias/MC_info/6718861_6007.bin differ
diff --git a/input/minbias/MC_info/6718861_6008.bin b/input/minbias/MC_info/6718861_6008.bin
index c3a82366dd4c0088b6d6ca5ae58a4c128a997cda..2449fe1f7d7657648f8179635c46d80bd6269447 100644
Binary files a/input/minbias/MC_info/6718861_6008.bin and b/input/minbias/MC_info/6718861_6008.bin differ
diff --git a/input/minbias/MC_info/6718861_6009.bin b/input/minbias/MC_info/6718861_6009.bin
index 68e0c4dc973c9aa022003efda5314c568b636031..51949f5634ace22b6472011aa0fb6364c9ea1266 100644
Binary files a/input/minbias/MC_info/6718861_6009.bin and b/input/minbias/MC_info/6718861_6009.bin differ
diff --git a/input/minbias/MC_info/6718861_6010.bin b/input/minbias/MC_info/6718861_6010.bin
index ec89e9d40227a0f7f1a672ea12332c206f29058e..a45e79a3ef9bbcb3a3958c3d4f5077a1f4bd354b 100644
Binary files a/input/minbias/MC_info/6718861_6010.bin and b/input/minbias/MC_info/6718861_6010.bin differ
diff --git a/input/minbias/banks/mdf/upgrade_mc_minbias_scifi_v5.mdf b/input/minbias/banks/mdf/upgrade_mc_minbias_scifi_v5.mdf
new file mode 100644
index 0000000000000000000000000000000000000000..601cb8ae4af14b61fab477531bc280d0b6e3fa4a
Binary files /dev/null and b/input/minbias/banks/mdf/upgrade_mc_minbias_scifi_v5.mdf differ
diff --git a/main/include/BankTypes.h b/main/include/BankTypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ebcf3b9c95ac217f07cbd827c41bba77c51e6f5
--- /dev/null
+++ b/main/include/BankTypes.h
@@ -0,0 +1,37 @@
+#ifndef BANKTYPES_H
+#define BANKTYPES_H 1
+
+#include <type_traits>
+#include <iostream>
+#include <unordered_map>
+#include <gsl-lite.hpp>
+
+namespace {
+   using gsl::span;
+}
+
+namespace {
+   constexpr auto BANKTYPES_START = __LINE__;
+}
+enum class BankTypes {
+           VP,
+           UT,
+           FT,
+           MUON
+};
+constexpr auto NBankTypes = __LINE__ - BANKTYPES_START - 4;
+const std::unordered_map<BankTypes, float> BankSizes = {{BankTypes::VP, 51.77f},
+                                                        {BankTypes::UT, 31.38f},
+                                                        {BankTypes::FT, 54.47f},
+                                                        {BankTypes::MUON, 5.13f}};
+
+
+std::string bank_name(BankTypes type);
+
+template<typename ENUM>
+constexpr auto to_integral(ENUM e) -> typename std::underlying_type<ENUM>::type
+{
+   return static_cast<typename std::underlying_type<ENUM>::type>(e);
+}
+
+#endif
diff --git a/main/include/Common.h b/main/include/Common.h
index bd5b49d775d89c7ba791300b5f3f9eafe94020d1..22d9cd13242be7988933460d620a62bfbc35eb45 100644
--- a/main/include/Common.h
+++ b/main/include/Common.h
@@ -1,6 +1,8 @@
 #pragma once
 
 #include <vector>
+#include <set>
+#include <type_traits>
 #include <iostream>
 #include <stdint.h>
 #include <stdio.h>
diff --git a/main/include/InputReader.h b/main/include/InputReader.h
index be81027e7909058ab773ebf33ae410a6b0200f7f..936da1bedd94cf3d1d14493e72bd3ac0e397100e 100644
--- a/main/include/InputReader.h
+++ b/main/include/InputReader.h
@@ -1,9 +1,15 @@
+#ifndef INPUTREADER_H
+#define INPUTREADER_H 1
+
 #include "InputTools.h"
 #include "Common.h"
+#include "BankTypes.h"
 #include "Tools.h"
 #include "CudaCommon.h"
 #include <string>
 #include <algorithm>
+#include <unordered_set>
+#include <gsl-lite.hpp>
 
 struct Reader {
   std::string folder_name;
@@ -32,13 +38,30 @@ struct UTMagnetToolReader : public Reader {
   std::vector<char> read_UT_magnet_tool();
 };
 
+using FolderMap = std::map<BankTypes, std::string>;
+
 struct EventReader : public Reader {
-  char* host_events;
-  uint* host_event_offsets;
-  size_t host_events_size;
-  size_t host_event_offsets_size;
+  EventReader(FolderMap folders)
+     : Reader(begin(folders)->second),
+       m_folders{std::move(folders)} {}
+
+  gsl::span<char> events(BankTypes type) {
+     auto it = m_events.find(type);
+     if (it == end(m_events)) {
+        return {};
+     } else {
+        return it->second.first;
+     }
+  }
 
-  EventReader(const std::string& folder_name) : Reader(folder_name) {}
+  gsl::span<uint> offsets(BankTypes type) {
+     auto it = m_events.find(type);
+     if (it == end(m_events)) {
+        return {};
+     } else {
+        return it->second.second;
+     }
+  }
 
   /**
    * @brief Reads files from the specified folder, starting from an event offset.
@@ -49,27 +72,39 @@ struct EventReader : public Reader {
    * @brief Checks the consistency of the read buffers.
    */
   virtual bool check_events(
+    BankTypes type,
     const std::vector<char>& events,
     const std::vector<uint>& event_offsets,
     uint number_of_events_requested
-  ) {
-    return true;
-  }
-};
+  ) const;
 
-struct VeloReader : public EventReader {
-  VeloReader(const std::string& folder_name) : EventReader(folder_name) {}
+protected:
 
-  /**
-   * @brief Checks the consistency of Velo raw data.
-   */
-  bool check_events(
-    const std::vector<char>& events,
-    const std::vector<uint>& event_offsets,
-    uint number_of_files
-  ) override {
-    return check_velopix_events(events, event_offsets, number_of_files);
-  }
+   std::string folder(BankTypes type) const {
+     auto it = m_folders.find(type);
+     if (it == end(m_folders)) {
+       return {};
+     } else {
+       return it->second;
+     }
+   }
+
+   std::unordered_set<BankTypes> types() const {
+      std::unordered_set<BankTypes> r;
+      for (const auto& entry : m_folders) {
+         r.emplace(entry.first);
+      }
+      return r;
+   }
+
+   bool add_events(BankTypes type, gsl::span<char> events, gsl::span<uint> offsets) {
+      auto r = m_events.emplace(type, std::make_pair(std::move(events), std::move(offsets)));
+      return r.second;
+   }
+
+private:
+   std::map<BankTypes, std::pair<gsl::span<char>, gsl::span<uint>>> m_events;
+   std::map<BankTypes, std::string> m_folders;
 };
 
-// TODO: Develop an UT event checker
+#endif
diff --git a/main/include/InputTools.h b/main/include/InputTools.h
index c82b6537cf71032f78004143f7e46b42e5000f39..119106d096b89f03c334b8ec8f6cefdc73b2a5e6 100644
--- a/main/include/InputTools.h
+++ b/main/include/InputTools.h
@@ -38,7 +38,8 @@ void appendFileToVector(
 );
 
 std::vector<std::string> list_folder(
-  const std::string& foldername
+  const std::string& foldername,
+  const std::string& extension = "bin"
 );
 
 uint get_number_of_events_requested(
diff --git a/main/include/MDFReader.h b/main/include/MDFReader.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc187ad725e16e1d6a473840a68860b18e384d10
--- /dev/null
+++ b/main/include/MDFReader.h
@@ -0,0 +1,20 @@
+#ifndef MDFREADER_H
+#define MDFREADER_H 1
+
+#include <map>
+#include <string>
+
+#include "InputReader.h"
+
+struct MDFReader : public EventReader {
+
+   MDFReader(FolderMap folders)
+      : EventReader(std::move(folders)) {}
+
+  /**
+   * @brief Reads files from the specified folder, starting from an event offset.
+   */
+  void read_events(uint number_of_events_requested=0, uint start_event_offset=0) override;
+
+};
+#endif
diff --git a/main/include/Tools.h b/main/include/Tools.h
index 1c4c291d19628c46a54eb19ce28d6964d1ad2c31..8eade8159e9395fc06a38025df47072edb9d371f 100644
--- a/main/include/Tools.h
+++ b/main/include/Tools.h
@@ -20,11 +20,12 @@
 #include "TrackChecker.h"
 #include "MCParticle.h"
 #include "VeloConsolidated.cuh"
+#include "CudaCommon.h"
 
 bool check_velopix_events(
   const std::vector<char>& events,
   const std::vector<uint>& event_offsets,
-  int n_events
+  size_t n_events
 );
 
 std::map<std::string, float> calcResults(
@@ -51,7 +52,7 @@ std::vector< trackChecker::Tracks > prepareVeloUTTracks(
 
 trackChecker::Tracks prepareForwardTracksVeloUTOnly(
   std::vector< VeloUTTracking::TrackUT > forward_tracks
-); 
+);
 
 trackChecker::Tracks prepareForwardTracksEvent(
   SciFi::Track forward_tracks[SciFi::max_tracks],
@@ -67,6 +68,10 @@ std::vector< trackChecker::Tracks > prepareForwardTracks(
 void call_pr_checker(
   const std::vector< trackChecker::Tracks >& all_tracks,
   const std::string& folder_name_MC,
-  const uint start_event_offset, 
+  const uint start_event_offset,
   const std::string& trackType
 );
+
+std::pair<size_t, std::string> set_device(
+  int cuda_device
+);
diff --git a/main/include/gsl-lite.hpp b/main/include/gsl-lite.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..62f9496ba3bd91f79f6e443c5e415df176146411
--- /dev/null
+++ b/main/include/gsl-lite.hpp
@@ -0,0 +1,2802 @@
+//
+// gsl-lite is based on GSL: Guideline Support Library.
+// For more information see https://github.com/martinmoene/gsl-lite
+//
+// Copyright (c) 2015-2018 Martin Moene
+// Copyright (c) 2015-2018 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#pragma once
+
+#ifndef GSL_GSL_LITE_HPP_INCLUDED
+#define GSL_GSL_LITE_HPP_INCLUDED
+
+#include <algorithm>
+#include <exception>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+#define  gsl_lite_MAJOR  0
+#define  gsl_lite_MINOR  32
+#define  gsl_lite_PATCH  0
+#define  gsl_lite_VERSION  gsl_STRINGIFY(gsl_lite_MAJOR) "." gsl_STRINGIFY(gsl_lite_MINOR) "." gsl_STRINGIFY(gsl_lite_PATCH)
+
+// gsl-lite backward compatibility:
+
+#ifdef gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR
+# define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR  gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR
+# pragma message ("gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR is deprecated since gsl-lite 0.7.0; replace with gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR, or consider span(with_container, cont).")
+#endif
+
+// M-GSL compatibility:
+
+#if defined( GSL_THROW_ON_CONTRACT_VIOLATION )
+# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS  1
+#endif
+
+#if defined( GSL_TERMINATE_ON_CONTRACT_VIOLATION )
+# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS  0
+#endif
+
+#if defined( GSL_UNENFORCED_ON_CONTRACT_VIOLATION )
+# define gsl_CONFIG_CONTRACT_LEVEL_OFF  1
+#endif
+
+// Configuration: Features
+
+#ifndef  gsl_FEATURE_WITH_CONTAINER_TO_STD
+# define gsl_FEATURE_WITH_CONTAINER_TO_STD  99
+#endif
+
+#ifndef  gsl_FEATURE_MAKE_SPAN_TO_STD
+# define gsl_FEATURE_MAKE_SPAN_TO_STD  99
+#endif
+
+#ifndef  gsl_FEATURE_BYTE_SPAN_TO_STD
+# define gsl_FEATURE_BYTE_SPAN_TO_STD  99
+#endif
+
+#ifndef  gsl_FEATURE_HAVE_IMPLICIT_MACRO
+# define gsl_FEATURE_HAVE_IMPLICIT_MACRO  1
+#endif
+
+#ifndef  gsl_FEATURE_HAVE_OWNER_MACRO
+# define gsl_FEATURE_HAVE_OWNER_MACRO  1
+#endif
+
+#ifndef  gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD
+# define gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD  0
+#endif
+
+// Configuration: Other
+
+#ifndef  gsl_CONFIG_DEPRECATE_TO_LEVEL
+# define gsl_CONFIG_DEPRECATE_TO_LEVEL  0
+#endif
+
+#ifndef  gsl_CONFIG_SPAN_INDEX_TYPE
+# define gsl_CONFIG_SPAN_INDEX_TYPE  size_t
+#endif
+
+#ifndef  gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR
+# define gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR  0
+#endif
+
+#ifndef  gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF
+# define gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF  0
+#endif
+
+#ifndef  gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS
+# define gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS  0
+#endif
+
+#ifndef  gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON
+# define gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON  1
+#endif
+
+#ifndef  gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR
+# define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR  0
+#endif
+
+#if    defined( gsl_CONFIG_CONTRACT_LEVEL_ON )
+# define        gsl_CONFIG_CONTRACT_LEVEL_MASK  0x11
+#elif  defined( gsl_CONFIG_CONTRACT_LEVEL_OFF )
+# define        gsl_CONFIG_CONTRACT_LEVEL_MASK  0x00
+#elif  defined( gsl_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY )
+# define        gsl_CONFIG_CONTRACT_LEVEL_MASK  0x01
+#elif  defined( gsl_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY )
+# define        gsl_CONFIG_CONTRACT_LEVEL_MASK  0x10
+#else
+# define        gsl_CONFIG_CONTRACT_LEVEL_MASK  0x11
+#endif
+
+#if   !defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS     ) && \
+      !defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES )
+# define        gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0
+#elif  defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS     ) && \
+      !defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES )
+# define        gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 1
+#elif !defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS     ) && \
+       defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES )
+# define        gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0
+#else
+# error only one of gsl_CONFIG_CONTRACT_VIOLATION_THROWS and gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES may be defined.
+#endif
+
+// C++ language version detection (C++20 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   gsl_CPLUSPLUS
+# ifdef  _MSVC_LANG
+#  define gsl_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define gsl_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define gsl_CPP98_OR_GREATER  ( gsl_CPLUSPLUS >= 199711L )
+#define gsl_CPP11_OR_GREATER  ( gsl_CPLUSPLUS >= 201103L )
+#define gsl_CPP14_OR_GREATER  ( gsl_CPLUSPLUS >= 201402L )
+#define gsl_CPP17_OR_GREATER  ( gsl_CPLUSPLUS >= 201703L )
+#define gsl_CPP20_OR_GREATER  ( gsl_CPLUSPLUS >= 202000L )
+
+// C++ language version (represent 98 as 3):
+
+#define gsl_CPLUSPLUS_V  ( gsl_CPLUSPLUS / 100 - (gsl_CPLUSPLUS > 200000 ? 2000 : 1994) )
+
+// half-open range [lo..hi):
+#define gsl_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+#if defined( _MSC_VER ) && !defined( __clang__ )
+# define gsl_COMPILER_MSVC_VERSION ( _MSC_VER / 10 - 10 * ( 5 + ( _MSC_VER < 1900 ) ) )
+#else
+# define gsl_COMPILER_MSVC_VERSION 0
+#endif
+
+#define gsl_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined __clang__
+# define gsl_COMPILER_CLANG_VERSION gsl_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ )
+#else
+# define gsl_COMPILER_CLANG_VERSION 0
+#endif
+
+#if defined __GNUC__
+# define gsl_COMPILER_GNUC_VERSION gsl_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ )
+#else
+# define gsl_COMPILER_GNUC_VERSION 0
+#endif
+
+// Compiler non-strict aliasing:
+
+#if defined __clang__ || defined __GNUC__
+# define gsl_may_alias  __attribute__((__may_alias__))
+#else
+# define gsl_may_alias
+#endif
+
+// Presence of gsl, language and library features:
+
+#define gsl_IN_STD( v )  ( (v) == 98 || (v) >= gsl_CPLUSPLUS_V )
+
+#define gsl_DEPRECATE_TO_LEVEL( level )  ( level <= gsl_CONFIG_DEPRECATE_TO_LEVEL )
+#define gsl_FEATURE_TO_STD(   feature )  ( gsl_IN_STD( gsl_FEATURE( feature##_TO_STD ) ) )
+#define gsl_FEATURE(          feature )  ( gsl_FEATURE_##feature )
+#define gsl_CONFIG(           feature )  ( gsl_CONFIG_##feature )
+#define gsl_HAVE(             feature )  ( gsl_HAVE_##feature )
+
+// Presence of wide character support:
+
+#ifdef __DJGPP__
+# define gsl_HAVE_WCHAR 0
+#else
+# define gsl_HAVE_WCHAR 1
+#endif
+
+// Presence of language & library features:
+
+#ifdef _HAS_CPP0X
+# define gsl_HAS_CPP0X  _HAS_CPP0X
+#else
+# define gsl_HAS_CPP0X  0
+#endif
+
+#define gsl_CPP11_100  (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 100)
+#define gsl_CPP11_110  (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110)
+#define gsl_CPP11_120  (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120)
+#define gsl_CPP11_140  (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 140)
+
+#define gsl_CPP14_000  (gsl_CPP14_OR_GREATER)
+#define gsl_CPP14_120  (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120)
+#define gsl_CPP14_140  (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 140)
+
+#define gsl_CPP17_000  (gsl_CPP17_OR_GREATER)
+#define gsl_CPP17_140  (gsl_CPP17_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 140)
+
+#define gsl_CPP11_140_CPP0X_90   (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VERSION >=  90 && gsl_HAS_CPP0X))
+#define gsl_CPP11_140_CPP0X_100  (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VERSION >= 100 && gsl_HAS_CPP0X))
+
+// Presence of C++11 language features:
+
+#define gsl_HAVE_AUTO                   gsl_CPP11_100
+#define gsl_HAVE_NULLPTR                gsl_CPP11_100
+#define gsl_HAVE_RVALUE_REFERENCE       gsl_CPP11_100
+
+#define gsl_HAVE_ENUM_CLASS             gsl_CPP11_110
+
+#define gsl_HAVE_ALIAS_TEMPLATE         gsl_CPP11_120
+#define gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG  gsl_CPP11_120
+#define gsl_HAVE_EXPLICIT               gsl_CPP11_120
+#define gsl_HAVE_INITIALIZER_LIST       gsl_CPP11_120
+
+#define gsl_HAVE_CONSTEXPR_11           gsl_CPP11_140
+#define gsl_HAVE_IS_DEFAULT             gsl_CPP11_140
+#define gsl_HAVE_IS_DELETE              gsl_CPP11_140
+#define gsl_HAVE_NOEXCEPT               gsl_CPP11_140
+
+#if gsl_CPP11_OR_GREATER
+// see above
+#endif
+
+// Presence of C++14 language features:
+
+#define gsl_HAVE_CONSTEXPR_14           gsl_CPP14_000
+#define gsl_HAVE_DECLTYPE_AUTO          gsl_CPP14_140
+
+// Presence of C++17 language features:
+// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7
+
+#define gsl_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE  gsl_CPP17_000
+#define gsl_HAVE_DEDUCTION_GUIDES      (gsl_CPP17_000 && ! gsl_BETWEEN( gsl_COMPILER_MSVC_VERSION, 1, 999 ) )
+
+// Presence of C++ library features:
+
+#define gsl_HAVE_ADDRESSOF              gsl_CPP17_000
+#define gsl_HAVE_ARRAY                  gsl_CPP11_110
+#define gsl_HAVE_TYPE_TRAITS            gsl_CPP11_110
+#define gsl_HAVE_TR1_TYPE_TRAITS        gsl_CPP11_110
+
+#define gsl_HAVE_CONTAINER_DATA_METHOD  gsl_CPP11_140_CPP0X_90
+#define gsl_HAVE_STD_DATA               gsl_CPP17_000
+
+#define gsl_HAVE_SIZED_TYPES            gsl_CPP11_140
+
+#define gsl_HAVE_MAKE_SHARED            gsl_CPP11_140_CPP0X_100
+#define gsl_HAVE_SHARED_PTR             gsl_CPP11_140_CPP0X_100
+#define gsl_HAVE_UNIQUE_PTR             gsl_CPP11_140_CPP0X_100
+
+#define gsl_HAVE_MAKE_UNIQUE            gsl_CPP14_120
+
+#define gsl_HAVE_UNCAUGHT_EXCEPTIONS    gsl_CPP17_140
+
+#define gsl_HAVE_ADD_CONST              gsl_HAVE_TYPE_TRAITS
+#define gsl_HAVE_INTEGRAL_CONSTANT      gsl_HAVE_TYPE_TRAITS
+#define gsl_HAVE_REMOVE_CONST           gsl_HAVE_TYPE_TRAITS
+#define gsl_HAVE_REMOVE_REFERENCE       gsl_HAVE_TYPE_TRAITS
+
+#define gsl_HAVE_TR1_ADD_CONST          gsl_HAVE_TR1_TYPE_TRAITS
+#define gsl_HAVE_TR1_INTEGRAL_CONSTANT  gsl_HAVE_TR1_TYPE_TRAITS
+#define gsl_HAVE_TR1_REMOVE_CONST       gsl_HAVE_TR1_TYPE_TRAITS
+#define gsl_HAVE_TR1_REMOVE_REFERENCE   gsl_HAVE_TR1_TYPE_TRAITS
+
+// C++ feature usage:
+
+#if gsl_HAVE( ADDRESSOF )
+# define gsl_ADDRESSOF(x)  std::addressof(x)
+#else
+# define gsl_ADDRESSOF(x)  (&x)
+#endif
+
+#if gsl_HAVE( CONSTEXPR_11 )
+# define gsl_constexpr constexpr
+#else
+# define gsl_constexpr /*constexpr*/
+#endif
+
+#if gsl_HAVE( CONSTEXPR_14 )
+# define gsl_constexpr14 constexpr
+#else
+# define gsl_constexpr14 /*constexpr*/
+#endif
+
+#if gsl_HAVE( EXPLICIT )
+# define gsl_explicit explicit
+#else
+# define gsl_explicit /*explicit*/
+#endif
+
+#if gsl_FEATURE( HAVE_IMPLICIT_MACRO )
+# define implicit /*implicit*/
+#endif
+
+#if gsl_HAVE( IS_DELETE )
+# define gsl_is_delete = delete
+#else
+# define gsl_is_delete
+#endif
+
+#if gsl_HAVE( IS_DELETE )
+# define gsl_is_delete_access public
+#else
+# define gsl_is_delete_access private
+#endif
+
+#if !gsl_HAVE( NOEXCEPT ) || gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+# define gsl_noexcept /*noexcept*/
+#else
+# define gsl_noexcept noexcept
+#endif
+
+#if gsl_HAVE( NULLPTR )
+# define gsl_nullptr  nullptr
+#else
+# define gsl_nullptr  NULL
+#endif
+
+#define gsl_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) )
+
+// Other features:
+
+#define gsl_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR  \
+    ( gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG && gsl_HAVE_CONTAINER_DATA_METHOD )
+
+// Note: !defined(__NVCC__) doesn't work with nvcc here:
+#define gsl_HAVE_UNCONSTRAINED_SPAN_CONTAINER_CTOR  \
+    ( gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR && (__NVCC__== 0) )
+
+// GSL API (e.g. for CUDA platform):
+
+#ifndef   gsl_api
+# ifdef   __CUDACC__
+#  define gsl_api __host__ __device__
+# else
+#  define gsl_api /*gsl_api*/
+# endif
+#endif
+
+// Additional includes:
+
+#if gsl_HAVE( ARRAY )
+# include <array>
+#endif
+
+#if gsl_HAVE( TYPE_TRAITS )
+# include <type_traits>
+#elif gsl_HAVE( TR1_TYPE_TRAITS )
+# include <tr1/type_traits>
+#endif
+
+#if gsl_HAVE( SIZED_TYPES )
+# include <cstdint>
+#endif
+
+// MSVC warning suppression macros:
+
+#if gsl_COMPILER_MSVC_VERSION >= 140
+# define gsl_SUPPRESS_MSGSL_WARNING(expr)        [[gsl::suppress(expr)]]
+# define gsl_SUPPRESS_MSVC_WARNING(code, descr)  __pragma(warning(suppress: code) )
+# define gsl_DISABLE_MSVC_WARNINGS(codes)        __pragma(warning(push))  __pragma(warning(disable: codes))
+# define gsl_RESTORE_MSVC_WARNINGS()             __pragma(warning(pop ))
+#else
+# define gsl_SUPPRESS_MSGSL_WARNING(expr)
+# define gsl_SUPPRESS_MSVC_WARNING(code, descr)
+# define gsl_DISABLE_MSVC_WARNINGS(codes)
+# define gsl_RESTORE_MSVC_WARNINGS()
+#endif
+
+// Suppress the following MSVC GSL warnings:
+// - C26410: gsl::r.32: the parameter 'ptr' is a reference to const unique pointer, use const T* or const T& instead
+// - C26415: gsl::r.30: smart pointer parameter 'ptr' is used only to access contained pointer. Use T* or T& instead
+// - C26418: gsl::r.36: shared pointer parameter 'ptr' is not copied or moved. Use T* or T& instead
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+//                      use brace initialization, gsl::narrow_cast or gsl::narow
+// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept'
+// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept'
+// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+// - C26482, gsl::b.2 : only index into arrays using constant expressions
+// - C26490: gsl::t.1 : don't use reinterpret_cast
+
+gsl_DISABLE_MSVC_WARNINGS( 26410 26415 26418 26472 26439 26440 26473 26481 26482 26490 )
+
+namespace gsl {
+
+// forward declare span<>:
+
+template< class T >
+class span;
+
+namespace details {
+
+// C++11 emulation:
+
+#if gsl_HAVE( ADD_CONST )
+
+using std::add_const;
+
+#elif gsl_HAVE( TR1_ADD_CONST )
+
+using std::tr1::add_const;
+
+#else
+
+template< class T > struct add_const { typedef const T type; };
+
+#endif // gsl_HAVE( ADD_CONST )
+
+#if gsl_HAVE( REMOVE_CONST )
+
+using std::remove_cv;
+using std::remove_const;
+using std::remove_volatile;
+
+#elif gsl_HAVE( TR1_REMOVE_CONST )
+
+using std::tr1::remove_cv;
+using std::tr1::remove_const;
+using std::tr1::remove_volatile;
+
+#else
+
+template< class T > struct remove_const          { typedef T type; };
+template< class T > struct remove_const<T const> { typedef T type; };
+
+template< class T > struct remove_volatile             { typedef T type; };
+template< class T > struct remove_volatile<T volatile> { typedef T type; };
+
+template< class T >
+struct remove_cv
+{
+    typedef typename details::remove_volatile<typename details::remove_const<T>::type>::type type;
+};
+
+#endif // gsl_HAVE( REMOVE_CONST )
+
+#if gsl_HAVE( INTEGRAL_CONSTANT )
+
+using std::integral_constant;
+using std::true_type;
+using std::false_type;
+
+#elif gsl_HAVE( TR1_INTEGRAL_CONSTANT )
+
+using std::tr1::integral_constant;
+using std::tr1::true_type;
+using std::tr1::false_type;
+
+#else
+
+template< int v > struct integral_constant { enum { value = v }; };
+typedef integral_constant< true  > true_type;
+typedef integral_constant< false > false_type;
+
+#endif
+
+#if gsl_HAVE( TYPE_TRAITS )
+
+template< class Q >
+struct is_span_oracle : std::false_type{};
+
+template< class T>
+struct is_span_oracle< span<T> > : std::true_type{};
+
+template< class Q >
+struct is_span : is_span_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_std_array_oracle : std::false_type{};
+
+#if gsl_HAVE( ARRAY )
+
+template< class T, std::size_t Extent >
+struct is_std_array_oracle< std::array<T, Extent> > : std::true_type{};
+
+#endif
+
+template< class Q >
+struct is_std_array : is_std_array_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_array : std::false_type {};
+
+template< class T >
+struct is_array<T[]> : std::true_type {};
+
+template< class T, std::size_t N >
+struct is_array<T[N]> : std::true_type {};
+
+#endif // gsl_HAVE( TYPE_TRAITS )
+
+} // namespace details
+
+//
+// GSL.util: utilities
+//
+
+// index type for all container indexes/subscripts/sizes
+typedef gsl_CONFIG_SPAN_INDEX_TYPE index;   // p0122r3 uses std::ptrdiff_t
+
+//
+// GSL.owner: ownership pointers
+//
+#if gsl_HAVE( SHARED_PTR )
+  using std::unique_ptr;
+  using std::shared_ptr;
+  using std::make_shared;
+# if gsl_HAVE( MAKE_UNIQUE )
+  using std::make_unique;
+# endif
+#endif
+
+#if  gsl_HAVE( ALIAS_TEMPLATE )
+# if gsl_HAVE( TYPE_TRAITS )
+  template< class T, class = typename std::enable_if< std::is_pointer<T>::value >::type >
+  using owner = T;
+# else
+  template< class T > using owner = T;
+# endif
+#else
+  template< class T > struct owner { typedef T type; };
+#endif
+
+#define gsl_HAVE_OWNER_TEMPLATE  gsl_HAVE_ALIAS_TEMPLATE
+
+#if gsl_FEATURE( HAVE_OWNER_MACRO )
+# if gsl_HAVE( OWNER_TEMPLATE )
+#  define Owner(t)  ::gsl::owner<t>
+# else
+#  define Owner(t)  ::gsl::owner<t>::type
+# endif
+#endif
+
+//
+// GSL.assert: assertions
+//
+
+#define gsl_ELIDE_CONTRACT_EXPECTS  ( 0 == ( gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) )
+#define gsl_ELIDE_CONTRACT_ENSURES  ( 0 == ( gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) )
+
+#if gsl_ELIDE_CONTRACT_EXPECTS
+# define Expects( x )  /* Expects elided */
+#elif gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+# define Expects( x )  ::gsl::fail_fast_assert( (x), "GSL: Precondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__) );
+#else
+# define Expects( x )  ::gsl::fail_fast_assert( (x) )
+#endif
+
+#if gsl_ELIDE_CONTRACT_EXPECTS
+# define gsl_EXPECTS_UNUSED_PARAM( x )  /* Make param unnamed if Expects elided */
+#else
+# define gsl_EXPECTS_UNUSED_PARAM( x )  x
+#endif
+
+#if gsl_ELIDE_CONTRACT_ENSURES
+# define Ensures( x )  /* Ensures elided */
+#elif gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+# define Ensures( x )  ::gsl::fail_fast_assert( (x), "GSL: Postcondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__) );
+#else
+# define Ensures( x )  ::gsl::fail_fast_assert( (x) )
+#endif
+
+#define gsl_STRINGIFY(  x )  gsl_STRINGIFY_( x )
+#define gsl_STRINGIFY_( x )  #x
+
+struct fail_fast : public std::logic_error
+{
+    gsl_api explicit fail_fast( char const * const message )
+    : std::logic_error( message ) {}
+};
+
+// workaround for gcc 5 throw/terminate constexpr bug:
+
+#if gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 430, 600 ) && gsl_HAVE( CONSTEXPR_14 )
+
+# if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+gsl_api inline gsl_constexpr14 auto fail_fast_assert( bool cond, char const * const message ) -> void
+{
+    !cond ? throw fail_fast( message ) : 0;
+}
+
+# else
+
+gsl_api inline gsl_constexpr14 auto fail_fast_assert( bool cond ) -> void
+{
+    struct F { static gsl_constexpr14 void f(){}; };
+
+    !cond ? std::terminate() : F::f();
+}
+
+# endif
+
+#else // workaround
+
+# if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+gsl_api inline gsl_constexpr14 void fail_fast_assert( bool cond, char const * const message )
+{
+    if ( !cond )
+        throw fail_fast( message );
+}
+
+# else
+
+gsl_api inline gsl_constexpr14 void fail_fast_assert( bool cond ) gsl_noexcept
+{
+    if ( !cond )
+        std::terminate();
+}
+
+# endif
+#endif // workaround
+
+//
+// GSL.util: utilities
+//
+
+#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD )
+
+// Add uncaught_exceptions for pre-2017 MSVC, GCC and Clang
+// Return unsigned char to save stack space, uncaught_exceptions can only increase by 1 in a scope
+
+namespace details {
+
+inline unsigned char to_uchar( unsigned x ) gsl_noexcept
+{
+    return static_cast<unsigned char>( x );
+}
+
+#if gsl_HAVE( UNCAUGHT_EXCEPTIONS )
+
+inline unsigned char uncaught_exceptions() gsl_noexcept
+{
+    return to_uchar( std::uncaught_exceptions() );
+}
+
+#elif gsl_COMPILER_MSVC_VERSION
+
+extern "C" char * __cdecl _getptd();
+inline unsigned char uncaught_exceptions() gsl_noexcept
+{
+    return to_uchar( *reinterpret_cast<unsigned*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90) ) );
+}
+
+#elif gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_GNUC_VERSION
+
+extern "C" char * __cxa_get_globals();
+inline unsigned char uncaught_exceptions() gsl_noexcept
+{
+    return to_uchar( *reinterpret_cast<unsigned*>(__cxa_get_globals() + sizeof(void*) ) );
+}
+#endif
+}
+#endif
+
+#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110
+
+template< class F >
+class final_action
+{
+public:
+    gsl_api explicit final_action( F action ) gsl_noexcept
+        : action_( std::move( action ) )
+        , invoke_( true )
+    {}
+
+    gsl_api final_action( final_action && other ) gsl_noexcept
+        : action_( std::move( other.action_ ) )
+        , invoke_( other.invoke_ )
+    {
+        other.invoke_ = false;
+    }
+
+    gsl_api virtual ~final_action() gsl_noexcept
+    {
+        if ( invoke_ )
+            action_();
+    }
+
+gsl_is_delete_access:
+    gsl_api final_action( final_action const  & ) gsl_is_delete;
+    gsl_api final_action & operator=( final_action const & ) gsl_is_delete;
+    gsl_api final_action & operator=( final_action && ) gsl_is_delete;
+
+protected:
+    gsl_api void dismiss() gsl_noexcept
+    {
+        invoke_ = false;
+    }
+
+private:
+    F action_;
+    bool invoke_;
+};
+
+template< class F >
+gsl_api inline final_action<F> finally( F const & action ) gsl_noexcept
+{
+    return final_action<F>( action );
+}
+
+template< class F >
+gsl_api inline final_action<F> finally( F && action ) gsl_noexcept
+{
+    return final_action<F>( std::forward<F>( action ) );
+}
+
+#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD )
+
+template< class F >
+class final_action_return : public final_action<F>
+{
+public:
+    gsl_api explicit final_action_return( F && action ) gsl_noexcept
+        : final_action<F>( std::move( action ) )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api final_action_return( final_action_return && other ) gsl_noexcept
+        : final_action<F>( std::move( other ) )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api ~final_action_return() override
+    {
+        if ( details::uncaught_exceptions() != exception_count )
+            this->dismiss();
+    }
+
+gsl_is_delete_access:
+    gsl_api final_action_return( final_action_return const & ) gsl_is_delete;
+    gsl_api final_action_return & operator=( final_action_return const & ) gsl_is_delete;
+
+private:
+    unsigned char exception_count;
+};
+
+template< class F >
+gsl_api inline final_action_return<F> on_return( F const & action ) gsl_noexcept
+{
+    return final_action_return<F>( action );
+}
+
+template< class F >
+gsl_api inline final_action_return<F> on_return( F && action ) gsl_noexcept
+{
+    return final_action_return<F>( std::forward<F>( action ) );
+}
+
+template< class F >
+class final_action_error : public final_action<F>
+{
+public:
+    gsl_api explicit final_action_error( F && action ) gsl_noexcept
+        : final_action<F>( std::move( action ) )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api final_action_error( final_action_error && other ) gsl_noexcept
+        : final_action<F>( std::move( other ) )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api ~final_action_error() override
+    {
+        if ( details::uncaught_exceptions() == exception_count )
+            this->dismiss();
+    }
+
+gsl_is_delete_access:
+    gsl_api final_action_error( final_action_error const & ) gsl_is_delete;
+    gsl_api final_action_error & operator=( final_action_error const & ) gsl_is_delete;
+
+private:
+    unsigned char exception_count;
+};
+
+template< class F >
+gsl_api inline final_action_error<F> on_error( F const & action ) gsl_noexcept
+{
+    return final_action_error<F>( action );
+}
+
+template< class F >
+gsl_api inline final_action_error<F> on_error( F && action ) gsl_noexcept
+{
+    return final_action_error<F>( std::forward<F>( action ) );
+}
+
+#endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD )
+
+#else // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110
+
+class final_action
+{
+public:
+    typedef void (*Action)();
+
+    gsl_api final_action( Action action )
+    : action_( action )
+    , invoke_( true )
+    {}
+
+    gsl_api final_action( final_action const & other )
+        : action_( other.action_ )
+        , invoke_( other.invoke_ )
+    {
+        other.invoke_ = false;
+    }
+
+    gsl_api virtual ~final_action()
+    {
+        if ( invoke_ )
+            action_();
+    }
+
+protected:
+    gsl_api void dismiss()
+    {
+        invoke_ = false;
+    }
+
+private:
+    gsl_api final_action & operator=( final_action const & );
+
+private:
+    Action action_;
+    mutable bool invoke_;
+};
+
+template< class F >
+gsl_api inline final_action finally( F const & f )
+{
+    return final_action(( f ));
+}
+
+#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD )
+
+class final_action_return : public final_action
+{
+public:
+    gsl_api explicit final_action_return( Action action )
+        : final_action( action )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api ~final_action_return()
+    {
+        if ( details::uncaught_exceptions() != exception_count )
+            this->dismiss();
+    }
+
+private:
+    gsl_api final_action_return & operator=( final_action_return const & );
+
+private:
+    unsigned char exception_count;
+};
+
+template< class F >
+gsl_api inline final_action_return on_return( F const & action )
+{
+    return final_action_return( action );
+}
+
+class final_action_error : public final_action
+{
+public:
+    gsl_api explicit final_action_error( Action action )
+        : final_action( action )
+        , exception_count( details::uncaught_exceptions() )
+    {}
+
+    gsl_api ~final_action_error()
+    {
+        if ( details::uncaught_exceptions() == exception_count )
+            this->dismiss();
+    }
+
+private:
+    gsl_api final_action_error & operator=( final_action_error const & );
+
+private:
+    unsigned char exception_count;
+};
+
+template< class F >
+gsl_api inline final_action_error on_error( F const & action )
+{
+    return final_action_error( action );
+}
+
+#endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD )
+
+#endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION == 110
+
+#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120
+
+template< class T, class U >
+gsl_api inline gsl_constexpr T narrow_cast( U && u ) gsl_noexcept
+{
+    return static_cast<T>( std::forward<U>( u ) );
+}
+
+#else
+
+template< class T, class U >
+gsl_api inline T narrow_cast( U u ) gsl_noexcept
+{
+    return static_cast<T>( u );
+}
+
+#endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120
+
+struct narrowing_error : public std::exception {};
+
+#if gsl_HAVE( TYPE_TRAITS )
+
+namespace details
+{
+    template< class T, class U >
+    struct is_same_signedness : public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value>
+    {};
+}
+#endif
+
+template< class T, class U >
+gsl_api inline T narrow( U u )
+{
+    T t = narrow_cast<T>( u );
+
+    if ( static_cast<U>( t ) != u )
+    {
+#if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+        throw narrowing_error();
+#else
+        std::terminate();
+#endif
+    }
+
+#if gsl_HAVE( TYPE_TRAITS )
+# if gsl_COMPILER_MSVC_VERSION
+    // Suppress MSVC level 4 warning C4127 (conditional expression is constant)
+    if ( 0, ! details::is_same_signedness<T, U>::value && ( ( t < T() ) != ( u < U() ) ) )
+# else
+    if (    ! details::is_same_signedness<T, U>::value && ( ( t < T() ) != ( u < U() ) ) )
+# endif
+#else
+    // Don't assume T() works:
+    if ( ( t < 0 ) != ( u < 0 ) )
+#endif
+    {
+#if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+        throw narrowing_error();
+#else
+        std::terminate();
+#endif
+    }
+    return t;
+}
+
+//
+// at() - Bounds-checked way of accessing static arrays, std::array, std::vector.
+//
+
+template< class T, size_t N >
+gsl_api inline gsl_constexpr14 T & at( T(&arr)[N], size_t index )
+{
+    Expects( index < N );
+    return arr[index];
+}
+
+#if gsl_HAVE( ARRAY )
+
+template< class T, size_t N >
+gsl_api inline gsl_constexpr14 T & at( std::array<T, N> & arr, size_t index )
+{
+    Expects( index < N );
+    return arr[index];
+}
+#endif
+
+template< class Container >
+gsl_api inline gsl_constexpr14 typename Container::value_type & at( Container & cont, size_t index )
+{
+    Expects( index < cont.size() );
+    return cont[index];
+}
+
+#if gsl_HAVE( INITIALIZER_LIST )
+
+template< class T >
+gsl_api inline const gsl_constexpr14 T & at( std::initializer_list<T> cont, size_t index )
+{
+    Expects( index < cont.size() );
+    return *( cont.begin() + index );
+}
+#endif
+
+template< class T >
+gsl_api inline gsl_constexpr T & at( span<T> s, size_t index )
+{
+    return s.at( index );
+}
+
+//
+// GSL.views: views
+//
+
+//
+// not_null<> - Wrap any indirection and enforce non-null.
+//
+template< class T >
+class not_null
+{
+#if gsl_CONFIG( NOT_NULL_EXPLICIT_CTOR )
+# define gsl_not_null_explicit   explicit
+#else
+# define gsl_not_null_explicit /*explicit*/
+#endif
+
+#if gsl_CONFIG( NOT_NULL_GET_BY_CONST_REF )
+    typedef T const & get_result_t;
+#else
+    typedef T get_result_t;
+#endif
+
+public:
+#if gsl_HAVE( TYPE_TRAITS )
+    static_assert( std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr." );
+#endif
+
+    template< class U
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class Dummy = typename std::enable_if<std::is_constructible<T, U>::value>::type
+#endif
+    >
+    gsl_api gsl_constexpr14 gsl_not_null_explicit 
+#if gsl_HAVE( RVALUE_REFERENCE )
+    not_null( U && u )
+    : ptr_( std::forward<U>( u ) )
+#else
+    not_null( U const & u )
+    : ptr_( u )
+#endif
+    {
+        Expects( ptr_ != gsl_nullptr );
+    }
+#undef gsl_not_null_explicit
+    
+#if gsl_HAVE( IS_DEFAULT )
+    gsl_api                ~not_null() = default;
+    gsl_api gsl_constexpr   not_null( not_null &&      other ) = default;
+    gsl_api gsl_constexpr   not_null( not_null const & other ) = default;
+    gsl_api                 not_null & operator=( not_null &&      other ) = default;
+    gsl_api                 not_null & operator=( not_null const & other ) = default;
+#else
+    gsl_api                ~not_null() {};
+    gsl_api gsl_constexpr   not_null( not_null const & other ) : ptr_ ( other.ptr_  ) {}
+    gsl_api                 not_null & operator=( not_null const & other ) { ptr_ = other.ptr_; return *this; }
+# if gsl_HAVE( RVALUE_REFERENCE )
+    gsl_api gsl_constexpr   not_null( not_null && other ) : ptr_( std::move( other.get() ) ) {}
+    gsl_api                 not_null & operator=( not_null && other ) { ptr_ = std::move( other.get() ); return *this; }
+# endif
+#endif
+
+    template< class U
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class Dummy = typename std::enable_if<std::is_convertible<U, T>::value>::type
+#endif
+    >
+    gsl_api gsl_constexpr not_null( not_null<U> const & other )
+    : ptr_( other.get() )
+    {}
+
+    gsl_api gsl_constexpr14 get_result_t get() const
+    {
+        // Without cheating and changing ptr_ from the outside, this check is superfluous:
+        Ensures( ptr_ != gsl_nullptr );
+        return ptr_;
+    }
+
+    gsl_api gsl_constexpr operator get_result_t  () const { return get(); }
+    gsl_api gsl_constexpr get_result_t operator->() const { return get(); }
+
+#if gsl_HAVE( DECLTYPE_AUTO )
+    gsl_api gsl_constexpr decltype(auto) operator*() const { return *get(); }
+#endif
+
+gsl_is_delete_access:
+    // prevent compilation when initialized with a nullptr or literal 0:
+#if gsl_HAVE( NULLPTR )
+    gsl_api not_null(             std::nullptr_t ) gsl_is_delete;
+    gsl_api not_null & operator=( std::nullptr_t ) gsl_is_delete;
+#else
+    gsl_api not_null(             int ) gsl_is_delete;
+    gsl_api not_null & operator=( int ) gsl_is_delete;
+#endif
+
+    // unwanted operators...pointers only point to single objects!
+    gsl_api not_null & operator++() gsl_is_delete;
+    gsl_api not_null & operator--() gsl_is_delete;
+    gsl_api not_null   operator++( int ) gsl_is_delete;
+    gsl_api not_null   operator--( int ) gsl_is_delete;
+    gsl_api not_null & operator+ ( size_t ) gsl_is_delete;
+    gsl_api not_null & operator+=( size_t ) gsl_is_delete;
+    gsl_api not_null & operator- ( size_t ) gsl_is_delete;
+    gsl_api not_null & operator-=( size_t ) gsl_is_delete;
+    gsl_api not_null & operator+=( std::ptrdiff_t ) gsl_is_delete;
+    gsl_api not_null & operator-=( std::ptrdiff_t ) gsl_is_delete;
+    gsl_api void       operator[]( std::ptrdiff_t ) const gsl_is_delete;
+
+private:
+    T ptr_;
+};
+
+// not_null with implicit constructor, allowing copy-initialization:
+
+template< class T >
+class not_null_ic : public not_null<T>
+{
+public:
+    template< class U
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class Dummy = typename std::enable_if<std::is_constructible<T, U>::value>::type
+#endif
+    >
+    gsl_api gsl_constexpr14
+#if gsl_HAVE( RVALUE_REFERENCE )
+    not_null_ic( U && u )
+    : not_null<T>( std::forward<U>( u ) )
+#else
+    not_null_ic( U const & u )
+    : not_null<T>( u )
+#endif
+    {}
+};
+
+// more not_null unwanted operators
+
+template< class T, class U >
+std::ptrdiff_t operator-( not_null<T> const &, not_null<U> const & ) gsl_is_delete;
+
+template< class T >
+not_null<T> operator-( not_null<T> const &, std::ptrdiff_t ) gsl_is_delete;
+
+template< class T >
+not_null<T> operator+( not_null<T> const &, std::ptrdiff_t ) gsl_is_delete;
+
+template< class T >
+not_null<T> operator+( std::ptrdiff_t, not_null<T> const & ) gsl_is_delete;
+
+// not_null comparisons
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator==( not_null<T> const & l, not_null<U> const & r )
+{
+    return  l.get() == r.get();
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator< ( not_null<U> const & l, not_null<U> const & r )
+{
+    return l.get() < r.get();
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator!=( not_null<U> const & l, not_null<U> const & r )
+{
+    return !( l == r );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator<=( not_null<U> const & l, not_null<U> const & r )
+{
+    return !( r < l );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator> ( not_null<U> const & l, not_null<U> const & r )
+{
+    return ( r < l );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator>=( not_null<U> const & l, not_null<U> const & r )
+{
+    return !( l < r );
+}
+
+//
+// Byte-specific type.
+//
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+  enum class gsl_may_alias byte : unsigned char {};
+#else
+  struct gsl_may_alias byte { typedef unsigned char type; type v; };
+#endif
+
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+# define gsl_ENABLE_IF_INTEGRAL_T(T)  \
+    , class = typename std::enable_if<std::is_integral<T>::value>::type
+#else
+# define gsl_ENABLE_IF_INTEGRAL_T(T)
+#endif
+
+template< class T >
+gsl_api inline gsl_constexpr byte to_byte( T v ) gsl_noexcept
+{
+#if    gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return static_cast<byte>( v );
+#elif  gsl_HAVE( CONSTEXPR_11 )
+    return { static_cast<typename byte::type>( v ) };
+#else
+    byte b = { static_cast<typename byte::type>( v ) }; return b;
+#endif
+}
+
+template< class IntegerType  gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) >
+gsl_api inline gsl_constexpr IntegerType to_integer( byte b ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return static_cast<typename std::underlying_type<byte>::type>( b );
+#else
+    return b.v;
+#endif
+}
+
+gsl_api inline gsl_constexpr unsigned char to_uchar( byte b ) gsl_noexcept
+{
+    return to_integer<unsigned char>( b );
+}
+
+gsl_api inline gsl_constexpr unsigned char to_uchar( int i ) gsl_noexcept
+{
+    return static_cast<unsigned char>( i );
+}
+
+#if ! gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+
+gsl_api inline gsl_constexpr bool operator==( byte l, byte r ) gsl_noexcept
+{
+    return l.v == r.v;
+}
+
+gsl_api inline gsl_constexpr bool operator!=( byte l, byte r ) gsl_noexcept
+{
+    return !( l == r );
+}
+
+gsl_api inline gsl_constexpr bool operator< ( byte l, byte r ) gsl_noexcept
+{
+    return l.v < r.v;
+}
+
+gsl_api inline gsl_constexpr bool operator<=( byte l, byte r ) gsl_noexcept
+{
+    return !( r < l );
+}
+
+gsl_api inline gsl_constexpr bool operator> ( byte l, byte r ) gsl_noexcept
+{
+    return ( r < l );
+}
+
+gsl_api inline gsl_constexpr bool operator>=( byte l, byte r ) gsl_noexcept
+{
+    return !( l < r );
+}
+#endif
+
+template< class IntegerType  gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) >
+gsl_api inline gsl_constexpr14 byte & operator<<=( byte & b, IntegerType shift ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return b = to_byte( to_uchar( b ) << shift );
+#else
+    b.v = to_uchar( b.v << shift ); return b;
+#endif
+}
+
+template< class IntegerType  gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) >
+gsl_api inline gsl_constexpr byte operator<<( byte b, IntegerType shift ) gsl_noexcept
+{
+    return to_byte( to_uchar( b ) << shift );
+}
+
+template< class IntegerType  gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) >
+gsl_api inline gsl_constexpr14 byte & operator>>=( byte & b, IntegerType shift ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return b = to_byte( to_uchar( b ) >> shift );
+#else
+    b.v = to_uchar( b.v >> shift ); return b;
+#endif
+}
+
+template< class IntegerType  gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) >
+gsl_api inline gsl_constexpr byte operator>>( byte b, IntegerType shift ) gsl_noexcept
+{
+    return to_byte( to_uchar( b ) >> shift );
+}
+
+gsl_api inline gsl_constexpr14 byte & operator|=( byte & l, byte r ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return l = to_byte( to_uchar( l ) | to_uchar( r ) );
+#else
+    l.v = to_uchar( l ) | to_uchar( r ); return l;
+#endif
+}
+
+gsl_api inline gsl_constexpr byte operator|( byte l, byte r ) gsl_noexcept
+{
+    return to_byte( to_uchar( l ) | to_uchar( r ) );
+}
+
+gsl_api inline gsl_constexpr14 byte & operator&=( byte & l, byte r ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return l = to_byte( to_uchar( l ) & to_uchar( r ) );
+#else
+    l.v = to_uchar( l ) & to_uchar( r ); return l;
+#endif
+}
+
+gsl_api inline gsl_constexpr byte operator&( byte l, byte r ) gsl_noexcept
+{
+    return to_byte( to_uchar( l ) & to_uchar( r ) );
+}
+
+gsl_api inline gsl_constexpr14 byte & operator^=( byte & l, byte r ) gsl_noexcept
+{
+#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE )
+    return l = to_byte( to_uchar( l ) ^ to_uchar (r ) );
+#else
+    l.v = to_uchar( l ) ^ to_uchar (r ); return l;
+#endif
+}
+
+gsl_api inline gsl_constexpr byte operator^( byte l, byte r ) gsl_noexcept
+{
+    return to_byte( to_uchar( l ) ^ to_uchar( r ) );
+}
+
+gsl_api inline gsl_constexpr byte operator~( byte b ) gsl_noexcept
+{
+    return to_byte( ~to_uchar( b ) );
+}
+
+#if gsl_FEATURE_TO_STD( WITH_CONTAINER )
+
+// Tag to select span constructor taking a container (prevent ms-gsl warning C26426):
+
+struct with_container_t { gsl_constexpr with_container_t() gsl_noexcept {} };
+const  gsl_constexpr   with_container_t with_container;
+
+#endif
+
+#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+namespace details {
+
+// Can construct from containers that:
+
+template<
+    class Container, class ElementType
+    , class = typename std::enable_if<
+        ! details::is_span< Container >::value &&
+        ! details::is_array< Container >::value &&
+        ! details::is_std_array< Container >::value &&
+          std::is_convertible<typename std::remove_pointer<decltype(std::declval<Container>().data())>::type(*)[], ElementType(*)[] >::value
+    >::type
+#if gsl_HAVE( STD_DATA )
+      // data(cont) and size(cont) well-formed:
+    , class = decltype( std::data( std::declval<Container>() ) )
+    , class = decltype( std::size( std::declval<Container>() ) )
+#endif
+>
+struct can_construct_span_from : details::true_type{};
+
+} // namespace details
+#endif
+
+//
+// span<> - A 1D view of contiguous T's, replace (*,len).
+//
+template< class T >
+class span
+{
+    template< class U > friend class span;
+
+public:
+    typedef index index_type;
+
+    typedef T element_type;
+    typedef typename details::remove_cv< T >::type value_type;
+
+    typedef T & reference;
+    typedef T * pointer;
+    typedef T const * const_pointer;
+    typedef T const & const_reference;
+
+    typedef pointer       iterator;
+    typedef const_pointer const_iterator;
+
+    typedef std::reverse_iterator< iterator >       reverse_iterator;
+    typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+    typedef typename std::iterator_traits< iterator >::difference_type difference_type;
+
+    // 26.7.3.2 Constructors, copy, and assignment [span.cons]
+
+    gsl_api gsl_constexpr14 span() gsl_noexcept
+        : first_( gsl_nullptr )
+        , last_ ( gsl_nullptr )
+    {
+        Expects( size() == 0 );
+    }
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 5 )
+
+#if gsl_HAVE( NULLPTR )
+    gsl_api gsl_constexpr14 span( std::nullptr_t, index_type gsl_EXPECTS_UNUSED_PARAM( size_in ) )
+        : first_( nullptr )
+        , last_ ( nullptr )
+    {
+        Expects( size_in == 0 );
+    }
+#endif
+
+#if gsl_HAVE( IS_DELETE )
+    gsl_api gsl_constexpr span( reference data_in )
+        : span( &data_in, 1 )
+    {}
+
+    gsl_api gsl_constexpr span( element_type && ) = delete;
+#endif
+
+#endif // deprecate
+
+    gsl_api gsl_constexpr14 span( pointer data_in, index_type size_in )
+        : first_( data_in )
+        , last_ ( data_in + size_in )
+    {
+        Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) );
+    }
+
+    gsl_api gsl_constexpr14 span( pointer first_in, pointer last_in )
+        : first_( first_in )
+        , last_ ( last_in )
+    {
+        Expects( first_in <= last_in );
+    }
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 5 )
+
+    template< class U >
+    gsl_api gsl_constexpr14 span( U * & data_in, index_type size_in )
+        : first_( data_in )
+        , last_ ( data_in + size_in )
+    {
+        Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) );
+    }
+
+    template< class U >
+    gsl_api gsl_constexpr14 span( U * const & data_in, index_type size_in )
+        : first_( data_in )
+        , last_ ( data_in + size_in )
+    {
+        Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) );
+    }
+
+#endif // deprecate
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 5 )
+    template< class U, size_t N >
+    gsl_api gsl_constexpr span( U (&arr)[N] ) gsl_noexcept
+        : first_( gsl_ADDRESSOF( arr[0] ) )
+        , last_ ( gsl_ADDRESSOF( arr[0] ) + N )
+    {}
+#else
+    template< size_t N
+# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class = typename std::enable_if<
+            std::is_convertible<value_type(*)[], element_type(*)[] >::value
+        >::type
+# endif
+    >
+    gsl_api gsl_constexpr span( element_type (&arr)[N] ) gsl_noexcept
+        : first_( gsl_ADDRESSOF( arr[0] ) )
+        , last_ ( gsl_ADDRESSOF( arr[0] ) + N )
+    {}
+#endif // deprecate
+
+#if gsl_HAVE( ARRAY )
+#if ! gsl_DEPRECATE_TO_LEVEL( 5 )
+
+    template< class U, size_t N >
+    gsl_api gsl_constexpr span( std::array< U, N > & arr )
+        : first_( arr.data() )
+        , last_ ( arr.data() + N )
+    {}
+
+    template< class U, size_t N >
+    gsl_api gsl_constexpr span( std::array< U, N > const & arr )
+        : first_( arr.data() )
+        , last_ ( arr.data() + N )
+    {}
+
+#else
+
+    template< size_t N
+# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class = typename std::enable_if<
+            std::is_convertible<value_type(*)[], element_type(*)[] >::value
+        >::type
+# endif
+    >
+    gsl_api gsl_constexpr span( std::array< value_type, N > & arr )
+        : first_( arr.data() )
+        , last_ ( arr.data() + N )
+    {}
+
+    template< size_t N
+# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class = typename std::enable_if<
+            std::is_convertible<value_type(*)[], element_type(*)[] >::value
+        >::type
+# endif
+    >
+    gsl_api gsl_constexpr span( std::array< value_type, N > const & arr )
+        : first_( arr.data() )
+        , last_ ( arr.data() + N )
+    {}
+
+#endif // deprecate
+#endif // gsl_HAVE( ARRAY )
+
+#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+    template< class Container
+        , class = typename std::enable_if<
+            details::can_construct_span_from< Container, element_type >::value
+        >::type
+    >
+    gsl_api gsl_constexpr span( Container & cont )
+        : first_( cont.data() )
+        , last_ ( cont.data() + cont.size() )
+    {}
+
+    template< class Container
+        , class = typename std::enable_if<
+            std::is_const< element_type >::value &&
+            details::can_construct_span_from< Container, element_type >::value
+        >::type
+    >
+    gsl_api gsl_constexpr span( Container const & cont )
+        : first_( cont.data() )
+        , last_ ( cont.data() + cont.size() )
+    {}
+
+#elif gsl_HAVE( UNCONSTRAINED_SPAN_CONTAINER_CTOR )
+
+    template< class Container >
+    gsl_api gsl_constexpr span( Container & cont )
+        : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) )
+        , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() )
+    {}
+
+    template< class Container >
+    gsl_api gsl_constexpr span( Container const & cont )
+        : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) )
+        , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() )
+    {}
+
+#endif
+
+#if gsl_FEATURE_TO_STD( WITH_CONTAINER )
+
+    template< class Container >
+    gsl_api gsl_constexpr span( with_container_t, Container & cont )
+        : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) )
+        , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() )
+    {}
+
+    template< class Container >
+    gsl_api gsl_constexpr span( with_container_t, Container const & cont )
+        : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) )
+        , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() )
+    {}
+
+#endif
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 4 )
+    // constructor taking shared_ptr deprecated since 0.29.0
+
+#if gsl_HAVE( SHARED_PTR )
+    gsl_api gsl_constexpr span( shared_ptr<element_type> const & ptr )
+        : first_( ptr.get() )
+        , last_ ( ptr.get() ? ptr.get() + 1 : 0 )
+    {}
+#endif
+
+    // constructors taking unique_ptr deprecated since 0.29.0
+
+#if gsl_HAVE( UNIQUE_PTR )
+# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+    template< class ArrayElementType = typename std::add_pointer<element_type>::type >
+# else
+    template< class ArrayElementType >
+# endif
+    gsl_api gsl_constexpr span( unique_ptr<ArrayElementType> const & ptr, index_type count )
+        : first_( ptr.get() )
+        , last_ ( ptr.get() + count )
+    {}
+
+    gsl_api gsl_constexpr span( unique_ptr<element_type> const & ptr )
+        : first_( ptr.get() )
+        , last_ ( ptr.get() ? ptr.get() + 1 : 0 )
+    {}
+#endif
+
+#endif // deprecate shared_ptr, unique_ptr
+
+#if gsl_HAVE( IS_DEFAULT ) && ! gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 430, 600)
+    gsl_api gsl_constexpr span( span && ) gsl_noexcept = default;
+    gsl_api gsl_constexpr span( span const & ) = default;
+#else
+    gsl_api gsl_constexpr span( span const & other )
+        : first_( other.begin() )
+        , last_ ( other.end() )
+    {}
+#endif
+
+#if gsl_HAVE( IS_DEFAULT )
+    ~span() = default;
+#else
+    ~span() {}
+#endif
+
+#if gsl_HAVE( IS_DEFAULT )
+    gsl_api gsl_constexpr14 span & operator=( span && ) gsl_noexcept = default;
+    gsl_api gsl_constexpr14 span & operator=( span const & ) gsl_noexcept = default;
+#else
+    gsl_api span & operator=( span other ) gsl_noexcept
+    {
+        other.swap( *this );
+        return *this;
+    }
+#endif
+
+    template< class U
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class = typename std::enable_if<
+            std::is_convertible<U(*)[], element_type(*)[]>::value
+        >::type
+#endif
+    >
+    gsl_api gsl_constexpr span( span<U> const & other )
+        : first_( other.begin() )
+        , last_ ( other.end() )
+    {}
+
+#if 0
+    // Converting from other span ?
+    template< class U > operator=();
+#endif
+
+    // 26.7.3.3 Subviews [span.sub]
+
+    gsl_api gsl_constexpr14 span first( index_type count ) const gsl_noexcept
+    {
+        Expects( 0 <= count && count <= this->size() );
+        return span( this->data(), count );
+    }
+
+    gsl_api gsl_constexpr14 span last( index_type count ) const gsl_noexcept
+    {
+        Expects( 0 <= count && count <= this->size() );
+        return span( this->data() + this->size() - count, count );
+    }
+
+    gsl_api gsl_constexpr14 span subspan( index_type offset ) const gsl_noexcept
+    {
+        Expects( 0 <= offset && offset <= this->size() );
+        return span( this->data() + offset, this->size() - offset );
+    }
+
+    gsl_api gsl_constexpr14 span subspan( index_type offset, index_type count ) const gsl_noexcept
+    {
+        Expects(
+            0 <= offset && offset <= this->size() &&
+            0 <= count  && count + offset <= this->size() );
+        return span( this->data() + offset, count );
+    }
+
+    // 26.7.3.4 Observers [span.obs]
+
+    gsl_api gsl_constexpr index_type size() const gsl_noexcept
+    {
+        return narrow_cast<index_type>( last_ - first_ );
+    }
+
+    gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept
+    {
+        return size() * narrow_cast<index_type>( sizeof( element_type ) );
+    }
+
+    gsl_api gsl_constexpr bool empty() const gsl_noexcept
+    {
+        return size() == 0;
+    }
+
+    // 26.7.3.5 Element access [span.elem]
+
+    gsl_api gsl_constexpr reference operator[]( index_type index ) const
+    {
+       return at( index );
+    }
+
+    gsl_api gsl_constexpr reference operator()( index_type index ) const
+    {
+       return at( index );
+    }
+
+    gsl_api gsl_constexpr14 reference at( index_type index ) const
+    {
+       Expects( index < size() );
+       return first_[ index ];
+    }
+
+    gsl_api gsl_constexpr pointer data() const gsl_noexcept
+    {
+        return first_;
+    }
+
+    // 26.7.3.6 Iterator support [span.iterators]
+
+    gsl_api gsl_constexpr iterator begin() const gsl_noexcept
+    {
+        return iterator( first_ );
+    }
+
+    gsl_api gsl_constexpr iterator end() const gsl_noexcept
+    {
+        return iterator( last_ );
+    }
+
+    gsl_api gsl_constexpr const_iterator cbegin() const gsl_noexcept
+    {
+#if gsl_CPP11_OR_GREATER
+        return { begin() };
+#else
+        return const_iterator( begin() );
+#endif
+    }
+
+    gsl_api gsl_constexpr const_iterator cend() const gsl_noexcept
+    {
+#if gsl_CPP11_OR_GREATER
+        return { end() };
+#else
+        return const_iterator( end() );
+#endif
+    }
+
+    gsl_api gsl_constexpr reverse_iterator rbegin() const gsl_noexcept
+    {
+        return reverse_iterator( end() );
+    }
+
+    gsl_api gsl_constexpr reverse_iterator rend() const gsl_noexcept
+    {
+        return reverse_iterator( begin() );
+    }
+
+    gsl_api gsl_constexpr const_reverse_iterator crbegin() const gsl_noexcept
+    {
+        return const_reverse_iterator( cend() );
+    }
+
+    gsl_api gsl_constexpr const_reverse_iterator crend() const gsl_noexcept
+    {
+        return const_reverse_iterator( cbegin() );
+    }
+
+    gsl_api void swap( span & other ) gsl_noexcept
+    {
+        using std::swap;
+        swap( first_, other.first_ );
+        swap( last_ , other.last_  );
+    }
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 3 )
+    // member length() deprecated since 0.29.0
+
+    gsl_api gsl_constexpr index_type length() const gsl_noexcept
+    {
+        return size();
+    }
+
+    // member length_bytes() deprecated since 0.29.0
+
+    gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept
+    {
+        return size_bytes();
+    }
+#endif
+
+#if ! gsl_DEPRECATE_TO_LEVEL( 2 )
+    // member as_bytes(), as_writeable_bytes deprecated since 0.17.0
+
+    gsl_api span< const byte > as_bytes() const gsl_noexcept
+    {
+        return span< const byte >( reinterpret_cast<const byte *>( data() ), size_bytes() ); // NOLINT
+    }
+
+    gsl_api span< byte > as_writeable_bytes() const gsl_noexcept
+    {
+        return span< byte >( reinterpret_cast<byte *>( data() ), size_bytes() ); // NOLINT
+    }
+
+#endif
+
+    template< class U >
+    gsl_api span< U > as_span() const gsl_noexcept
+    {
+        Expects( ( this->size_bytes() % sizeof(U) ) == 0 );
+        return span< U >( reinterpret_cast<U *>( this->data() ), this->size_bytes() / sizeof( U ) ); // NOLINT
+    }
+
+private:
+    pointer first_;
+    pointer last_;
+};
+
+// class template argument deduction guides:
+
+#if gsl_HAVE( DEDUCTION_GUIDES )   // gsl_CPP17_OR_GREATER
+
+template< class T, size_t N >
+span( T (&)[N] ) -> span<T /*, N*/>;
+
+template< class T, size_t N >
+span( std::array<T, N> & ) -> span<T /*, N*/>;
+
+template< class T, size_t N >
+span( std::array<T, N> const & ) -> span<const T /*, N*/>;
+
+template< class Container >
+span( Container& ) -> span<typename Container::value_type>;
+
+template< class Container >
+span( Container const & ) -> span<const typename Container::value_type>;
+
+#endif // gsl_HAVE( DEDUCTION_GUIDES )
+
+// 26.7.3.7 Comparison operators [span.comparison]
+
+#if gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator==( span<T> const & l, span<U> const & r )
+{
+    return  l.size()  == r.size()
+        && (l.begin() == r.begin() || std::equal( l.begin(), l.end(), r.begin() ) );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator< ( span<T> const & l, span<U> const & r )
+{
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+
+#else
+
+template< class T >
+gsl_api inline gsl_constexpr bool operator==( span<T> const & l, span<T> const & r )
+{
+    return  l.size()  == r.size()
+        && (l.begin() == r.begin() || std::equal( l.begin(), l.end(), r.begin() ) );
+}
+
+template< class T >
+gsl_api inline gsl_constexpr bool operator< ( span<T> const & l, span<T> const & r )
+{
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+#endif
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator!=( span<T> const & l, span<U> const & r )
+{
+    return !( l == r );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator<=( span<T> const & l, span<U> const & r )
+{
+    return !( r < l );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator> ( span<T> const & l, span<U> const & r )
+{
+    return ( r < l );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr bool operator>=( span<T> const & l, span<U> const & r )
+{
+    return !( l < r );
+}
+
+// span algorithms
+
+namespace details {
+
+template< class II, class N, class OI >
+gsl_api inline OI copy_n( II first, N count, OI result )
+{
+    if ( count > 0 )
+    {
+        *result++ = *first;
+        for ( N i = 1; i < count; ++i )
+        {
+            *result++ = *++first;
+        }
+    }
+    return result;
+}
+}
+
+template< class T, class U >
+gsl_api inline void copy( span<T> src, span<U> dest )
+{
+#if gsl_CPP14_OR_GREATER // gsl_HAVE( TYPE_TRAITS ) (circumvent Travis clang 3.4)
+    static_assert( std::is_assignable<U &, T const &>::value, "Cannot assign elements of source span to elements of destination span" );
+#endif
+    Expects( dest.size() >= src.size() );
+    details::copy_n( src.data(), src.size(), dest.data() );
+}
+
+// span creator functions (see ctors)
+
+template< class T >
+gsl_api inline span< const byte > as_bytes( span<T> spn ) gsl_noexcept
+{
+    return span< const byte >( reinterpret_cast<const byte *>( spn.data() ), spn.size_bytes() ); // NOLINT
+}
+
+template< class T>
+gsl_api inline span< byte > as_writeable_bytes( span<T> spn ) gsl_noexcept
+{
+    return span< byte >( reinterpret_cast<byte *>( spn.data() ), spn.size_bytes() ); // NOLINT
+}
+
+#if gsl_FEATURE_TO_STD( MAKE_SPAN )
+
+template< class T >
+gsl_api inline gsl_constexpr span<T>
+make_span( T * ptr, typename span<T>::index_type count )
+{
+    return span<T>( ptr, count );
+}
+
+template< class T >
+gsl_api inline gsl_constexpr span<T>
+make_span( T * first, T * last )
+{
+    return span<T>( first, last );
+}
+
+template< class T, size_t N >
+gsl_api inline gsl_constexpr span<T>
+make_span( T (&arr)[N] )
+{
+    return span<T>( gsl_ADDRESSOF( arr[0] ), N );
+}
+
+#if gsl_HAVE( ARRAY )
+
+template< class T, size_t N >
+gsl_api inline gsl_constexpr span<T>
+make_span( std::array<T,N> & arr )
+{
+    return span<T>( arr );
+}
+
+template< class T, size_t N >
+gsl_api inline gsl_constexpr span<const T>
+make_span( std::array<T,N> const & arr )
+{
+    return span<const T>( arr );
+}
+#endif
+
+#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && gsl_HAVE( AUTO )
+
+template< class Container, class = decltype(std::declval<Container>().data()) >
+gsl_api inline gsl_constexpr auto
+make_span( Container & cont ) -> span< typename Container::value_type >
+{
+    return span< typename Container::value_type >( cont );
+}
+
+template< class Container, class = decltype(std::declval<Container>().data()) >
+gsl_api inline gsl_constexpr auto
+make_span( Container const & cont ) -> span< const typename Container::value_type >
+{
+    return span< const typename Container::value_type >( cont );
+}
+
+#else
+
+template< class T >
+gsl_api inline span<T>
+make_span( std::vector<T> & cont )
+{
+    return span<T>( with_container, cont );
+}
+
+template< class T >
+gsl_api inline span<const T>
+make_span( std::vector<T> const & cont )
+{
+    return span<const T>( with_container, cont );
+}
+#endif
+
+#if gsl_FEATURE_TO_STD( WITH_CONTAINER )
+
+template< class Container >
+gsl_api inline gsl_constexpr span<typename Container::value_type>
+make_span( with_container_t, Container & cont ) gsl_noexcept
+{
+    return span< typename Container::value_type >( with_container, cont );
+}
+
+template< class Container >
+gsl_api inline gsl_constexpr span<const typename Container::value_type>
+make_span( with_container_t, Container const & cont ) gsl_noexcept
+{
+    return span< const typename Container::value_type >( with_container, cont );
+}
+
+#endif // gsl_FEATURE_TO_STD( WITH_CONTAINER )
+
+template< class Ptr >
+gsl_api inline span<typename Ptr::element_type>
+make_span( Ptr & ptr )
+{
+    return span<typename Ptr::element_type>( ptr );
+}
+
+template< class Ptr >
+gsl_api inline span<typename Ptr::element_type>
+make_span( Ptr & ptr, typename span<typename Ptr::element_type>::index_type count )
+{
+    return span<typename Ptr::element_type>( ptr, count);
+}
+
+#endif // gsl_FEATURE_TO_STD( MAKE_SPAN )
+
+#if gsl_FEATURE_TO_STD( BYTE_SPAN )
+
+template< class T >
+gsl_api inline gsl_constexpr span<byte>
+byte_span( T & t ) gsl_noexcept
+{
+    return span<byte>( reinterpret_cast<byte *>( &t ), sizeof(T) );
+}
+
+template< class T >
+gsl_api inline gsl_constexpr span<const byte>
+byte_span( T const & t ) gsl_noexcept
+{
+    return span<const byte>( reinterpret_cast<byte const *>( &t ), sizeof(T) );
+}
+
+#endif // gsl_FEATURE_TO_STD( BYTE_SPAN )
+
+//
+// basic_string_span:
+//
+
+template< class T >
+class basic_string_span;
+
+namespace details {
+
+template< class T >
+struct is_basic_string_span_oracle : false_type {};
+
+template< class T >
+struct is_basic_string_span_oracle< basic_string_span<T> > : true_type {};
+
+template< class T >
+struct is_basic_string_span : is_basic_string_span_oracle< typename remove_cv<T>::type > {};
+
+template< class T >
+gsl_api inline gsl_constexpr14 std::size_t string_length( T * ptr, std::size_t max )
+{
+    if ( ptr == gsl_nullptr || max <= 0 )
+        return 0;
+
+    std::size_t len = 0;
+    while ( len < max && ptr[len] ) // NOLINT
+        ++len;
+
+    return len;
+}
+
+} // namespace details
+
+//
+// basic_string_span<> - A view of contiguous characters, replace (*,len).
+//
+template< class T >
+class basic_string_span
+{
+public:
+    typedef T element_type;
+    typedef span<T> span_type;
+
+    typedef typename span_type::index_type index_type;
+    typedef typename span_type::difference_type difference_type;
+
+    typedef typename span_type::pointer pointer ;
+    typedef typename span_type::reference reference ;
+
+    typedef typename span_type::iterator iterator ;
+    typedef typename span_type::const_iterator const_iterator ;
+    typedef typename span_type::reverse_iterator reverse_iterator;
+    typedef typename span_type::const_reverse_iterator const_reverse_iterator;
+
+    // construction:
+
+#if gsl_HAVE( IS_DEFAULT )
+    gsl_api gsl_constexpr basic_string_span() gsl_noexcept = default;
+#else
+    gsl_api gsl_constexpr basic_string_span() gsl_noexcept {}
+#endif
+
+#if gsl_HAVE( NULLPTR )
+    gsl_api gsl_constexpr basic_string_span( std::nullptr_t ptr ) gsl_noexcept
+    : span_( ptr, index_type( 0 ) )
+    {}
+#endif
+
+    gsl_api gsl_constexpr basic_string_span( pointer ptr )
+    : span_( remove_z( ptr, std::numeric_limits<index_type>::max() ) )
+    {}
+
+    gsl_api gsl_constexpr basic_string_span( pointer ptr, index_type count )
+    : span_( ptr, count )
+    {}
+
+    gsl_api gsl_constexpr basic_string_span( pointer firstElem, pointer lastElem )
+    : span_( firstElem, lastElem )
+    {}
+
+    template< std::size_t N >
+    gsl_api gsl_constexpr basic_string_span( element_type (&arr)[N] )
+    : span_( remove_z( gsl_ADDRESSOF( arr[0] ), N ) )
+    {}
+
+#if gsl_HAVE( ARRAY )
+
+    template< std::size_t N >
+    gsl_api gsl_constexpr basic_string_span( std::array< typename details::remove_const<element_type>::type, N> & arr )
+    : span_( remove_z( arr ) )
+    {}
+
+    template< std::size_t N >
+    gsl_api gsl_constexpr basic_string_span( std::array< typename details::remove_const<element_type>::type, N> const & arr )
+    : span_( remove_z( arr ) )
+    {}
+
+#endif
+
+#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+    // Exclude: array, [basic_string,] basic_string_span
+
+    template<
+        class Container,
+        class = typename std::enable_if<
+            ! details::is_std_array< Container >::value
+            && ! details::is_basic_string_span< Container >::value
+            && std::is_convertible< typename Container::pointer, pointer >::value
+            && std::is_convertible< typename Container::pointer, decltype(std::declval<Container>().data()) >::value
+        >::type
+    >
+    gsl_api gsl_constexpr basic_string_span( Container & cont )
+    : span_( ( cont ) )
+    {}
+
+    // Exclude: array, [basic_string,] basic_string_span
+
+    template<
+        class Container,
+        class = typename std::enable_if<
+            ! details::is_std_array< Container >::value
+            && ! details::is_basic_string_span< Container >::value
+            && std::is_convertible< typename Container::pointer, pointer >::value
+            && std::is_convertible< typename Container::pointer, decltype(std::declval<Container const &>().data()) >::value
+        >::type
+    >
+    gsl_api gsl_constexpr basic_string_span( Container const & cont )
+    : span_( ( cont ) )
+    {}
+
+#elif gsl_HAVE( UNCONSTRAINED_SPAN_CONTAINER_CTOR )
+
+    template< class Container >
+    gsl_api gsl_constexpr basic_string_span( Container & cont )
+    : span_( cont )
+    {}
+
+    template< class Container >
+    gsl_api gsl_constexpr basic_string_span( Container const & cont )
+    : span_( cont )
+    {}
+
+#else
+
+    template< class U >
+    gsl_api gsl_constexpr basic_string_span( span<U> const & rhs )
+    : span_( rhs )
+    {}
+
+#endif
+
+#if gsl_FEATURE_TO_STD( WITH_CONTAINER )
+
+    template< class Container >
+    gsl_api gsl_constexpr basic_string_span( with_container_t, Container & cont )
+    : span_( with_container, cont )
+    {}
+#endif
+
+#if gsl_HAVE( IS_DEFAULT )
+# if gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 440, 600 )
+    gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) = default;
+
+    gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) = default;
+# else
+    gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) gsl_noexcept = default;
+
+    gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) gsl_noexcept = default;
+# endif
+#endif
+
+    template< class U
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        , class = typename std::enable_if< std::is_convertible<typename basic_string_span<U>::pointer, pointer>::value >::type
+#endif
+    >
+    gsl_api gsl_constexpr basic_string_span( basic_string_span<U> const & rhs )
+    : span_( reinterpret_cast<pointer>( rhs.data() ), rhs.length() ) // NOLINT
+    {}
+
+#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120
+    template< class U
+        , class = typename std::enable_if< std::is_convertible<typename basic_string_span<U>::pointer, pointer>::value >::type
+    >
+    gsl_api gsl_constexpr basic_string_span( basic_string_span<U> && rhs )
+    : span_( reinterpret_cast<pointer>( rhs.data() ), rhs.length() ) // NOLINT
+    {}
+#endif
+
+    template< class CharTraits, class Allocator >
+    gsl_api gsl_constexpr basic_string_span(
+        std::basic_string< typename details::remove_const<element_type>::type, CharTraits, Allocator > & str )
+    : span_( gsl_ADDRESSOF( str[0] ), str.length() )
+    {}
+
+    template< class CharTraits, class Allocator >
+    gsl_api gsl_constexpr basic_string_span(
+        std::basic_string< typename details::remove_const<element_type>::type, CharTraits, Allocator > const & str )
+    : span_( gsl_ADDRESSOF( str[0] ), str.length() )
+    {}
+
+    // destruction, assignment:
+
+#if gsl_HAVE( IS_DEFAULT )
+    gsl_api ~basic_string_span() gsl_noexcept = default;
+
+    gsl_api basic_string_span & operator=( basic_string_span const & rhs ) gsl_noexcept = default;
+
+    gsl_api basic_string_span & operator=( basic_string_span && rhs ) gsl_noexcept = default;
+#endif
+
+    // sub span:
+
+    gsl_api gsl_constexpr basic_string_span first( index_type count ) const
+    {
+        return span_.first( count );
+    }
+
+    gsl_api gsl_constexpr basic_string_span last( index_type count ) const
+    {
+        return span_.last( count );
+    }
+
+    gsl_api gsl_constexpr basic_string_span subspan( index_type offset ) const
+    {
+        return span_.subspan( offset );
+    }
+
+    gsl_api gsl_constexpr basic_string_span subspan( index_type offset, index_type count ) const
+    {
+        return span_.subspan( offset, count );
+    }
+
+    // observers:
+
+    gsl_api gsl_constexpr index_type length() const gsl_noexcept
+    {
+        return span_.size();
+    }
+
+    gsl_api gsl_constexpr index_type size() const gsl_noexcept
+    {
+        return span_.size();
+    }
+
+    gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept
+    {
+        return span_.size_bytes();
+    }
+
+    gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept
+    {
+        return span_.size_bytes();
+    }
+
+    gsl_api gsl_constexpr bool empty() const gsl_noexcept
+    {
+        return size() == 0;
+    }
+
+    gsl_api gsl_constexpr reference operator[]( index_type idx ) const
+    {
+        return span_[idx];
+    }
+
+    gsl_api gsl_constexpr reference operator()( index_type idx ) const
+    {
+        return span_[idx];
+    }
+
+    gsl_api gsl_constexpr pointer data() const gsl_noexcept
+    {
+        return span_.data();
+    }
+
+    gsl_api iterator begin() const gsl_noexcept
+    {
+        return span_.begin();
+    }
+
+    gsl_api iterator end() const gsl_noexcept
+    {
+        return span_.end();
+    }
+
+    gsl_api reverse_iterator rbegin() const gsl_noexcept
+    {
+        return span_.rbegin();
+    }
+
+    gsl_api reverse_iterator rend() const gsl_noexcept
+    {
+        return span_.rend();
+    }
+
+    // const version not in p0123r2:
+
+    gsl_api const_iterator cbegin() const gsl_noexcept
+    {
+        return span_.cbegin();
+    }
+
+    gsl_api const_iterator cend() const gsl_noexcept
+    {
+        return span_.cend();
+    }
+
+    gsl_api const_reverse_iterator crbegin() const gsl_noexcept
+    {
+        return span_.crbegin();
+    }
+
+    gsl_api const_reverse_iterator crend() const gsl_noexcept
+    {
+        return span_.crend();
+    }
+
+private:
+    gsl_api static gsl_constexpr14 span_type remove_z( pointer const & sz, std::size_t max )
+    {
+        return span_type( sz, details::string_length( sz, max ) );
+    }
+
+#if gsl_HAVE( ARRAY )
+    template< size_t N >
+    gsl_api static gsl_constexpr14 span_type remove_z( std::array<typename details::remove_const<element_type>::type, N> & arr )
+    {
+        return remove_z( gsl_ADDRESSOF( arr[0] ), narrow_cast< std::size_t >( N ) );
+    }
+
+    template< size_t N >
+    gsl_api static gsl_constexpr14 span_type remove_z( std::array<typename details::remove_const<element_type>::type, N> const & arr )
+    {
+        return remove_z( gsl_ADDRESSOF( arr[0] ), narrow_cast< std::size_t >( N ) );
+    }
+#endif
+
+private:
+    span_type span_;
+};
+
+// basic_string_span comparison functions:
+
+#if gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator==( basic_string_span<T> const & l, U const & u ) gsl_noexcept
+{
+    const basic_string_span< typename details::add_const<T>::type > r( u );
+
+    return l.size() == r.size()
+        && std::equal( l.begin(), l.end(), r.begin() );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator<( basic_string_span<T> const & l, U const & u ) gsl_noexcept
+{
+    const basic_string_span< typename details::add_const<T>::type > r( u );
+
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator==( U const & u, basic_string_span<T> const & r ) gsl_noexcept
+{
+    const basic_string_span< typename details::add_const<T>::type > l( u );
+
+    return l.size() == r.size()
+        && std::equal( l.begin(), l.end(), r.begin() );
+}
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator<( U const & u, basic_string_span<T> const & r ) gsl_noexcept
+{
+    const basic_string_span< typename details::add_const<T>::type > l( u );
+
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+#endif
+
+#else //gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+
+template< class T >
+gsl_api inline gsl_constexpr14 bool operator==( basic_string_span<T> const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return l.size() == r.size()
+        && std::equal( l.begin(), l.end(), r.begin() );
+}
+
+template< class T >
+gsl_api inline gsl_constexpr14 bool operator<( basic_string_span<T> const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+
+#endif // gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator!=( basic_string_span<T> const & l, U const & r ) gsl_noexcept
+{
+    return !( l == r );
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator<=( basic_string_span<T> const & l, U const & r ) gsl_noexcept
+{
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) || ! gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+    return !( r < l );
+#else
+    basic_string_span< typename details::add_const<T>::type > rr( r );
+    return !( rr < l );
+#endif
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator>( basic_string_span<T> const & l, U const & r ) gsl_noexcept
+{
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) || ! gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON )
+    return ( r < l );
+#else
+    basic_string_span< typename details::add_const<T>::type > rr( r );
+    return ( rr < l );
+#endif
+}
+
+template< class T, class U >
+gsl_api inline gsl_constexpr14 bool operator>=( basic_string_span<T> const & l, U const & r ) gsl_noexcept
+{
+    return !( l < r );
+}
+
+#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator!=( U const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return !( l == r );
+}
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator<=( U const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return !( r < l );
+}
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator>( U const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return ( r < l );
+}
+
+template< class T, class U,
+    class = typename std::enable_if<!details::is_basic_string_span<U>::value >::type >
+gsl_api inline gsl_constexpr14 bool operator>=( U const & l, basic_string_span<T> const & r ) gsl_noexcept
+{
+    return !( l < r );
+}
+
+#endif // gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+
+// convert basic_string_span to byte span:
+
+template< class T >
+gsl_api inline span< const byte > as_bytes( basic_string_span<T> spn ) gsl_noexcept
+{
+    return span< const byte >( reinterpret_cast<const byte *>( spn.data() ), spn.size_bytes() ); // NOLINT
+}
+
+//
+// String types:
+//
+
+typedef char * zstring;
+typedef const char * czstring;
+
+#if gsl_HAVE( WCHAR )
+typedef wchar_t * zwstring;
+typedef const wchar_t * cwzstring;
+#endif
+
+typedef basic_string_span< char > string_span;
+typedef basic_string_span< char const > cstring_span;
+
+#if gsl_HAVE( WCHAR )
+typedef basic_string_span< wchar_t > wstring_span;
+typedef basic_string_span< wchar_t const > cwstring_span;
+#endif
+
+// to_string() allow (explicit) conversions from string_span to string
+
+#if 0
+
+template< class T >
+gsl_api inline std::basic_string< typename std::remove_const<T>::type > to_string( basic_string_span<T> spn )
+{
+     std::string( spn.data(), spn.length() );
+}
+
+#else
+
+gsl_api inline std::string to_string( string_span const & spn )
+{
+    return std::string( spn.data(), spn.length() );
+}
+
+gsl_api inline std::string to_string( cstring_span const & spn )
+{
+    return std::string( spn.data(), spn.length() );
+}
+
+#if gsl_HAVE( WCHAR )
+
+gsl_api inline std::wstring to_string( wstring_span const & spn )
+{
+    return std::wstring( spn.data(), spn.length() );
+}
+
+gsl_api inline std::wstring to_string( cwstring_span const & spn )
+{
+    return std::wstring( spn.data(), spn.length() );
+}
+
+#endif // gsl_HAVE( WCHAR )
+#endif // to_string()
+
+//
+// Stream output for string_span types
+//
+
+namespace details {
+
+template< class Stream >
+gsl_api void write_padding( Stream & os, std::streamsize n )
+{
+    for ( std::streamsize i = 0; i < n; ++i )
+        os.rdbuf()->sputc( os.fill() );
+}
+
+template< class Stream, class Span >
+gsl_api Stream & write_to_stream( Stream & os, Span const & spn )
+{
+    typename Stream::sentry sentry( os );
+
+    if ( !os )
+        return os;
+
+    const std::streamsize length = narrow<std::streamsize>( spn.length() );
+
+    // Whether, and how, to pad
+    const bool pad = ( length < os.width() );
+    const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
+
+    if ( left_pad )
+        write_padding( os, os.width() - length );
+
+    // Write span characters
+    os.rdbuf()->sputn( spn.begin(), length );
+
+    if ( pad && !left_pad )
+        write_padding( os, os.width() - length );
+
+    // Reset output stream width
+    os.width(0);
+
+    return os;
+}
+
+} // namespace details
+
+template< typename Traits >
+gsl_api std::basic_ostream< char, Traits > & operator<<( std::basic_ostream< char, Traits > & os, string_span const & spn )
+{
+    return details::write_to_stream( os, spn );
+}
+
+template< typename Traits >
+gsl_api std::basic_ostream< char, Traits > & operator<<( std::basic_ostream< char, Traits > & os, cstring_span const & spn )
+{
+    return details::write_to_stream( os, spn );
+}
+
+#if gsl_HAVE( WCHAR )
+
+template< typename Traits >
+gsl_api std::basic_ostream< wchar_t, Traits > & operator<<( std::basic_ostream< wchar_t, Traits > & os, wstring_span const & spn )
+{
+    return details::write_to_stream( os, spn );
+}
+
+template< typename Traits >
+gsl_api std::basic_ostream< wchar_t, Traits > & operator<<( std::basic_ostream< wchar_t, Traits > & os, cwstring_span const & spn )
+{
+    return details::write_to_stream( os, spn );
+}
+
+#endif // gsl_HAVE( WCHAR )
+
+//
+// ensure_sentinel()
+//
+// Provides a way to obtain a span from a contiguous sequence
+// that ends with a (non-inclusive) sentinel value.
+//
+// Will fail-fast if sentinel cannot be found before max elements are examined.
+//
+namespace details {
+
+template< class T, class SizeType, const T Sentinel >
+gsl_api static span<T> ensure_sentinel( T * seq, SizeType max = std::numeric_limits<SizeType>::max() )
+{
+    typedef T * pointer;
+
+    gsl_SUPPRESS_MSVC_WARNING( 26429, "f.23: symbol 'cur' is never tested for nullness, it can be marked as not_null" )
+
+    pointer cur = seq;
+
+    while ( static_cast<SizeType>( cur - seq ) < max && *cur != Sentinel )
+        ++cur;
+
+    Expects( *cur == Sentinel );
+
+    return span<T>( seq, narrow_cast< typename span<T>::index_type >( cur - seq ) );
+}
+} // namespace details
+
+//
+// ensure_z - creates a string_span for a czstring or cwzstring.
+// Will fail fast if a null-terminator cannot be found before
+// the limit of size_type.
+//
+
+template< class T >
+gsl_api inline span<T> ensure_z( T * const & sz, size_t max = std::numeric_limits<size_t>::max() )
+{
+    return details::ensure_sentinel<T, size_t, 0>( sz, max );
+}
+
+template< class T, size_t N >
+gsl_api inline span<T> ensure_z( T (&sz)[N] )
+{
+    return ensure_z( gsl_ADDRESSOF( sz[0] ), N );
+}
+
+# if gsl_HAVE( TYPE_TRAITS )
+
+template< class Container >
+gsl_api inline span< typename std::remove_pointer<typename Container::pointer>::type >
+ensure_z( Container & cont )
+{
+    return ensure_z( cont.data(), cont.length() );
+}
+# endif
+
+} // namespace gsl
+
+#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120
+
+namespace std {
+
+template<>
+struct hash< gsl::byte >
+{
+public:
+    std::size_t operator()( gsl::byte v ) const gsl_noexcept
+    {
+        return gsl::to_integer<std::size_t>( v );
+    }
+};
+
+} // namespace std
+
+#endif
+
+gsl_RESTORE_MSVC_WARNINGS()
+
+#endif // GSL_GSL_LITE_HPP_INCLUDED
+
+// end of file
diff --git a/main/src/BankTypes.cpp b/main/src/BankTypes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e2c5926cf8458de7213d31bf2b9c993d3fa8268
--- /dev/null
+++ b/main/src/BankTypes.cpp
@@ -0,0 +1,21 @@
+#include <map>
+#include <string>
+
+#include <BankTypes.h>
+
+namespace {
+   const std::map<BankTypes, std::string> BankNames = {{BankTypes::VP, "VP"},
+                                                       {BankTypes::UT, "UT"},
+                                                       {BankTypes::FT, "FTCluster"},
+                                                       {BankTypes::MUON, "Muon"}};
+}
+
+std::string bank_name(BankTypes type)
+{
+   auto it = BankNames.find(type);
+   if (it != end(BankNames)) {
+      return it->second;
+   } else {
+      return "Unknown";
+   }
+}
diff --git a/main/src/CheckVP.cpp b/main/src/CheckVP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e4d1ab3b5c020965fd31c8b26cd493a557f00890
--- /dev/null
+++ b/main/src/CheckVP.cpp
@@ -0,0 +1,49 @@
+#include <iostream>
+
+#include "Tools.h"
+
+bool check_velopix_events(
+  const std::vector<char>& events,
+  const std::vector<uint>& event_offsets,
+  size_t n_events
+) {
+  int error_count = 0;
+  int n_sps_all_events = 0;
+  for ( size_t i_event = 0; i_event < n_events; ++i_event ) {
+    const char* raw_input = events.data() + event_offsets[i_event];
+
+    const char* p = events.data() + event_offsets[i_event];
+    uint32_t number_of_raw_banks = *((uint32_t*)p); p += sizeof(uint32_t);
+    uint32_t* raw_bank_offset = (uint32_t*) p; p += number_of_raw_banks * sizeof(uint32_t);
+
+    uint32_t sensor =  *((uint32_t*)p);  p += sizeof(uint32_t);
+    uint32_t sp_count =  *((uint32_t*)p); p += sizeof(uint32_t);
+
+    const auto raw_event = VeloRawEvent(raw_input);
+    int n_sps_event = 0;
+    for ( int i_raw_bank = 0; i_raw_bank < raw_event.number_of_raw_banks; i_raw_bank++ ) {
+       const auto raw_bank = VeloRawBank(raw_event.payload + raw_event.raw_bank_offset[i_raw_bank]);
+      n_sps_event += raw_bank.sp_count;
+      if ( i_raw_bank != raw_bank.sensor_index ) {
+        error_cout << "at raw bank " << i_raw_bank << ", but index = " << raw_bank.sensor_index << std::endl;
+        ++error_count;
+      }
+      if ( raw_bank.sp_count > 0 ) {
+        uint32_t sp_word = raw_bank.sp_word[0];
+        uint8_t sp = sp_word & 0xFFU;
+        if (0 == sp) { continue; };
+        const uint32_t sp_addr = (sp_word & 0x007FFF00U) >> 8;
+        const uint32_t sp_row = sp_addr & 0x3FU;
+        const uint32_t sp_col = (sp_addr >> 6);
+        const uint32_t no_sp_neighbours = sp_word & 0x80000000U;
+      }
+    }
+    n_sps_all_events += n_sps_event;
+  }
+
+  if (error_count>0) {
+    error_cout << error_count << " errors detected." << std::endl;
+    return false;
+  }
+  return true;
+}
diff --git a/main/src/InputReader.cpp b/main/src/InputReader.cpp
index cb675d5925f3513f785599edddede5149ffb787b..2d19149de6d6ddba11c74a497ee3e729ad799809 100644
--- a/main/src/InputReader.cpp
+++ b/main/src/InputReader.cpp
@@ -1,5 +1,9 @@
 #include "InputReader.h"
 
+namespace {
+   using std::make_pair;
+}
+
 Reader::Reader(const std::string& folder_name) : folder_name(folder_name) {
   if (!exists_test(folder_name)) {
     throw StrException("Folder " + folder_name + " does not exist.");
@@ -19,32 +23,48 @@ std::vector<char> UTMagnetToolReader::read_UT_magnet_tool() {
 }
 
 void EventReader::read_events(uint number_of_events_requested, uint start_event_offset) {
-  std::vector<char> events;
-  std::vector<uint> event_offsets;
-
-  read_folder(
-    folder_name,
-    number_of_events_requested,
-    events,
-    event_offsets,
-    start_event_offset
-  );
-
-  check_events(events, event_offsets, number_of_events_requested);
-
-  // TODO Remove: Temporal check to understand if number_of_events_requested is the same as number_of_events
-  const int number_of_events = event_offsets.size() - 1;
-  if (number_of_events_requested != number_of_events) {
-    throw StrException("Number of events requested differs from number of events read.");
-  }
 
-  // Copy raw data to pinned host memory
-  cudaCheck(cudaMallocHost((void**)&host_events, events.size()));
-  cudaCheck(cudaMallocHost((void**)&host_event_offsets, event_offsets.size() * sizeof(uint)));
-  std::copy_n(std::begin(events), events.size(), host_events);
-  std::copy_n(std::begin(event_offsets), event_offsets.size(), host_event_offsets);
+   for (auto bank_type : types()) {
+      const auto& folder = this->folder(bank_type);
+
+      std::vector<char> events;
+      std::vector<uint> event_offsets;
+
+      read_folder(folder,
+                  number_of_events_requested,
+                  events,
+                  event_offsets,
+                  start_event_offset);
 
-  host_events_size = events.size();
-  host_event_offsets_size = event_offsets.size();
+      check_events(bank_type, events, event_offsets, number_of_events_requested);
+
+      // TODO Remove: Temporal check to understand if number_of_events_requested is the same as number_of_events
+      const int number_of_events = event_offsets.size() - 1;
+      if (number_of_events_requested != number_of_events) {
+         throw StrException("Number of events requested differs from number of events read.");
+      }
+
+      // Copy raw data to pinned host memory
+      char* events_mem = nullptr;
+      uint* offsets_mem = nullptr;
+      cudaCheck(cudaMallocHost((void**)&events_mem, events.size()));
+      cudaCheck(cudaMallocHost((void**)&offsets_mem, event_offsets.size() * sizeof(uint)));
+      std::copy_n(std::begin(events), events.size(), events_mem);
+      std::copy_n(std::begin(event_offsets), event_offsets.size(), offsets_mem);
+
+      m_events[bank_type] = make_pair(gsl::span<char>{events_mem, events.size()},
+                                      gsl::span<uint>{offsets_mem, event_offsets.size()});
+   }
 }
 
+bool EventReader::check_events(BankTypes type,
+                               const std::vector<char>& events,
+                               const std::vector<uint>& event_offsets,
+                               uint number_of_events_requested) const
+{
+   if (type == BankTypes::VP) {
+      return check_velopix_events(events, event_offsets, number_of_events_requested);
+   } else {
+      return events.size() == event_offsets.back();
+   }
+}
diff --git a/main/src/InputTools.cpp b/main/src/InputTools.cpp
index 2c674787d3339d9eaa3d126436daefa23c2b608a..70913a4a588a9c57e900c188c92c7152d355bd20 100644
--- a/main/src/InputTools.cpp
+++ b/main/src/InputTools.cpp
@@ -1,5 +1,79 @@
+#include <regex>
 #include "InputTools.h"
 
+namespace {
+
+  // Factory for filename checking: a regex and a predicate on the its matches
+  using factory = std::tuple<std::reference_wrapper<const std::regex>,
+                             std::function<bool(const std::smatch&)>>;
+
+  // Check binary files: they should match the regex and have n non-zero sub-matches
+  const std::regex bin_format{"(\\d+)(?:_(\\d+))?\\.bin"};
+  auto check_bin = [] (size_t n) -> factory {
+                     return {std::cref(bin_format),
+                             [n] (const std::smatch& matches) {
+                               return std::accumulate(begin(matches) + 1, end(matches), 0ul,
+                                                      [] (const auto& v, const auto& m) {
+                                                        return v + (m.length() != 0);
+                                                      }) == n;
+                             }};
+                   };
+
+  // Check mdf files: they should match the regex and have not-empty filename
+  const std::regex mdf_format{"(.+)\\.mdf"};
+  auto check_mdf = [] () -> factory {
+                     return {std::cref(mdf_format),
+                             [] (const std::smatch& matches) {
+                               return matches.size() == 2 && matches.length(1) > 0;
+                             }};
+                   };
+
+  // Check geometry files: they should match the regex.
+  const std::regex geom_format{".*geometry.*"};
+  auto check_geom = [] () -> factory {
+                     return {std::cref(geom_format),
+                             [] (const std::smatch& matches) {
+                               return matches.size() == 1;
+                             }};
+                    };
+
+  // Check all filenames using the regex and its match predicate
+  // returned by calling the factory function
+  auto check_names = [] (const auto& names, const factory& fact) {
+                       // Check if all all names have the right format and the same format
+                       const std::regex& expr = std::get<0>(fact).get();
+                       const auto& pred = std::get<1>(fact);
+                       return std::all_of(begin(names), end(names),
+                                          [&expr, &pred] (const auto& t) {
+                                            std::smatch matches;
+                                            auto s = std::regex_match(t, matches, expr);
+                                            // Check the predicate we've been given
+                                            return s && pred(matches);
+                                          });
+                     };
+
+  // Convert "N.bin" to (0, N) and "N_M.bin" to (N, M)
+  auto name_to_number = [] (const std::string& arg) -> std::pair<int, long long>
+    {
+     std::smatch m;
+     if (!std::regex_match(arg, m, bin_format)) {
+       return {0, 0};
+     } else if (m.length(2) == 0) {
+       return {0, std::stol(std::string{m[1].first, m[1].second})};
+     } else {
+       return {std::stoi(std::string{m[1].first, m[1].second}),
+               std::stol(std::string{m[2].first, m[2].second})};
+     }
+    };
+
+  // Sort in natural order by converting the filename to a pair of (int, long long)
+  auto natural_order = [] (const std::string& lhs, const std::string& rhs) -> bool {
+                         return std::less<std::pair<int, long long>>{}(name_to_number(lhs),
+                                                                       name_to_number(rhs));
+                       };
+
+};
+
 /**
  * @brief Test to check existence of filename.
  */
@@ -8,19 +82,6 @@ bool exists_test(const std::string& name) {
   return f.good();
 }
 
-/**
- * @brief Natural ordering for strings.
- */
-bool naturalOrder(const std::string& s1, const std::string& s2 ) {
-  size_t lastindex1 = s1.find_last_of("."); 
-  std::string raw1 = s1.substr(0, lastindex1);
-  size_t lastindex2 = s2.find_last_of("."); 
-  std::string raw2 = s2.substr(0, lastindex2);
-  int int1 = stoi(raw1, nullptr, 0);
-  int int2 = stoi(raw2, nullptr, 0);
-  return int1 < int2;
-}
-
 /**
  * @brief Read files into vectors.
  */
@@ -62,27 +123,39 @@ void appendFileToVector(
   event_sizes.push_back(dataSize);
   infile.close();
 }
- 
+
 
 std::vector<std::string> list_folder(
-  const std::string& foldername
+  const std::string& foldername,
+  const std::string& extension
 ) {
   std::vector<std::string> folderContents;
   DIR *dir;
   struct dirent *ent;
-
+  std::string suffix = std::string{"."} + extension;
   // Find out folder contents
   if ((dir = opendir(foldername.c_str())) != NULL) {
     /* print all the files and directories within directory */
     while ((ent = readdir(dir)) != NULL) {
-      std::string filename = std::string(ent->d_name);
-      if (filename.find(".bin") != std::string::npos &&
-          filename.find("geometry") == std::string::npos) 
-        folderContents.push_back(filename);
+      std::string filename = ent->d_name;
+      if (filename != "." && filename != "..") {
+        folderContents.emplace_back(filename);
+      }
     }
     closedir(dir);
     if (folderContents.size() == 0) {
-      error_cout << "No binary files found in folder " << foldername << std::endl;
+      error_cout << "No " << extension << " files found in folder " << foldername << std::endl;
+      exit(-1);
+    } else if (!check_names(folderContents, check_geom())
+               && !check_names(folderContents, check_bin(1))
+               && !check_names(folderContents, check_bin(2))
+               && !check_names(folderContents, check_mdf())) {
+      error_cout << "Not all files in the folder have the correct and the same filename format." << std::endl;
+      if (extension == ".bin") {
+        error_cout << "All files should be named N.bin or all files should be named N_M.bin" << std::endl;
+      } else {
+        error_cout << "All files should end with .mdf" << std::endl;
+      }
       exit(-1);
     } else {
       verbose_cout << "Found " << folderContents.size() << " binary files" << std::endl;
@@ -93,10 +166,7 @@ std::vector<std::string> list_folder(
   }
 
   // Sort folder contents (file names)
-  std::sort(folderContents.begin(), folderContents.end(), naturalOrder);
-  // for ( int i_file = 0; i_file < folderContents.size(); ++i_file) {
-  //   debug_cout << "file " << i_file << " is called " << folderContents[i_file] << std::endl;
-  //}
+  std::sort(folderContents.begin(), folderContents.end(), natural_order);
 
   return folderContents;
 }
@@ -142,7 +212,7 @@ void read_folder(
 
     event_offsets.push_back(accumulated_size);
     accumulated_size += event_sizes.back();
-    
+
     readFiles++;
     if ((readFiles % 100) == 0) {
       info_cout << "." << std::flush;
@@ -194,7 +264,7 @@ void read_UT_magnet_tool(
       pr_ut_magnet_tool->dxLayTable[i++] = deflection;
     }
   }
-  
+
   std::ifstream bdlfile;
   filename = folder_name + "/bdl.txt";
   if (!exists_test(filename)) {
diff --git a/main/src/MDFReader.cpp b/main/src/MDFReader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61493abc846f7a402407d4857ab72b37c255539c
--- /dev/null
+++ b/main/src/MDFReader.cpp
@@ -0,0 +1,62 @@
+#include <vector>
+#include <string>
+
+#include "MDFReader.h"
+
+#include "read_mdf.hpp"
+#include "odin.hpp"
+
+namespace {
+   using std::pair;
+   using std::string;
+   using std::vector;
+}
+
+void MDFReader::read_events(uint number_of_events_requested, uint start_event_offset) {
+
+   size_t n_read = 0;
+   LHCbToGPU::buffer_map buffers;
+   vector<LHCb::ODIN> odins;
+
+   auto bank_type = *begin(types());
+   auto foldername = folder(bank_type);
+   auto filenames = list_folder(foldername, "mdf");
+   vector<string> files;
+   files.reserve(filenames.size());
+   for (auto name : filenames) {
+      files.emplace_back(foldername + "/" + name);
+   }
+   std::tie(n_read, buffers, odins) = MDF::read_events(number_of_events_requested, files,
+                                                       types(), start_event_offset);
+
+   for (auto bank_type : types()) {
+      auto it = buffers.find(bank_type);
+      if (it == end(buffers)) {
+         throw StrException(string{"Cannot find buffer for bank type "} + bank_name(bank_type));
+      }
+      auto& entry = it->second;;
+      check_events(bank_type, entry.first, entry.second, number_of_events_requested);
+   }
+
+   // TODO Remove: Temporal check to understand if number_of_events_requested is the same as number_of_events
+   const int number_of_events = begin(buffers)->second.second.size() - 1;
+   if (number_of_events_requested != number_of_events) {
+      throw StrException("Number of events requested differs from number of events read.");
+   }
+
+   for (auto bank_type : types()) {
+      auto it = buffers.find(bank_type);
+      const auto& ev_buf = it->second.first;
+      const auto& offsets_buf = it->second.second;
+
+      // Copy raw data to pinned host memory
+      char* events_mem = nullptr;
+      uint* offsets_mem = nullptr;
+      cudaCheck(cudaMallocHost((void**)&events_mem, ev_buf.size()));
+      cudaCheck(cudaMallocHost((void**)&offsets_mem, offsets_buf.size() * sizeof(uint)));
+      std::copy_n(std::begin(ev_buf), ev_buf.size(), events_mem);
+      std::copy_n(std::begin(offsets_buf), offsets_buf.size(), offsets_mem);
+
+      add_events(bank_type, {events_mem, ev_buf.size()}, {offsets_mem, offsets_buf.size()});
+   }
+}
diff --git a/main/src/Tools.cpp b/main/src/Tools.cpp
index 3b3f7a586b62fc36c4da1ed991b725a8b871a9fc..3b45d7a885051c30327f3ee6ffda5dac4b1714bb 100644
--- a/main/src/Tools.cpp
+++ b/main/src/Tools.cpp
@@ -1,51 +1,5 @@
 #include "Tools.h"
 
-bool check_velopix_events(
-  const std::vector<char>& events,
-  const std::vector<uint>& event_offsets,
-  int n_events
-) {
-  int error_count = 0;
-  int n_sps_all_events = 0;
-  for ( int i_event = 0; i_event < n_events; ++i_event ) {
-    const char* raw_input = events.data() + event_offsets[i_event];
-
-    const char* p = events.data() + event_offsets[i_event];
-    uint32_t number_of_raw_banks = *((uint32_t*)p); p += sizeof(uint32_t);
-    uint32_t* raw_bank_offset = (uint32_t*) p; p += number_of_raw_banks * sizeof(uint32_t);
-
-    uint32_t sensor =  *((uint32_t*)p);  p += sizeof(uint32_t);
-    uint32_t sp_count =  *((uint32_t*)p); p += sizeof(uint32_t);
-
-    const auto raw_event = VeloRawEvent(raw_input);
-    int n_sps_event = 0;
-    for ( int i_raw_bank = 0; i_raw_bank < raw_event.number_of_raw_banks; i_raw_bank++ ) {
-      const auto raw_bank = VeloRawBank(raw_event.payload + raw_event.raw_bank_offset[i_raw_bank]);
-      n_sps_event += raw_bank.sp_count;
-      if ( i_raw_bank != raw_bank.sensor_index ) {
-        error_cout << "at raw bank " << i_raw_bank << ", but index = " << raw_bank.sensor_index << std::endl;
-        ++error_count;
-      }
-      if ( raw_bank.sp_count > 0 ) {
-        uint32_t sp_word = raw_bank.sp_word[0];
-        uint8_t sp = sp_word & 0xFFU;
-        if (0 == sp) { continue; };
-        const uint32_t sp_addr = (sp_word & 0x007FFF00U) >> 8;
-        const uint32_t sp_row = sp_addr & 0x3FU;
-        const uint32_t sp_col = (sp_addr >> 6);
-        const uint32_t no_sp_neighbours = sp_word & 0x80000000U;
-      }
-    }
-    n_sps_all_events += n_sps_event;
-  }
-
-  if (error_count>0) {
-    error_cout << error_count << " errors detected." << std::endl;
-    return false;
-  }
-  return true;
-}
-
 /**
  * @brief Obtains results statistics.
  */
@@ -86,24 +40,24 @@ std::vector<trackChecker::Tracks> prepareTracks(
   std::vector< trackChecker::Tracks > all_tracks; // all tracks from all events
   for ( uint i_event = 0; i_event < number_of_events; i_event++ ) {
     trackChecker::Tracks tracks; // all tracks within one event
-    
+
     const Velo::Consolidated::Tracks velo_tracks {host_velo_tracks_atomics, host_velo_track_hit_number_pinned, i_event, number_of_events};
     const uint number_of_tracks_event = velo_tracks.number_of_tracks(i_event);
 
     for ( uint i_track = 0; i_track < number_of_tracks_event; i_track++ ) {
       trackChecker::Track t;
-      
+
       const uint velo_track_number_of_hits = velo_tracks.number_of_hits(i_track);
       Velo::Consolidated::Hits velo_track_hits = velo_tracks.get_hits((uint*) host_velo_track_hits_pinned, i_track);
 
       for ( int i_hit = 0; i_hit < velo_track_number_of_hits; ++i_hit ) {
         t.addId(velo_track_hits.LHCbID[i_hit]);
-      } 
+      }
       tracks.push_back( t );
     } // tracks
     all_tracks.emplace_back( tracks );
   }
-  
+
   return all_tracks;
 }
 
@@ -185,9 +139,9 @@ trackChecker::Tracks prepareForwardTracksEvent(
     }
     checker_tracks.push_back( checker_track );
   }
-  
+
   return checker_tracks;
-} 
+}
 
 std::vector< trackChecker::Tracks > prepareVeloUTTracks(
   const VeloUTTracking::TrackUT* veloUT_tracks,
@@ -211,7 +165,7 @@ void call_pr_checker(
   const std::string& trackType
 ) {
   if ( trackType == "Velo" ) {
-    call_pr_checker_impl<TrackCheckerVelo> (
+    call_pr_checker_impl<TrackCheckerVelo>(
       all_tracks,
       folder_name_MC,
       start_event_offset,
@@ -236,3 +190,25 @@ void call_pr_checker(
   }
 }
 
+std::pair<size_t, std::string> set_device(int cuda_device) {
+  int n_devices = 0;
+  cudaDeviceProp device_properties;
+  cudaCheck(cudaGetDeviceCount(&n_devices));
+
+  debug_cout << "There are " << n_devices << " CUDA devices available" << std::endl;
+  for (int cd = 0; cd < n_devices; ++cd) {
+     cudaDeviceProp device_properties;
+     cudaCheck(cudaGetDeviceProperties(&device_properties, cd));
+     debug_cout << std::setw(3) << cd << " " << device_properties.name << std::endl;
+  }
+
+  if (cuda_device >= n_devices) {
+     error_cout << "Chosen device (" << cuda_device << ") is not available." << std::endl;
+     return {0, ""};
+  }
+  debug_cout << std::endl;
+
+  cudaCheck(cudaSetDevice(cuda_device));
+  cudaCheck(cudaGetDeviceProperties(&device_properties, cuda_device));
+  return {n_devices, device_properties.name};
+}
diff --git a/main/src/main.cpp b/main/src/main.cpp
index d236cb0a074ee243716a5609fa1f390e41d3c28b..e78dbd5c93a4342222b72f616aecab6c537cc1a2 100644
--- a/main/src/main.cpp
+++ b/main/src/main.cpp
@@ -18,6 +18,8 @@
 #include <algorithm>
 #include <stdio.h>
 #include <unistd.h>
+#include <getopt.h>
+
 #include "tbb/tbb.h"
 #include "cuda_runtime.h"
 #include "CudaCommon.h"
@@ -26,6 +28,7 @@
 #include "Tools.h"
 #include "InputTools.h"
 #include "InputReader.h"
+#include "MDFReader.h"
 #include "Timer.h"
 #include "StreamWrapper.cuh"
 #include "Constants.cuh"
@@ -33,26 +36,27 @@
 void printUsage(char* argv[]){
   std::cerr << "Usage: "
     << argv[0]
-    << std::endl << " -f {folder containing directories with raw bank binaries for every sub-detector}"
-    << std::endl << " -g {folder containing detector configuration}"
-    << std::endl << " -d {folder containing .bin files with MC truth information}"
-    << std::endl << " -n {number of events to process}=0 (all)"
-    << std::endl << " -o {offset of events from which to start}=0 (beginning)"
-    << std::endl << " -t {number of threads / streams}=1"
-    << std::endl << " -r {number of repetitions per thread / stream}=1"
-    << std::endl << " -c {run checkers}=0"
-    << std::endl << " -k {simplified kalman filter}=0"
-    << std::endl << " -m {reserve Megabytes}=1024"
-    << std::endl << " -v {verbosity}=3 (info)"
-    << std::endl << " -p {print memory usage}=0"
-    << std::endl << " -a {run only data preparation algorithms: decoding, clustering, sorting}=0"
-    << std::endl << " -x {run algorithms on x86 architecture if implementation is available}=0"
+    << std::endl << "  -f   {folder containing directories with raw bank binaries for every sub-detector}"
+    << std::endl << " --mdf {use MDF files as input instead of binary files}"
+    << std::endl << "  -g   {folder containing detector configuration}"
+    << std::endl << "  -d   {folder containing .bin files with MC truth information}"
+    << std::endl << "  -n   {number of events to process}=0 (all)"
+    << std::endl << "  -o   {offset of events from which to start}=0 (beginning)"
+    << std::endl << "  -t   {number of threads / streams}=1"
+    << std::endl << "  -r   {number of repetitions per thread / stream}=1"
+    << std::endl << "  -c   {run checkers}=0"
+    << std::endl << "  -k   {simplified kalman filter}=0"
+    << std::endl << "  -m   {reserve Megabytes}=1024"
+    << std::endl << "  -v   {verbosity}=3 (info)"
+    << std::endl << "  -p   {print memory usage}=0"
+    << std::endl << "  -a   {run only data preparation algorithms: decoding, clustering, sorting}=0"
+    << std::endl << "  -x   {run algorithms on x86 architecture if implementation is available}=0"
     << std::endl;
 }
 
 int main(int argc, char *argv[])
 {
-  std::string folder_name_raw_banks = "../input/minbias/banks/";
+  std::string folder_name_raw = "../input/minbias/banks/";
   std::string folder_name_MC = "../input/minbias/MC_info/";
   std::string folder_name_detector_configuration = "../input/detector_configuration/";
   uint number_of_events_requested = 0;
@@ -67,11 +71,37 @@ int main(int argc, char *argv[])
   bool run_on_x86 = false;
   size_t reserve_mb = 1024;
 
+  int use_mdf = 0;
+  int cuda_device = 0;
+  struct option long_options[] =
+     {
+      /* These options set a flag. */
+      {"mdf", no_argument,       &use_mdf, 1},
+      {"device", required_argument, &cuda_device, 0},
+      /* These options don’t set a flag.
+         We distinguish them by their indices. */
+      {0, 0, 0, 0}
+     };
+  /* getopt_long stores the option index here. */
+  int option_index = 0;
+
+
+
   signed char c;
-  while ((c = getopt(argc, argv, "f:d:n:o:t:r:pha:b:d:v:c:k:m:g:x")) != -1) {
+  while ((c = getopt_long(argc, argv, "f:d:n:o:t:r:pha:b:d:v:c:k:m:g:x",
+                          long_options, &option_index)) != -1) {
     switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0) {
+         if (long_options[option_index].name == "device" && optarg) {
+            cuda_device = atoi(optarg);
+         }
+         break;
+      }
+      /* If this option set a flag, do nothing else now. */
+      break;
     case 'f':
-      folder_name_raw_banks = std::string(optarg);
+      folder_name_raw = std::string(optarg);
       break;
     case 'd':
       folder_name_MC = std::string(optarg);
@@ -118,10 +148,10 @@ int main(int argc, char *argv[])
   }
 
   // Options sanity check
-  if (folder_name_raw_banks.empty() || folder_name_detector_configuration.empty() || (folder_name_MC.empty() && do_check)) {
+  if (folder_name_raw.empty() || folder_name_detector_configuration.empty() || (folder_name_MC.empty() && do_check)) {
     std::string missing_folder = "";
 
-    if (folder_name_raw_banks.empty()) missing_folder = "raw banks";
+    if (folder_name_raw.empty()) missing_folder = "raw banks";
     else if (folder_name_detector_configuration.empty()) missing_folder = "detector geometry";
     else if (folder_name_MC.empty() && do_check) missing_folder = "Monte Carlo";
 
@@ -134,13 +164,25 @@ int main(int argc, char *argv[])
   std::cout << std::fixed << std::setprecision(2);
   logger::ll.verbosityLevel = verbosity;
 
-  // Get device properties
-  cudaDeviceProp device_properties;
-  cudaCheck(cudaGetDeviceProperties(&device_properties, 0));
+  // Set device
+  size_t n_devices = 0;
+  std::string device_name;
+  try {
+    std::tie(n_devices, device_name) = set_device(cuda_device);
+    if (n_devices == 0) {
+      error_cout << "Failed to select device " << cuda_device << std::endl;
+      return -1;
+    }
+  } catch (const std::invalid_argument& e) {
+    error_cout << e.what() << std::endl;
+    error_cout << "Failed to select device " << cuda_device << std::endl;
+    return -1;
+  }
 
   // Show call options
   std::cout << "Requested options:" << std::endl
-    << " folder containing directories with raw bank binaries for every sub-detector (-f): " << folder_name_raw_banks << std::endl
+    << " folder containing directories with raw bank binaries for every sub-detector (-f): " << folder_name_raw << std::endl
+    << " using " << (use_mdf ? "MDF" : "binary") << " input" << (use_mdf ? " (--mdf)" : "") << std::endl
     << " folder with detector configuration (-g): " << folder_name_detector_configuration << std::endl
     << " folder with MC truth input (-d): " << folder_name_MC << std::endl
     << " run checkers (-c): " << do_check << std::endl
@@ -153,32 +195,39 @@ int main(int argc, char *argv[])
     << " run algorithms on x86 architecture if implementation is available (-x): " << run_on_x86 << std::endl
     << " print memory usage (-p): " << print_memory_usage << std::endl
     << " verbosity (-v): " << verbosity << std::endl
-    << " device: " << device_properties.name << std::endl
+    << " device (--device) " << cuda_device << ": " << device_name << std::endl
     << std::endl;
 
   // Read all inputs
   info_cout << "Reading input datatypes" << std::endl;
 
-  std::string folder_name_velopix_raw = folder_name_raw_banks + "VP"; 
+  std::string folder_name_velopix_raw = folder_name_raw + "VP";
   number_of_events_requested = get_number_of_events_requested(
     number_of_events_requested, folder_name_velopix_raw);
 
-  std::string folder_name_UT_raw = folder_name_raw_banks + "UT";
-  std::string folder_name_SciFi_raw = folder_name_raw_banks + "FTCluster";
+  std::string folder_name_UT_raw = folder_name_raw + "UT";
+  std::string folder_name_mdf = folder_name_raw + "mdf";
+  std::string folder_name_SciFi_raw = folder_name_raw + "FTCluster";
   auto geometry_reader = GeometryReader(folder_name_detector_configuration);
   auto ut_magnet_tool_reader = UTMagnetToolReader(folder_name_detector_configuration);
-  auto velo_reader = VeloReader(folder_name_velopix_raw);
-  auto ut_reader = EventReader(folder_name_UT_raw);
-  auto scifi_reader = EventReader(folder_name_SciFi_raw);
 
+  std::unique_ptr<EventReader> event_reader;
+  if (use_mdf) {
+     event_reader = std::make_unique<MDFReader>(FolderMap{{{BankTypes::VP, folder_name_mdf},
+                                                           {BankTypes::UT, folder_name_mdf},
+                                                           {BankTypes::FT, folder_name_mdf}}});
+  } else {
+     event_reader = std::make_unique<EventReader>(FolderMap{{{BankTypes::VP, folder_name_velopix_raw},
+                                                             {BankTypes::UT, folder_name_UT_raw},
+                                                             {BankTypes::FT, folder_name_SciFi_raw}}});
+  }
   std::vector<char> velo_geometry = geometry_reader.read_geometry("velo_geometry.bin");
   std::vector<char> ut_boards = geometry_reader.read_geometry("ut_boards.bin");
   std::vector<char> ut_geometry = geometry_reader.read_geometry("ut_geometry.bin");
   std::vector<char> ut_magnet_tool = ut_magnet_tool_reader.read_UT_magnet_tool();
   std::vector<char> scifi_geometry = geometry_reader.read_geometry("scifi_geometry.bin");
-  velo_reader.read_events(number_of_events_requested, start_event_offset);
-  ut_reader.read_events(number_of_events_requested, start_event_offset);
-  scifi_reader.read_events(number_of_events_requested, start_event_offset);
+
+  event_reader->read_events(number_of_events_requested, start_event_offset);
 
   info_cout << std::endl << "All input datatypes successfully read" << std::endl << std::endl;
 
@@ -215,18 +264,18 @@ int main(int argc, char *argv[])
     static_cast<uint>(tbb_threads),
     [&] (uint i) {
       auto runtime_options = RuntimeOptions{
-        velo_reader.host_events,
-        velo_reader.host_event_offsets,
-        velo_reader.host_events_size,
-        velo_reader.host_event_offsets_size,
-        ut_reader.host_events,
-        ut_reader.host_event_offsets,
-        ut_reader.host_events_size,
-        ut_reader.host_event_offsets_size,
-        scifi_reader.host_events,
-        scifi_reader.host_event_offsets,
-        scifi_reader.host_events_size,
-        scifi_reader.host_event_offsets_size,
+        event_reader->events(BankTypes::VP).begin(),
+        event_reader->offsets(BankTypes::VP).begin(),
+        event_reader->events(BankTypes::VP).size(),
+        event_reader->offsets(BankTypes::VP).size(),
+        event_reader->events(BankTypes::UT).begin(),
+        event_reader->offsets(BankTypes::UT).begin(),
+        event_reader->events(BankTypes::UT).size(),
+        event_reader->offsets(BankTypes::UT).size(),
+        event_reader->events(BankTypes::FT).begin(),
+        event_reader->offsets(BankTypes::FT).begin(),
+        event_reader->events(BankTypes::FT).size(),
+        event_reader->offsets(BankTypes::FT).size(),
         number_of_events_requested,
         number_of_repetitions};
 
diff --git a/mdf/CMakeLists.txt b/mdf/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c64397875fa70bc3c70e4b05727718f2f2d770cb
--- /dev/null
+++ b/mdf/CMakeLists.txt
@@ -0,0 +1,67 @@
+set(SOURCES
+  src/compression.cpp
+  src/raw_helpers.cpp
+  src/read_mdf.cpp)
+
+add_library(mdf ${SOURCES})
+target_include_directories (mdf PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+target_include_directories(
+  mdf PUBLIC
+  ${CMAKE_SOURCE_DIR}/main/include
+  ${ZLIB_INCLUDE_DIRS})
+
+target_link_libraries(
+  mdf PUBLIC
+  ${ZLIB_LIBRARIES})
+
+if (LZMA_FOUND)
+  target_include_directories(
+	mdf PUBLIC
+	${LIBLZMA_INCLUDE_DIRS})
+
+  target_link_libraries(
+	mdf PUBLIC
+	${LIBLZMA_LIBRARIES})
+
+  target_compile_definitions(
+	mdf	PRIVATE
+	"-DHAVE_LZMA")
+endif (LZMA_FOUND)
+
+if (LZ4_FOUND)
+  target_include_directories(
+	mdf PUBLIC
+	${LIBLZ4_INCLUDE_DIRS})
+
+  target_link_libraries(
+	mdf
+	${LIBLZ4_LIBRARIES})
+
+  target_compile_definitions(
+	mdf	PRIVATE
+	"-DHAVE_LZ4")
+endif (LZ4_FOUND)
+
+macro(test_program name)
+cuda_add_executable(${name}
+  test/${name}.cpp)
+
+target_include_directories(${name} PUBLIC
+  ${CMAKE_SOURCE_DIR}/mdf/include
+  ${CMAKE_SOURCE_DIR}/main/include
+  ${CMAKE_SOURCE_DIR}/cuda/event_model/velo/include
+  ${CMAKE_SOURCE_DIR}/cuda/event_model/common/include
+  ${CMAKE_SOURCE_DIR}/cuda/velo/common/include
+  ${CMAKE_SOURCE_DIR}/cuda/SciFi/common/include
+  ${CMAKE_SOURCE_DIR}/cuda/SciFi/PrForward/include
+  ${CMAKE_SOURCE_DIR}/cuda/UT/common/include
+  ${CMAKE_SOURCE_DIR}/cuda/UT/PrVeloUT/include
+  ${CMAKE_SOURCE_DIR}/checker/tracking/include)
+
+target_link_libraries(${name} Common Velo)
+endmacro()
+
+test_program(test_read)
+test_program(test_read_bin)
+test_program(dump_banks)
diff --git a/mdf/include/compression.hpp b/mdf/include/compression.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a3a101391ade2d6715ef26d18e9f9596f55d497
--- /dev/null
+++ b/mdf/include/compression.hpp
@@ -0,0 +1,42 @@
+// @(#)root/zip:$Id$
+// Author: Sergey Linev   7 July 2014
+
+/*************************************************************************
+ * Copyright (C) 1995-2014, Rene Brun and Fons Rademakers.               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * For the licensing terms see $ROOTSYS/LICENSE.                         *
+ * For the list of contributors see $ROOTSYS/README/CREDITS.             *
+ *************************************************************************/
+// #include "Compression.h"
+
+/**
+ * These are definitions of various free functions for the C-style compression routines in ROOT.
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H 1
+
+namespace Compression {
+
+unsigned long crc32(unsigned long crc, const unsigned char* buf, unsigned int len);
+
+void unzip(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep);
+
+int unzip_header(int *srcsize, unsigned char *src, int *tgtsize);
+
+void unzipZLIB(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep);
+
+#ifdef HAVE_LZMA
+void unzipLZMA(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep);
+#endif
+
+#ifdef HAVE_LZ4
+void unzipLZ4(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep);
+#endif
+
+enum { kMAXZIPBUF = 0xffffff };
+
+}
+
+#endif
diff --git a/mdf/include/mdf_header.hpp b/mdf/include/mdf_header.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c674fc1511a77747c3e569922ef63c322ed63a74
--- /dev/null
+++ b/mdf/include/mdf_header.hpp
@@ -0,0 +1,172 @@
+#ifndef MDFHEADER
+#define MDFHEADER
+
+#include <stdexcept>
+#define DAQ_ERR_BANK_VERSION 0
+#define DAQ_STATUS_BANK      16
+#define DAQ_PROCERR_HEADER   32
+#define DAQ_PROCERR_BANK     33
+#define DAQ_FILEID_BANK      255
+
+#define MDFHEADER_ALIGNED(x) x __attribute__((__packed__))
+
+/*
+ *   LHCb namespace
+ */
+namespace LHCb    {
+
+  /** @struct MDFHeader  MDFHeader.h  MDF/MDFHeader.h
+    *
+    * Structure describing the header structure preceding each
+    * event buffer in MDF files.
+    *
+    * Known versions:
+    * 0   : VELO testbeam  [early version]
+    * 1   : RICH/MUON/OTR test beam
+    * 2   : Empty specific header
+    * 3   : New version (like 1, but with data type)
+    *
+    * Known data types:
+    * 1 BODY_TYPE_BANKS
+    * 2 BODY_TYPE_MEP
+    *
+    * Caution:
+    * The data member need to be aligned in a way that the compiler
+    * does not inject additional padding !
+    *
+    * @author  M.Frank
+    * @version 1.0
+    *
+    */
+  MDFHEADER_ALIGNED(class) MDFHeader  {
+  public:
+    enum { BODY_TYPE_BANKS=1, BODY_TYPE_MEP=2 };
+
+    MDFHEADER_ALIGNED(struct) HeaderTriggerMask  {
+      /// Trigger mask used for event selection
+      unsigned int   m_trMask[4];
+      HeaderTriggerMask() {
+        m_trMask[0] = m_trMask[1] = m_trMask[2] = m_trMask[3] = 0;
+      }
+      /// Accessor: Number of bits in the trigger mask
+      unsigned int  maskBits() const         { return sizeof(m_trMask)*8;        }
+      /// Accessor: trigger mask
+      const unsigned int* triggerMask() const{ return m_trMask;                  }
+      /// Update the trigger mask of the event
+      void setTriggerMask(const unsigned int* mask){
+        m_trMask[0] = mask[0];
+        m_trMask[1] = mask[1];
+        m_trMask[2] = mask[2];
+        m_trMask[3] = mask[3];
+      }
+    };
+
+    MDFHEADER_ALIGNED(struct) Header1  : public HeaderTriggerMask  {
+      /// Run number
+      unsigned int   m_runNumber = 0;
+      /// Orbit counter
+      unsigned int   m_orbitCount = 0;
+      /// Bunch identifier
+      unsigned int   m_bunchID = 0;
+      /// Set run number
+      void setRunNumber(unsigned int runno)   { m_runNumber  = runno;   }
+      /// Set orbit counter
+      void setOrbitNumber(unsigned int orbno) { m_orbitCount = orbno;   }
+      /// Set bunch identifier
+      void setBunchID(unsigned int bid)       { m_bunchID    = bid;     }
+      /// Access run number
+      unsigned int runNumber() const          { return m_runNumber;     }
+      /// Access run number
+      unsigned int orbitNumber() const        { return m_orbitCount;    }
+      /// Access run number
+      unsigned int bunchID()   const          { return m_bunchID;       }
+    };
+
+
+    /// Data member indicating the size of the event
+    unsigned int   m_size[3];
+    /// Optional checksum over the event data (if 0, no checksum was calculated)
+    unsigned int   m_checkSum;
+    /// Identifier of the compression algorithm used to compress the data buffer
+    unsigned char  m_compression;
+    /// Header type: split into { version:4, length:4 } for possible future upgrade
+    unsigned char  m_hdr;
+    /// Data type
+    unsigned char  m_dataType;
+    /// Spare
+    Header1  m_subHeader;
+
+
+  public:
+    static unsigned int sizeOf(int hdr_type)  {
+      switch(hdr_type)  {
+        case 3:
+           return sizeof(MDFHeader)+sizeof(Header1);
+        default:
+          throw std::runtime_error("Unknown MDF header type!");
+      }
+    }
+    /// Default constructor
+    MDFHeader() : m_checkSum(0), m_compression(0), m_hdr(0), m_dataType(0)
+    {
+      m_size[0] = m_size[1] = m_size[2] = 0;
+      setSubheaderLength(0);
+    }
+    /// Default destructor
+    ~MDFHeader()  {}
+    /// Access record size
+    unsigned int  recordSize()  const      { return m_size[0];                 }
+    /// Accessor: event size
+    unsigned int  size() const
+    { return m_size[0]-sizeOf(headerVersion());                                }
+    /// Update event size
+    void setSize(unsigned int val)
+    { m_size[0]=m_size[1]=m_size[2]=val+sizeOf(headerVersion());               }
+    /// For checks: return 0th. size word
+    unsigned int size0() const             { return m_size[0];                 }
+    /// For checks: return 1rst. size word
+    unsigned int size1() const             { return m_size[1];                 }
+    /// For checks: return 2nd. size word
+    unsigned int size2() const             { return m_size[2];                 }
+    /// For special stuff: modify 3rd. size word by hand
+    void setSize2(unsigned int val)        { m_size[2] = val;                  }
+    /// Accessor: checksum of the event data
+    unsigned int  checkSum() const         { return m_checkSum;                }
+    /// Update checksum of the event data
+    void setChecksum(unsigned int val)     { m_checkSum = val;                 }
+    /// Accessor: Identifier of the compression method
+    unsigned char compression() const      { return m_compression;             }
+    /// Update the identifier of the compression method
+    void setCompression(unsigned int val)  { m_compression=(unsigned char)val; }
+    /// Accessor: length of the event header
+    unsigned int subheaderLength() const   { return (m_hdr&0x0F)*sizeof(int);  }
+    /// Update the length of the event header
+    void setSubheaderLength(unsigned int l)  {
+       l = (l%sizeof(int)) ? (l/sizeof(int)) + 1 : l/sizeof(int);
+       m_hdr = (unsigned char)((0xF0&m_hdr) + (0x0F&l));
+    }
+    /// Accessor: version of the event header
+    unsigned int  headerVersion() const    { return m_hdr>>4;                  }
+    /// Update the version of the event header
+    void setHeaderVersion(unsigned int vsn)
+    {  m_hdr = (unsigned char)(((vsn<<4)+(m_hdr&0xF))&0xFF);                   }
+    /// Accessor: hdr field
+    unsigned char hdr() const              { return m_hdr;                     }
+    /// Update hdr field
+    void setHdr(unsigned char val)         { m_hdr = val;                      }
+    /// Accessor: event type identifier
+    unsigned char dataType() const         { return m_dataType;                }
+    /// Update the event type
+    void setDataType(unsigned char val)    { m_dataType = val;                 }
+    /// Access to data payload (Header MUST be initialized)
+    char* data() {  return ((char*)this)+sizeOf(headerVersion());              }
+    /// Access to data payload (Header MUST be initialized)
+    const char* data() const {  return ((char*)this)+sizeOf(headerVersion());  }
+
+    /// Access to sub-headers
+    Header1 subHeader()                  { return m_subHeader;      }
+  };
+}    // End namespace LHCb
+
+#undef MDFHEADER_ALIGNED
+#endif // EVENT_MDFHEADER
diff --git a/mdf/include/odin.hpp b/mdf/include/odin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..06cd1d14a8cf71a028db5829210956639d0709b6
--- /dev/null
+++ b/mdf/include/odin.hpp
@@ -0,0 +1,38 @@
+
+#ifndef ODIN_H
+#define ODIN_H 1
+
+namespace LHCb {
+struct ODIN final {
+   enum Data{ RunNumber = 0,
+              EventType,
+              OrbitNumber,
+              L0EventIDHi,
+              L0EventIDLo,
+              GPSTimeHi,
+              GPSTimeLo,
+              Word7,
+              Word8,
+              TriggerConfigurationKey
+   };
+
+   enum EventTypeBitsEnum{ EventTypeBits = 0,
+                           CalibrationStepBits = 16
+   };
+
+   enum EventTypeMasks{ EventTypeMask       = 0x0000FFFF,
+                        CalibrationStepMask = 0xFFFF0000,
+                        FlaggingModeMask    = 0x00008000
+   };
+
+   unsigned int run_number;
+   unsigned int event_type;
+   unsigned int orbit_number;
+   unsigned long long event_number;
+   unsigned int version;
+   unsigned int calibration_step;
+   unsigned int tck;
+};
+}
+
+#endif
diff --git a/mdf/include/raw_bank.hpp b/mdf/include/raw_bank.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bff3fcdadcf4edf18101e346711408fcf68a65e3
--- /dev/null
+++ b/mdf/include/raw_bank.hpp
@@ -0,0 +1,197 @@
+#ifndef RAWBANK_H
+#define RAWBANK_H 1
+
+#include <string>
+#include <vector>
+
+/** @class LHCb::RawBank RawBank.h
+  *
+  * Raw data bank sent by the TELL1 boards of the LHCb DAQ.
+  *
+  * For a detailed description of the raw bank format,
+  * see <a href="https://edms.cern.ch/document/565851/5">EDMS-565851/5</a>
+  *
+  * Note concerning the changes done 06/03/2006:
+  * - The bank size is in BYTES
+  * - The full size of a bank in memory is long word (32 bit) aligned
+  *   ie. a bank of 17 Bytes length actually uses 20 Bytes in memory.
+  * - The bank length accessors size() and setSize() do not contain
+  *   the bank header, ie. size() = (total size) - (header size).
+  * - The length passed to the RawEvent::createBank should NOT
+  *   contain the size of the header !
+  * - If the full padded bank size is required use the utility
+  *   function RawBank::totalSize = size + header size + padding size.
+  *
+  * @author Helder Lopes
+  * @author Markus Frank
+  * created Tue Oct 04 14:45:20 2005
+  *
+  */
+namespace LHCb
+{
+
+  class RawBank  {
+  private:
+    /// Default Constructor
+    RawBank() {}
+
+    /// Default Destructor
+    ~RawBank() {}
+
+  public:
+
+    /// Define bank types for RawBank
+    enum BankType{ L0Calo=0,        //  0
+                   L0DU,            //  1
+                   PrsE,            //  2
+                   EcalE,           //  3
+                   HcalE,           //  4
+                   PrsTrig,         //  5
+                   EcalTrig,        //  6
+                   HcalTrig,        //  7
+                   Velo,            //  8
+                   Rich,            //  9
+                   TT,              // 10
+                   IT,              // 11
+                   OT,              // 12
+                   Muon,            // 13
+                   L0PU,            // 14
+                   DAQ,             // 15
+                   ODIN,            // 16
+                   HltDecReports,   // 17
+                   VeloFull,        // 18
+                   TTFull,          // 19
+                   ITFull,          // 20
+                   EcalPacked,      // 21
+                   HcalPacked,      // 22
+                   PrsPacked,       // 23
+                   L0Muon,          // 24
+                   ITError,         // 25
+                   TTError,         // 26
+                   ITPedestal,      // 27
+                   TTPedestal,      // 28
+                   VeloError,       // 29
+                   VeloPedestal,    // 30
+                   VeloProcFull,    // 31
+                   OTRaw,           // 32
+                   OTError,         // 33
+                   EcalPackedError, // 34
+                   HcalPackedError, // 35
+                   PrsPackedError,  // 36
+                   L0CaloFull,      // 37
+                   L0CaloError,     // 38
+                   L0MuonCtrlAll,   // 39
+                   L0MuonProcCand,  // 40
+                   L0MuonProcData,  // 41
+                   L0MuonRaw,       // 42
+                   L0MuonError,     // 43
+                   GaudiSerialize,  // 44
+                   GaudiHeader,     // 45
+                   TTProcFull,      // 46
+                   ITProcFull,      // 47
+                   TAEHeader,       // 48
+                   MuonFull,        // 49
+                   MuonError,       // 50
+                   TestDet,         // 51
+                   L0DUError,       // 52
+                   HltRoutingBits,  // 53
+                   HltSelReports,   // 54
+                   HltVertexReports,// 55
+                   HltLumiSummary,  // 56
+                   L0PUFull,        // 57
+                   L0PUError,       // 58
+                   DstBank,         // 59
+                   DstData,         // 60
+                   DstAddress,      // 61
+                   FileID,          // 62
+                   VP,              // 63
+                   FTCluster,       // 64
+                   VL,              // 65
+                   UT,              // 66
+                   UTFull,          // 67
+                   UTError,         // 68
+                   UTPedestal,      // 69
+                   HC,              // 70
+                   HltTrackReports, // 71
+                   HCError,         // 72
+                   // Add new types here. Don't forget to update also RawBank.cpp
+                   LastType    // LOOP Marker; add new bank types ONLY before!
+    };
+
+    /// Magic pattern for Raw bank headers
+    enum RawPattern{ MagicPattern=0xCBCB };
+
+    /// Access to magic word for integrity check
+    int magic()  const              {    return m_magic;            }
+
+    /// Set magic word
+    void setMagic()                 {    m_magic = MagicPattern;    }
+
+    /// Header size
+    int hdrSize()  const            {    return sizeof(RawBank)-sizeof(m_data);}
+
+    /// Return size of the data body part of the bank
+    int size() const                {    return m_length-hdrSize();            }
+
+    /// Set data size of the bank in bytes
+    void setSize(size_t val)        {    m_length = (val&0xFFFF)+hdrSize();    }
+
+    /// Access the full (padded) size of the bank
+    int totalSize() const           {
+      typedef unsigned int T;
+      return m_length%sizeof(T)==0 ? m_length : (m_length/sizeof(T)+1)*sizeof(T);
+    }
+    /// Return bankType of this bank
+    BankType type() const           {    return BankType(int(m_type));              }
+
+    /// Set the bank type
+    void setType(BankType val)      {    m_type = (unsigned char)(char(val)&0xFF);  }
+
+    /// Return version of this bank
+    int version() const             {    return m_version;                          }
+
+    /// Set the version information of this bank
+    void setVersion(int val)        {    m_version = (unsigned char)(val&0xFF);     }
+
+    /// Return SourceID of this bank  (TELL1 board ID)
+    int sourceID() const            {    return m_sourceID;                         }
+
+    /// Set the source ID of this bank (TELL1 board ID)
+    void setSourceID(int val)       {    m_sourceID = (unsigned short)(val&0xFFFF); }
+
+    /// Return pointer to begining of data body of the bank
+    unsigned int* data()            {    return &m_data[0];         }
+
+    /// Return pointer to begining of data body of the bank (const data access)
+    const unsigned int* data() const {   return &m_data[0];         }
+
+    /// Begin iterator
+    template <typename T> T* begin(){    return (T*)m_data;         }
+
+    /// End iterator
+    template <typename T> T* end()  {    return ((T*)m_data) + size()/sizeof(T);  }
+
+    /// Begin iterator over const iteration
+    template <typename T> const T* begin() const {return (T*)m_data;}
+
+    /// End iterator of const iteration
+    template <typename T>  const T* end() const  {return ((T*)m_data) + size()/sizeof(T);  }
+
+  private:
+    /// Magic word (by definition 0xCBCB)
+    unsigned short m_magic = MagicPattern;
+    /// Bank length in bytes (must be >= 0)
+    unsigned short m_length = 0;
+    /// Bank type (must be >= 0)
+    unsigned char  m_type = 0;
+    /// Version identifier (must be >= 0)
+    unsigned char  m_version = 0;
+    /// Source ID (valid source IDs are > 0; invalid ones 0)
+    short          m_sourceID = 0;
+    /// Opaque data block
+     unsigned int   m_data[1] = {0};
+  }; // class RawBank
+
+} // namespace LHCb
+
+#endif ///DAQEvent_RawBank_H
diff --git a/mdf/include/raw_helpers.hpp b/mdf/include/raw_helpers.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2bd76702979d9158ab81aa102856bf49ad8e483b
--- /dev/null
+++ b/mdf/include/raw_helpers.hpp
@@ -0,0 +1,20 @@
+#ifndef RAW_HELPERS_H
+#define RAW_HELPERS_H 1
+
+#include <iostream>
+
+namespace LHCb  {
+
+  // Forward declarations
+  unsigned int hash32Checksum(const void* ptr, size_t len);
+  unsigned int adler32Checksum(unsigned int old, const char *buf, size_t len);
+
+  /// Generate XOR Checksum
+  unsigned int genChecksum(int flag, const void* ptr, size_t len);
+
+  bool decompressBuffer(int algtype, unsigned char* tar, size_t tar_len,
+                        unsigned char* src, size_t src_len, size_t& new_len);
+
+}
+
+#endif
diff --git a/mdf/include/read_mdf.hpp b/mdf/include/read_mdf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..19137fc57c01de9f643d258982e8ee4a9a84cda6
--- /dev/null
+++ b/mdf/include/read_mdf.hpp
@@ -0,0 +1,47 @@
+#ifndef READ_MDF_H
+#define READ_MDF_H 1
+
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <BankTypes.h>
+
+#include <gsl-lite.hpp>
+
+#include "odin.hpp"
+#include "raw_bank.hpp"
+#include "mdf_header.hpp"
+
+namespace LHCbToGPU {
+   const std::unordered_map<LHCb::RawBank::BankType, BankTypes> bank_types =
+      {{LHCb::RawBank::VP, BankTypes::VP},
+       {LHCb::RawBank::UT, BankTypes::UT},
+       {LHCb::RawBank::FTCluster, BankTypes::FT},
+       {LHCb::RawBank::Muon, BankTypes::MUON}};
+
+   using buffer_map = std::unordered_map<BankTypes, std::pair<std::vector<char>,
+                                                              std::vector<unsigned int>>>;
+}
+
+namespace MDF {
+void dump_hex(const char* start, int size);
+
+std::tuple<bool, bool, gsl::span<char>> read_event(std::ifstream& input, LHCb::MDFHeader& h,
+                                                   std::vector<char>& buffer, bool dbg = false);
+
+std::tuple<bool, bool, gsl::span<char>> read_banks(std::ifstream& input, const LHCb::MDFHeader& h,
+                                                   std::vector<char>& buffer, bool dbg = false);
+
+std::tuple<size_t, LHCbToGPU::buffer_map, std::vector<LHCb::ODIN>>
+read_events(size_t n, const std::vector<std::string>& files,
+            const std::unordered_set<BankTypes>& types,
+            size_t offset = 0);
+
+LHCb::ODIN decode_odin(const LHCb::RawBank* bank);
+
+}
+#endif
diff --git a/mdf/src/compression.cpp b/mdf/src/compression.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..274b5fce041ffcac3dbbb091a8d4f62d2f7205fc
--- /dev/null
+++ b/mdf/src/compression.cpp
@@ -0,0 +1,269 @@
+#include <iostream>
+#include <cstdio>
+#include <assert.h>
+
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <zlib.h>
+
+#ifdef HAVE_LZMA
+#include <lzma.h>
+#endif
+
+#ifdef HAVE_LZ4
+#include <xxhash.h>
+#include <lz4.h>
+#include <lz4hc.h>
+#endif
+
+#include "compression.hpp"
+
+// The size of the ROOT block framing headers for compression:
+// - 3 bytes to identify the compression algorithm and version.
+// - 3 bytes to identify the deflated buffer size.
+// - 3 bytes to identify the inflated buffer size.
+#define HDRSIZE 9
+
+/**
+ * Below are the routines for unzipping (inflating) buffers.
+ */
+
+namespace {
+static int is_valid_header_zlib(unsigned char *src)
+{
+   return src[0] == 'Z' && src[1] == 'L' && src[2] == Z_DEFLATED;
+}
+
+static int is_valid_header_old(unsigned char *src)
+{
+   return src[0] == 'C' && src[1] == 'S' && src[2] == Z_DEFLATED;
+}
+
+static int is_valid_header_lzma(unsigned char *src)
+{
+   return src[0] == 'X' && src[1] == 'Z' && src[2] == 0;
+}
+
+static int is_valid_header_lz4(unsigned char *src)
+{
+   return src[0] == 'L' && src[1] == '4';
+}
+
+static int is_valid_header(unsigned char *src)
+{
+   return is_valid_header_zlib(src) || is_valid_header_old(src) || is_valid_header_lzma(src) ||
+          is_valid_header_lz4(src);
+}
+
+static const int lzmaHeaderSize = 9;
+}
+
+int Compression::unzip_header(int *srcsize, unsigned char* src, int *tgtsize)
+{
+  // Reads header envelope, and determines target size.
+  // Returns 0 in case of success.
+
+  *srcsize = 0;
+  *tgtsize = 0;
+
+  /*   C H E C K   H E A D E R   */
+  if (!is_valid_header(src)) {
+     fprintf(stderr, "Error unzip_header: error in header.  Values: %x%x\n", src[0], src[1]);
+     return 1;
+  }
+
+  *srcsize = HDRSIZE + ((long)src[3] | ((long)src[4] << 8) | ((long)src[5] << 16));
+  *tgtsize = (long)src[6] | ((long)src[7] << 8) | ((long)src[8] << 16);
+
+  return 0;
+}
+
+
+void Compression::unzip(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
+{
+  long isize;
+  unsigned char  *ibufptr,*obufptr;
+  long  ibufcnt, obufcnt;
+
+  *irep = 0L;
+
+  /*   C H E C K   H E A D E R   */
+
+  if (*srcsize < HDRSIZE) {
+    fprintf(stderr,"unzip: too small source\n");
+    return;
+  }
+
+  /*   C H E C K   H E A D E R   */
+  if (!is_valid_header(src)) {
+     fprintf(stderr, "Error unzip: error in header\n");
+     return;
+  }
+
+  ibufptr = src + HDRSIZE;
+  ibufcnt = (long)src[3] | ((long)src[4] << 8) | ((long)src[5] << 16);
+  isize   = (long)src[6] | ((long)src[7] << 8) | ((long)src[8] << 16);
+  obufptr = tgt;
+  obufcnt = *tgtsize;
+
+  if (obufcnt < isize) {
+    fprintf(stderr,"R__unzip: too small target\n");
+    return;
+  }
+
+  if (ibufcnt + HDRSIZE != *srcsize) {
+     fprintf(stderr,"R__unzip: discrepancy in source length %li %d\n", ibufcnt + HDRSIZE, *srcsize);
+    return;
+  }
+
+  /* DECOMPRESS DATA */
+
+  /* ZLIB and other standard compression algorithms */
+  if (is_valid_header_zlib(src)) {
+     unzipZLIB(srcsize, src, tgtsize, tgt, irep);
+     return;
+#ifdef HAVE_LZMA
+  } else if (is_valid_header_lzma(src)) {
+     unzipLZMA(srcsize, src, tgtsize, tgt, irep);
+     return;
+#endif
+#ifdef HAVE_LZ4
+  } else if (is_valid_header_lz4(src)) {
+     unzipLZ4(srcsize, src, tgtsize, tgt, irep);
+     return;
+#endif
+  } else {
+     std::cerr << "Unknown compression algorith." << std::endl;
+  }
+}
+
+void Compression::unzipZLIB(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
+{
+     z_stream stream; /* decompression stream */
+     int err = 0;
+
+     stream.next_in = (Bytef *)(&src[HDRSIZE]);
+     stream.avail_in = (uInt)(*srcsize) - HDRSIZE;
+     stream.next_out = (Bytef *)tgt;
+     stream.avail_out = (uInt)(*tgtsize);
+     stream.zalloc = (alloc_func)0;
+     stream.zfree = (free_func)0;
+     stream.opaque = (voidpf)0;
+
+     err = inflateInit(&stream);
+     if (err != Z_OK) {
+        fprintf(stderr, "R__unzip: error %d in inflateInit (zlib)\n", err);
+        return;
+     }
+
+     while ((err = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
+        if (err != Z_OK) {
+           inflateEnd(&stream);
+           fprintf(stderr, "R__unzip: error %d in inflate (zlib)\n", err);
+           return;
+        }
+     }
+
+     inflateEnd(&stream);
+
+     *irep = stream.total_out;
+     return;
+}
+
+#ifdef HAVE_LZMA
+void Compression::unzipLZMA(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
+{
+   lzma_stream stream = LZMA_STREAM_INIT;
+   lzma_ret returnStatus;
+
+   *irep = 0;
+
+   returnStatus = lzma_stream_decoder(&stream,
+                                      UINT64_MAX,
+                                      0U);
+   if (returnStatus != LZMA_OK) {
+      fprintf(stderr,
+              "R__unzipLZMA: error %d in lzma_stream_decoder\n",
+              returnStatus);
+      return;
+   }
+
+   stream.next_in   = (const uint8_t *)(&src[lzmaHeaderSize]);
+   stream.avail_in  = (size_t)(*srcsize);
+   stream.next_out  = (uint8_t *)tgt;
+   stream.avail_out = (size_t)(*tgtsize);
+
+   returnStatus = lzma_code(&stream, LZMA_FINISH);
+   if (returnStatus != LZMA_STREAM_END) {
+      fprintf(stderr,
+              "unzipLZMA: error %d in lzma_code\n",
+              returnStatus);
+      lzma_end(&stream);
+      return;
+   }
+   lzma_end(&stream);
+
+   *irep = (int)stream.total_out;
+}
+#endif
+
+
+#ifdef HAVE_LZ4
+// Header consists of:
+// - 2 byte identifier "L4"
+// - 1 byte LZ4 version string.
+// - 3 bytes of uncompressed size
+// - 3 bytes of compressed size
+// - 8 byte checksum using xxhash 64.
+static const int kChecksumOffset = 2 + 1 + 3 + 3;
+static const int kChecksumSize = sizeof(XXH64_canonical_t);
+static const int lz4HeaderSize = kChecksumOffset + kChecksumSize;
+
+void Compression::unzipLZ4(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
+{
+   // NOTE: We don't check that srcsize / tgtsize is reasonable or within the ROOT-imposed limits.
+   // This is assumed to be handled by the upper layers.
+
+   int LZ4_version = LZ4_versionNumber() / (100 * 100);
+   *irep = 0;
+   if (src[0] != 'L' || src[1] != '4') {
+      fprintf(stderr, "R__unzipLZ4: algorithm run against buffer with incorrect header (got %d%d; expected %d%d).\n",
+              src[0], src[1], 'L', '4');
+      return;
+   }
+   if (src[2] != LZ4_version) {
+      fprintf(stderr,
+              "R__unzipLZ4: This version of LZ4 is incompatible with the on-disk version (got %d; expected %d).\n",
+              src[2], LZ4_version);
+      return;
+   }
+
+   int inputBufferSize = *srcsize - lz4HeaderSize;
+
+   // TODO: The checksum followed by the decompression means we iterate through the buffer twice.
+   // We should perform some performance tests to see whether we can interleave the two -- i.e., at
+   // what size of chunks does interleaving (avoiding two fetches from RAM) improve enough for the
+   // extra function call costs?  NOTE that ROOT limits the buffer size to 16MB.
+   XXH64_hash_t checksumResult = XXH64(src + lz4HeaderSize, inputBufferSize, 0);
+   XXH64_hash_t checksumFromFile =
+      XXH64_hashFromCanonical(reinterpret_cast<XXH64_canonical_t *>(src + kChecksumOffset));
+
+   if (checksumFromFile != checksumResult) {
+      fprintf(
+         stderr,
+         "R__unzipLZ4: Buffer corruption error!  Calculated checksum %llu; checksum calculated in the file was %llu.\n",
+         checksumResult, checksumFromFile);
+      return;
+   }
+   int returnStatus = LZ4_decompress_safe((char *)(&src[lz4HeaderSize]), (char *)(tgt), inputBufferSize, *tgtsize);
+   if (returnStatus < 0) {
+      fprintf(stderr, "R__unzipLZ4: error in decompression around byte %d out of maximum %d.\n", -returnStatus,
+              *tgtsize);
+      return;
+   }
+
+   *irep = returnStatus;
+}
+#endif
diff --git a/mdf/src/raw_helpers.cpp b/mdf/src/raw_helpers.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9cc670f39db0359038f1bc1895a12c6a3fe64014
--- /dev/null
+++ b/mdf/src/raw_helpers.cpp
@@ -0,0 +1,249 @@
+#include <cstring>
+#include <arpa/inet.h>
+
+#include "compression.hpp"
+#include "raw_helpers.hpp"
+
+/// one-at-time hash function
+unsigned int LHCb::hash32Checksum(const void* ptr, size_t len) {
+  unsigned int hash = 0;
+  const char* k = (const char*)ptr;
+  for (size_t i=0; i<len; ++i, ++k) {
+    hash += *k;
+    hash += (hash << 10);
+    hash ^= (hash >> 6);
+  }
+  hash += (hash << 3);
+  hash ^= (hash >> 11);
+  hash += (hash << 15);
+  return hash;
+}
+
+/* ========================================================================= */
+unsigned int LHCb::adler32Checksum(unsigned int adler,
+				   const char* buf,
+				   size_t len)
+{
+#define DO1(buf,i)  {s1 +=(unsigned char)buf[i]; s2 += s1;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+  static const unsigned int BASE = 65521;    /* largest prime smaller than 65536 */
+  /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+  static const unsigned int NMAX = 5550;
+  unsigned int s1 = adler & 0xffff;
+  unsigned int s2 = (adler >> 16) & 0xffff;
+
+  if (buf == NULL) return 1;
+
+  while (len > 0) {
+    int k = len < NMAX ? (int)len : NMAX;
+    len -= k;
+    while (k >= 16) {
+      DO16(buf);
+      buf += 16;
+      k -= 16;
+    }
+    if (k != 0) do {
+      s1 += (unsigned char)*buf++;
+      s2 += s1;
+    } while (--k);
+    s1 %= BASE;
+    s2 %= BASE;
+  }
+  unsigned int result = (s2 << 16) | s1;
+  return result;
+}
+/* ========================================================================= */
+
+static unsigned int xorChecksum(const int* ptr, size_t len)  {
+  unsigned int checksum = 0;
+  len = len/sizeof(int) + ((len%sizeof(int)) ? 1 : 0);
+  for(const int *p=ptr, *end=p+len; p<end; ++p)  {
+    checksum ^= *p;
+  }
+  return checksum;
+}
+
+#define QUOTIENT  0x04c11db7
+class CRC32Table  {
+public:
+  unsigned int m_data[256];
+  CRC32Table()  {
+    for (int i = 0; i < 256; i++)    {
+      unsigned int crc = i << 24;
+      for (int j = 0; j < 8; j++)   {
+        if (crc & 0x80000000)
+          crc = (crc << 1) ^ QUOTIENT;
+        else
+          crc = crc << 1;
+      }
+      m_data[i] = htonl(crc);
+    }
+  }
+  const unsigned int* data() const { return m_data; }
+};
+
+// Only works for word aligned data and assumes that the data is an exact number of words
+// Copyright  1993 Richard Black. All rights are reserved.
+static unsigned int crc32Checksum(const char *data, size_t len)    {
+  static CRC32Table table;
+  const unsigned int *crctab = table.data();
+  const unsigned int *p = (const unsigned int *)data;
+  const unsigned int *e = (const unsigned int *)(data + len);
+  if ( len < 4 || (size_t(data)%sizeof(unsigned int)) != 0 ) return ~0x0;
+  unsigned int result = ~*p++;
+  while( p < e )  {
+#if defined(LITTLE_ENDIAN)
+    result = crctab[result & 0xff] ^ result >> 8;
+    result = crctab[result & 0xff] ^ result >> 8;
+    result = crctab[result & 0xff] ^ result >> 8;
+    result = crctab[result & 0xff] ^ result >> 8;
+    result ^= *p++;
+#else
+    result = crctab[result >> 24] ^ result << 8;
+    result = crctab[result >> 24] ^ result << 8;
+    result = crctab[result >> 24] ^ result << 8;
+    result = crctab[result >> 24] ^ result << 8;
+    result ^= *p++;
+#endif
+  }
+
+  return ~result;
+}
+
+static unsigned short crc16Checksum (const char *data, size_t len) {
+  static const unsigned short wCRCTable[] =
+  { 0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
+    0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
+    0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
+    0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
+    0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
+    0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
+    0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
+    0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
+    0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
+    0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
+    0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
+    0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
+    0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
+    0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
+    0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
+    0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
+    0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
+    0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
+    0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
+    0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
+    0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
+    0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
+    0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
+    0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
+    0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
+    0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
+    0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
+    0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
+    0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
+    0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
+    0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
+    0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
+
+    unsigned short wCRCWord = 0xFFFF;
+    while (len--)  {
+      unsigned int nTemp = *data++ ^ wCRCWord;
+      wCRCWord >>= 8;
+      wCRCWord = (unsigned short)(wCRCWord^wCRCTable[nTemp]);
+    }
+    return wCRCWord;
+}
+
+static char crc8Checksum(const char *data, int len) {
+  static unsigned char crc8_table[] =
+  { 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
+    157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
+    35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
+    190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
+    70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
+    219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
+    101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
+    248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
+    140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
+    17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
+    175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
+    50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
+    202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
+    87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
+    233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
+    116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
+  };
+  const char *s = data;
+  char c = 0;
+  while (len--) c = crc8_table[c ^ *s++];
+  return c;
+}
+
+/// Generate XOR Checksum
+unsigned int LHCb::genChecksum(int flag,const void* ptr, size_t len)  {
+  switch(flag)  {
+    case 0:
+      return xorChecksum((const int*)ptr, len);
+    case 1:
+      return hash32Checksum(ptr, len);
+    case 2:
+      len = (len/sizeof(int))*sizeof(int);
+      return crc32Checksum((const char*)ptr, len);
+    case 3:
+      len = (len/sizeof(short))*sizeof(short);
+      return crc16Checksum((const char*)ptr, len);
+    case 4:
+      return crc8Checksum((const char*)ptr, len);
+    case 5:
+      len = (len/sizeof(int))*sizeof(int);
+      return adler32Checksum(1, (const char*)ptr, len);
+    case 22:  // Old CRC32 (fixed by now)
+      return crc32Checksum((const char*)ptr, len);
+    default:
+      return ~0x0;
+  }
+}
+
+/// Decompress opaque data buffer
+bool LHCb::decompressBuffer(int algtype,
+                            unsigned char*    tar,
+                            size_t        tar_len,
+                            unsigned char*   src,
+                            size_t        src_len,
+                            size_t&       new_len)
+{
+  int in_len, out_len, res_len = 0;
+  switch(algtype)  {
+    case 0:
+      if ( tar != src && tar_len >= src_len )  {
+        new_len = src_len;
+        ::memcpy(tar, src, src_len);
+        return true;
+      }
+      break;
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+      in_len = src_len;
+      out_len = tar_len;
+      Compression::unzip(&in_len, src, &out_len, tar, &res_len);
+      if ( res_len > 0 )  {
+        new_len = res_len;
+        return true;
+      }
+      break;
+    default:
+      break;
+  }
+  return false;
+}
diff --git a/mdf/src/read_mdf.cpp b/mdf/src/read_mdf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ed59762ef0be5edfeeaa863a7dae06b435d7c4ac
--- /dev/null
+++ b/mdf/src/read_mdf.cpp
@@ -0,0 +1,382 @@
+#include <cstdio>
+#include <cstring>
+#include <unistd.h>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <vector>
+#include <array>
+
+#include "mdf_header.hpp"
+#include "read_mdf.hpp"
+#include "raw_bank.hpp"
+#include "raw_helpers.hpp"
+
+namespace {
+   using std::ifstream;
+   using std::cout;
+   using std::cerr;
+   using std::endl;
+   using std::vector;
+   using std::array;
+   using std::make_tuple;
+   using gsl::span;
+
+   const bool ignoreChecksum = false;
+}
+
+std::tuple<size_t, LHCbToGPU::buffer_map, std::vector<LHCb::ODIN>>
+MDF::read_events(size_t n, const std::vector<std::string>& files,
+                 const std::unordered_set<BankTypes>& types,
+                 size_t start_event)
+{
+
+   LHCbToGPU::buffer_map buffers;
+   for (auto bank_type : types) {
+      auto r = buffers.emplace(bank_type, make_pair(vector<char>{}, vector<unsigned int>{}));
+      // Reserve some memory
+      auto& banks = r.first->second.first;
+      banks.reserve(n * 100 * 1024);
+      // Reserve for 100 banks per event, more than enough
+      auto& offsets = r.first->second.second;
+      offsets.reserve(n * 100);
+      offsets.push_back(0);
+   }
+
+   vector<LHCb::ODIN> odins;
+   odins.reserve(n);
+
+   // Some storage for reading the events into
+   LHCb::MDFHeader header;
+   vector<char> buffer;
+   buffer.reserve(1024 * 1024);
+   bool eof = false, error = false;
+
+   gsl::span<const char> bank_span;
+   size_t n_read = 0;
+   unsigned int event_size = 0;
+
+   array<std::vector<uint32_t>, NBankTypes> bank_offsets;
+   array<std::vector<uint32_t>, NBankTypes> bank_datas;
+
+   // Lambda to copy data from the event-local buffer to the global
+   // one, while keeping the global buffer's size consistent with its
+   // content
+   auto copy_data = [] (unsigned int& event_offset, vector<char>& buf,
+                        const void* source, size_t s) {
+                       size_t n_chars = buf.size();
+                       for (size_t i = 0; i < s; ++i) {
+                          buf.emplace_back(0);
+                       }
+                       ::memcpy(&buf[n_chars], source, s);
+                       event_offset += s;
+                    };
+
+   for (const auto& filename : files) {
+      ifstream input{filename.c_str(), std::ios::binary};
+      if (!input.good()) {
+         cout << "failed to open file " << filename << endl;
+         break;
+      }
+
+      while (n_read++ < n) {
+
+         std::tie(eof, error, bank_span) = read_event(input, header, buffer);
+         if (eof || error) {
+            break;
+         }
+
+         // Skip some events
+         if (n_read < start_event) {
+            continue;
+         }
+
+
+         // Clear bank offsets
+         for (auto& bo : bank_offsets) {
+            bo.clear();
+            bo.push_back(0);
+         }
+
+         // Clear bank data
+         for (auto& bd : bank_datas) {
+            bd.clear();
+         }
+
+         // Put the banks in the event-local buffers
+         const auto* bank = bank_span.begin();
+         const auto* end = bank_span.end();
+         while (bank < end) {
+            const auto* b = reinterpret_cast<const LHCb::RawBank*>(bank);
+            if (b->magic() != LHCb::RawBank::MagicPattern) {
+               cout << "magic pattern failed: " << std::hex << b->magic() << std::dec << endl;
+            }
+
+            // Decode the odin bank
+            if (b->type() == LHCb::RawBank::ODIN) {
+               odins.emplace_back(decode_odin(b));
+            }
+
+            // Check if cuda_hlt even knows about this type of bank
+            auto cuda_type_it = LHCbToGPU::bank_types.find(b->type());
+            if (cuda_type_it == LHCbToGPU::bank_types.end()) {
+               bank += b->totalSize();
+               continue;
+            }
+
+            // Check if we want this bank
+            auto buf_it = buffers.find(cuda_type_it->second);
+            if (buf_it == buffers.end()) {
+               bank += b->totalSize();
+               continue;
+            }
+
+            auto index = to_integral(cuda_type_it->second);
+            auto& bank_data = bank_datas[index];
+            auto& offsets = bank_offsets[index];
+            auto offset = offsets.back()  / sizeof(uint32_t);
+
+            // Store this bank in the event-local buffers
+            bank_data.push_back(b->sourceID());
+            offset++;
+
+            auto b_start = b->begin<uint32_t>();
+            auto b_end = b->end<uint32_t>();
+
+            while (b_start != b_end) {
+               const uint32_t raw_data = *b_start;
+               bank_data.emplace_back(raw_data);
+
+               b_start++;
+               offset++;
+            }
+
+            // Record raw bank offset
+            offsets.push_back(offset * sizeof(uint32_t));
+
+            // Move to next raw bank
+            bank += b->totalSize();
+         }
+
+         // Fill the output buffers from the event-local buffers in the right format:
+         // - number of raw banks (uint32_t)
+         // - raw bank offset within event as number of char (one more than number of banks)
+         // - raw bank data as uint32_t
+         for (auto& entry : buffers) {
+            auto& buf = entry.second.first;
+
+            auto index = to_integral(entry.first);
+            auto& event_offsets = entry.second.second;
+            auto event_offset = event_offsets.back();
+
+            // Copy in number of banks
+            const auto& offsets = bank_offsets[index];
+            uint32_t n_banks = offsets.size() - 1;
+            copy_data(event_offset, buf, &n_banks, sizeof(n_banks));
+
+            // Copy in bank offsets
+            copy_data(event_offset, buf, offsets.data(), offsets.size() * sizeof(uint32_t));
+
+            // Copy in bank data
+            const auto& bank_data = bank_datas[index];
+            copy_data(event_offset, buf, bank_data.data(), bank_data.size() * sizeof(uint32_t));
+
+            event_offsets.push_back(event_offset);
+         }
+      }
+   }
+   n_read = n_read > 0 ? n_read - 1 : 0;
+   return make_tuple(n_read, std::move(buffers), std::move(odins));
+}
+
+// return eof, error, span that covers all banks in the event
+std::tuple<bool, bool, gsl::span<char>> MDF::read_event(ifstream& input, LHCb::MDFHeader& h,
+                                                        vector<char>& buffer, bool dbg)
+{
+   int rawSize = sizeof(LHCb::MDFHeader);
+   // Read directly into the header
+   input.read(reinterpret_cast<char*>(&h), rawSize);
+   if (input.good()) {
+      return read_banks(input, h, buffer, dbg);
+   } else if (input.eof()) {
+      cout << "Cannot read more data  (Header). End-of-File reached." << endl;
+      return {true, false, {}};
+   } else {
+      cerr << "Failed to read header" << endl;
+      return {false, true, {}};
+   }
+}
+
+// return eof, error, span that covers all banks in the event
+std::tuple<bool, bool, gsl::span<char>> MDF::read_banks(ifstream& input, const LHCb::MDFHeader& h,
+                                                        vector<char>& buffer, bool dbg)
+{
+   int rawSize   = sizeof(LHCb::MDFHeader);
+   unsigned int checksum = h.checkSum();
+   int compress = h.compression() & 0xF;
+   int expand = (h.compression() >> 4) + 1;
+   int hdrSize = h.subheaderLength();
+   int readSize = h.recordSize() - rawSize;
+   int chkSize = h.recordSize() - 4 * sizeof(int);
+   int alloc_len = (rawSize + readSize + sizeof(LHCb::MDFHeader) + sizeof(LHCb::RawBank) +
+                    sizeof(int) + (compress ? expand*readSize : 0));
+
+   // Build the DAQ status bank that contains the header
+   auto build_bank
+      = [rawSize, &h] (char* address) {
+           auto* b = reinterpret_cast<LHCb::RawBank*>(address);
+           b->setMagic();
+           b->setType(LHCb::RawBank::DAQ);
+           // Reverse engineered, somehow...
+           b->setSize(rawSize + 1);
+           b->setVersion(DAQ_STATUS_BANK);
+           b->setSourceID(0);
+           ::memcpy(b->data(), &h, rawSize);
+           return b;
+        };
+
+   if (dbg) {
+      cout << "Size: " << std::setw(6) << h.recordSize()
+           << " Compression:" << compress << " Checksum: 0x"
+           << std::hex << checksum << std::dec << endl;
+   }
+
+   // accomodate for potential padding of MDF header bank!
+   buffer.reserve(alloc_len + sizeof(int) + sizeof(LHCb::RawBank));
+
+   auto* b = build_bank(&buffer[0]);
+   int bnkSize = b->totalSize();
+   char* bptr = (char*)b->data();
+   auto* hdr = reinterpret_cast<LHCb::MDFHeader*>(bptr);
+   if (compress != 0)  {
+      std::vector<char> tmp;
+      tmp.reserve(readSize + rawSize);
+      ::memcpy(tmp.data(), &h, rawSize); // Need to copy header to get checksum right
+
+      input.read(tmp.data() + rawSize, readSize);
+      if (input.eof()) {
+         cout << "Cannot read more data  (Header). End-of-File reached." << endl;
+         return {true, false, {}};
+      } else if (input.fail()) {
+         cerr << "Failed to read banks" << endl;
+         return {false, true, {}};
+      }
+
+      int space_retry = 0;
+      while (space_retry++ < 5) {
+         if (space_retry > 1) {
+            alloc_len *= 2;
+            if (dbg) {
+               cout << "Retry with increased buffer space of " << alloc_len << " bytes." << endl;
+            }
+            buffer.reserve(alloc_len + sizeof(int) + sizeof(LHCb::RawBank));
+            // Rebuild the DAQ status bank on reallocate
+            b = build_bank(&buffer[0]);
+            bnkSize = b->totalSize();
+            bptr = reinterpret_cast<char*>(b->data());
+            hdr = reinterpret_cast<LHCb::MDFHeader*>(bptr);
+         }
+
+         // Checksum if requested
+         if (ignoreChecksum) {
+            hdr->setChecksum(0);
+         } else if (checksum) {
+            auto c = LHCb::genChecksum(1, &tmp[0] + 4 * sizeof(int), chkSize);
+            if (checksum != c) {
+               cerr << "Checksum doesn't match: "
+                    << std::hex << c << " instead of 0x"
+                     << checksum << std::dec << endl;
+               return {false, true, {}};
+            }
+         }
+
+         // Checksum is correct...from all we know data integrity is proven
+         size_t new_len = 0;
+
+         // NOTE: Figured out the magic +1 from dumping stuff until it
+         // made sense...
+         auto* src = reinterpret_cast<unsigned char*>(&tmp[0]) + rawSize + 1;
+         auto* ptr = reinterpret_cast<unsigned char*>(&buffer[0]) + bnkSize;
+         size_t space_size = buffer.capacity() - bnkSize;
+         // NOTE: Figured out the magic h.size() + hdrSize - 1 from
+         // dumping stuff until it made sense...
+         if (LHCb::decompressBuffer(compress, ptr, space_size, src, h.size() + hdrSize - 1, new_len)) {
+            hdr->setSize(new_len);
+            hdr->setCompression(0);
+            hdr->setChecksum(0);
+            return {false, false, {buffer.data(), bnkSize + new_len}};
+         }
+      }
+      cerr << "Failed to read compressed data." << endl;
+      return {false, true, {}};
+   } else {
+      // Read uncompressed data file...
+      input.read(bptr + rawSize, readSize);
+      if (input.eof()) {
+         cout << "Cannot read more data  (Header). End-of-File reached." << endl;
+         return {true, false, {}};
+      } else if (input.fail()) {
+         cerr << "Failed to read banks" << endl;
+         return {false, true, {}};
+      }
+
+      if (ignoreChecksum) {
+         hdr->setChecksum(0);
+      } else if (checksum) {
+         auto c = LHCb::genChecksum(1, bptr + 4 * sizeof(int), chkSize);
+         if (checksum != c) {
+            cerr << "Checksum doesn't match: 0x" << std::hex << c
+                 << " instead of 0x" << checksum << std::dec << endl;
+            return {false, true, {}};
+         }
+      }
+      return {false, false, {buffer.data(), bnkSize + h.size() - 1}};
+   }
+}
+
+// Decode the ODIN bank
+LHCb::ODIN MDF::decode_odin(const LHCb::RawBank* bank)
+{
+   LHCb::ODIN odin;
+   unsigned long long temp64{0};
+   unsigned int       temp32{0};
+   const unsigned int* odinData = bank->data();
+
+   // Fill the ODIN object
+   odin.version = bank->version();
+   odin.run_number = odinData[LHCb::ODIN::Data::RunNumber];
+   odin.orbit_number = odinData[LHCb::ODIN::Data::OrbitNumber];
+
+   temp64 = odinData[LHCb::ODIN::Data::L0EventIDHi];
+   odin.event_number = (temp64 << 32) + odinData[LHCb::ODIN::Data::L0EventIDLo];
+
+   temp32 = odinData[LHCb::ODIN::Data::EventType];
+   odin.event_type = (temp32 & LHCb::ODIN::EventTypeMasks::EventTypeMask) >> LHCb::ODIN::EventTypeBitsEnum::EventTypeBits;
+   odin.calibration_step = (temp32 & LHCb::ODIN::EventTypeMasks::CalibrationStepMask) >> LHCb::ODIN::EventTypeBitsEnum::CalibrationStepBits;
+
+   odin.tck = odinData[LHCb::ODIN::Data::TriggerConfigurationKey];
+   return odin;
+}
+
+void MDF::dump_hex(const char* start, int size)
+{
+   const auto* content = start;
+   size_t m = 0;
+   cout << std::hex << std::setw(7) << m << " ";
+   auto prev = cout.fill();
+   auto flags = cout.flags();
+   while (content < start + size) {
+      if (m % 32 == 0 && m != 0) {
+         cout << endl;
+         cout << std::setw(7) << m << " ";
+      }
+      cout << std::setw(2) << std::setfill('0') << ((int)(*content) & 0xff);
+      ++m;
+      if (m % 2 == 0) {
+         cout << " ";
+      }
+      ++content;
+   }
+   cout << std::dec << std::setfill(prev) << endl;
+   cout.setf(flags);
+}
diff --git a/mdf/test/dump_banks.cpp b/mdf/test/dump_banks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3c88e1f3b5c15a832d051625aa6f32994a39efd8
--- /dev/null
+++ b/mdf/test/dump_banks.cpp
@@ -0,0 +1,111 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <iomanip>
+#include <unordered_set>
+#include <map>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <cerrno>
+#include <cstring>
+
+#include "raw_bank.hpp"
+#include "read_mdf.hpp"
+#include "Tools.h"
+
+namespace {
+   using std::ifstream;
+   using std::vector;
+   using std::unordered_set;
+   using std::string;
+   using std::map;
+   using std::to_string;
+   using std::cout;
+   using std::cerr;
+   using std::endl;
+   using std::make_pair;
+   using std::ios;
+}
+
+
+int main(int argc, char* argv[]) {
+   if (argc <=2) {
+      cout << "usage: dump_banks <file.mdf> <output_dir>" << endl;
+      return -1;
+   }
+
+   vector<string> files = {{argv[1]}};
+   string output_dir = {argv[2]};
+
+   // Make output directory if needed
+   vector<string> directories;
+   auto pos = output_dir.find("/");
+   while(pos != string::npos) {
+      auto first = string{output_dir}.substr(0, pos);
+      directories.push_back(first);
+      output_dir = output_dir.substr(pos + 1, string::npos);
+      pos = output_dir.find("/");
+   }
+   if (!output_dir.empty()) {
+      directories.push_back(output_dir);
+   }
+
+   output_dir = "";
+   for (auto dir : directories) {
+      output_dir = output_dir.empty() ? dir : output_dir + "/" + dir;
+      auto* d = opendir(output_dir.c_str());
+      if (d) {
+         closedir(d);
+      } else {
+         const int status = mkdir(output_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+         if (status != 0 & errno != EEXIST) {
+            cerr << "Error creating directory " << output_dir << endl;
+            cerr << std::strerror(errno) << endl;
+            return status;
+         }
+      }
+   }
+
+   unordered_set<BankTypes> types = {BankTypes::VP, BankTypes::UT, BankTypes::FT, BankTypes::MUON};
+
+   size_t n_read = 0;
+   LHCbToGPU::buffer_map buffers;
+   vector<LHCb::ODIN> odins;
+   std::tie(n_read, buffers, odins) = MDF::read_events(10, files, types);
+   for (const auto& odin : odins) {
+      cout << odin.run_number << " " << odin.event_number << " " << odin.tck << endl;
+   }
+
+   for (auto type : types) {
+      auto dir = output_dir + "/" + bank_name(type);
+      const int status = mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+      if (status != 0 & errno != EEXIST) {
+         cerr << "Error creating directory " << output_dir << endl;
+         cerr << std::strerror(errno) << endl;
+         return status;
+      }
+   }
+
+   for (const auto& entry : buffers) {
+      if (entry.first == BankTypes::VP) {
+         cout << entry.second.first.size() << " " << entry.second.second.back() << endl;
+         check_velopix_events(entry.second.first, entry.second.second, n_read);
+      } else {
+         cout << entry.second.first.size() << " " << entry.second.second.back() << endl;
+      }
+
+      const auto& buf = entry.second.first;
+      const auto& offsets = entry.second.second;
+      for (size_t i = 0; i < offsets.size() - 1; ++i) {
+         const auto& odin = odins[i];
+
+         string filename = (output_dir + "/" + bank_name(entry.first) + "/" +
+                            to_string(odin.run_number) + "_" + to_string(odin.event_number) + ".bin");
+         std::ofstream outfile{filename.c_str(), ios::out | ios::binary};
+         outfile.write(buf.data() + offsets[i], offsets[i + 1] - offsets[i]);
+         outfile.close();
+      }
+   }
+
+   cout << "read " << n_read << " events" << endl;
+}
diff --git a/mdf/test/test_read.cpp b/mdf/test/test_read.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..556f2a4309b15a594364a6eec720569b94bc6c19
--- /dev/null
+++ b/mdf/test/test_read.cpp
@@ -0,0 +1,135 @@
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <iomanip>
+#include <unordered_set>
+#include <map>
+
+#include "raw_bank.hpp"
+#include "read_mdf.hpp"
+#include "Tools.h"
+
+namespace {
+   using std::ifstream;
+   using std::vector;
+   using std::unordered_set;
+   using std::string;
+   using std::map;
+   using std::to_string;
+   using std::cout;
+   using std::endl;
+   using std::make_pair;
+   using std::ios;
+}
+
+
+int main(int argc, char* argv[]) {
+   if (argc <=1) {
+      cout << "usage: test_read <file.mdf>" << endl;
+      return -1;
+   }
+
+   string filename = {argv[1]};
+
+   // Some storage for reading the events into
+   LHCb::MDFHeader header;
+   vector<char> read_buffer;
+   read_buffer.reserve(1024 * 1024);
+   bool eof = false, error = false;
+
+   gsl::span<const char> bank_span;
+
+   ifstream input{filename.c_str(), std::ios::binary};
+   if (!input.good()) {
+      cout << "failed to open file " << filename << endl;
+      return -1;
+   }
+
+   // Lambda to copy data from the event-local buffer to the global
+   // one, while keeping the global buffer's size consistent with its
+   // content
+   auto copy_data = [] (unsigned int& event_offset, vector<char>& buf,
+                        const void* source, size_t s) {
+                       size_t n_chars = buf.size();
+                       for (size_t i = 0; i < s; ++i) {
+                          buf.emplace_back(0);
+                       }
+                       ::memcpy(&buf[n_chars], source, s);
+                       event_offset += s;
+                    };
+
+
+   std::tie(eof, error, bank_span) = MDF::read_event(input, header, read_buffer);
+   if (eof || error) {
+      return -1;
+   }
+
+   vector<uint32_t> bank_data;
+   vector<uint32_t> bank_offsets;
+   bank_offsets.reserve(100);
+   bank_offsets.push_back(0);
+
+   // Put the banks in the event-local buffers
+   const auto* bank = bank_span.begin();
+   const auto* end = bank_span.end();
+   while (bank < end) {
+      const auto* b = reinterpret_cast<const LHCb::RawBank*>(bank);
+      if (b->magic() != LHCb::RawBank::MagicPattern) {
+         cout << "magic pattern failed: " << std::hex << b->magic() << std::dec << endl;
+      }
+
+      // Check if cuda_hlt even knows about this type of bank
+      auto cuda_type_it = LHCbToGPU::bank_types.find(b->type());
+      if (cuda_type_it == LHCbToGPU::bank_types.end()) {
+         bank += b->totalSize();
+         continue;
+      }
+
+      // Check if we want this bank
+      if (cuda_type_it->second != BankTypes::VP) {
+         bank += b->totalSize();
+         continue;
+      }
+
+      auto offset = bank_offsets.back() / sizeof(uint32_t);
+
+      // Store this bank in the event-local buffers
+      const uint32_t sourceID = static_cast<uint32_t>(b->sourceID());
+      bank_data.push_back(sourceID);
+      offset++;
+
+      auto b_start = b->begin<uint32_t>();
+      auto b_end = b->end<uint32_t>();
+
+      while (b_start != b_end) {
+         const uint32_t raw_data = *b_start;
+         bank_data.emplace_back(raw_data);
+
+         b_start++;
+         offset++;
+      }
+
+      // Record raw bank offset
+      bank_offsets.push_back(offset * sizeof(uint32_t));
+
+      // Move to next raw bank
+      bank += b->totalSize();
+   }
+
+   unsigned int event_offset = 0;
+   uint32_t n_banks = bank_offsets.size() - 1;
+   vector<char> buf;
+   cout << "n_banks: " << n_banks << endl;
+   copy_data(event_offset, buf, &n_banks, sizeof(n_banks));
+
+   // Copy in bank offsets
+   copy_data(event_offset, buf, bank_offsets.data(), bank_offsets.size() * sizeof(uint32_t));
+
+   // Copy in bank data
+   copy_data(event_offset, buf, bank_data.data(), bank_data.size() * sizeof(uint32_t));
+
+   std::vector<unsigned int> event_offsets = {0, event_offset};
+   cout << event_offset << endl;
+   check_velopix_events(buf, event_offsets, 1);
+}
diff --git a/mdf/test/test_read_bin.cpp b/mdf/test/test_read_bin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ea94e5880219d4df35b9f4412c5cdd261d69750
--- /dev/null
+++ b/mdf/test/test_read_bin.cpp
@@ -0,0 +1,41 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <iomanip>
+#include <unordered_set>
+#include <map>
+
+#include "Tools.h"
+#include "InputTools.h"
+
+namespace {
+   using std::ifstream;
+   using std::vector;
+   using std::unordered_set;
+   using std::string;
+   using std::map;
+   using std::to_string;
+   using std::cout;
+   using std::endl;
+   using std::make_pair;
+   using std::ios;
+}
+
+
+int main(int argc, char* argv[]) {
+   if (argc <=1) {
+      cout << "usage: test_read <file.mdf>" << endl;
+      return -1;
+   }
+
+   string filename = {argv[1]};
+   vector<char> data;
+   vector<unsigned int> offsets(1, 0);
+   appendFileToVector(filename, data, offsets);
+
+   check_velopix_events(data, offsets, 1);
+
+   for (size_t i = 0; i < offsets.size(); ++i) {
+      cout << offsets[i] << endl;
+   }
+}
diff --git a/readme.md b/readme.md
index fac789327340a35ffd47c9ec0e96b2a64f99a183..92ae9d79e2dbccbd2a5ff40818a5986b6bf4cc27 100644
--- a/readme.md
+++ b/readme.md
@@ -28,7 +28,9 @@ Cuda compilation tools, release 9.2, V9.2.88
 
 You can check your compiler standard compatibility by scrolling to the `C++14 features` chart [here](https://en.cppreference.com/w/cpp/compiler_support).
 
-Optional: you can compile the project with ROOT. Then, trees will be filled with variables to check when running the VeloUT algorithm on x86 architecture.
+Optional: you can compile the project with ROOT. Then, trees will be filled with variables to check when running the UT tracking or SciFi tracking algorithms on x86 architecture.
+In addition, histograms of reconstructible and reconstructed tracks are then filled in the track checker, they are saved in the file output/PrCheckerPlots.root. 
+Plots of efficiencies versus various kinematic variables can be created by running efficiency_plots.py in the directory checker/tracking/python_scripts. 
 
 [Building and running inside Docker](readme_docker.md)
 
@@ -36,9 +38,9 @@ Where to find input
 -------------
 Input from 1k events can be found here: 
 
-minimum bias (for performance checks): /afs/cern.ch/work/d/dovombru/public/gpu_input/1kevents_minbias.tar.gz
+minimum bias (for performance checks): /afs/cern.ch/work/d/dovombru/public/gpu_input/1kevents_minbias_dump_phi_nPV.tar.gz
 
-Bs->PhiPhi (for efficiency checks): /afs/cern.ch/work/d/dovombru/public/gpu_input/1kevents_BsPhiPhi.tar.gz
+Bs->PhiPhi (for efficiency checks): /afs/cern.ch/work/d/dovombru/public/gpu_input/1kevents_BsPhiPhi_dump_phi_nPV.tar.gz
 
 How to run it
 -------------
diff --git a/stream/CMakeLists.txt b/stream/CMakeLists.txt
index d6a28bd52949593db7cc50d2d72bdc83fefb6043..467bf9eea8adbd6340678c952122b594bf1b648e 100644
--- a/stream/CMakeLists.txt
+++ b/stream/CMakeLists.txt
@@ -56,23 +56,18 @@ cuda_add_library(Stream STATIC
   ${stream_sequence_visitors_SciFi}
   )
 
-if ( ROOT_FOUND )
-  target_compile_definitions(Stream PUBLIC WITH_ROOT)
-  message("-- Found ROOT, setting WITH_ROOT to true")
-  target_link_libraries(Stream
-  Utils
-  Velo
-  UT
-  x86Forward
-  SciFi
-  x86VeloUT
-  ${ROOT_LIBRARIES})
-else()
-  target_link_libraries(Stream
+target_link_libraries(Stream
+  Common
   Utils
   Velo
   UT
   x86Forward
   SciFi
   x86VeloUT)
+
+if ( ROOT_FOUND )
+  target_compile_definitions(Stream PUBLIC WITH_ROOT)
+  message("-- Found ROOT, setting WITH_ROOT to true")
+  target_link_libraries(Stream
+    ${ROOT_LIBRARIES})
 endif()
diff --git a/stream/sequence/src/Stream.cu b/stream/sequence/src/Stream.cu
index 184cf0221472dddda6e5cee77c3da09992380970..d306c2eb0e04b11d0f64edf611f368aa92492385 100644
--- a/stream/sequence/src/Stream.cu
+++ b/stream/sequence/src/Stream.cu
@@ -97,6 +97,11 @@ cudaError_t Stream::run_sequence(const RuntimeOptions& runtime_options) {
 void Stream::run_monte_carlo_test(const uint number_of_events_requested) {
   std::cout << "Checking Velo tracks reconstructed on GPU" << std::endl;
 
+#ifdef WITH_ROOT
+  TFile *f = new TFile("../output/PrCheckerPlots.root", "RECREATE");
+  f->Close();
+#endif
+  
   const std::vector<trackChecker::Tracks> tracks_events = prepareTracks(
     host_buffers.host_velo_tracks_atomics,
     host_buffers.host_velo_track_hit_number,
diff --git a/x86/SciFi/CMakeLists.txt b/x86/SciFi/CMakeLists.txt
index 97e53c6f44f2f84bb9762896cea5ae0fd8b09bb3..9b1c3605ce1fbf80a39b8e8fb2f0447fe6c892f1 100644
--- a/x86/SciFi/CMakeLists.txt
+++ b/x86/SciFi/CMakeLists.txt
@@ -28,4 +28,5 @@ if ( ROOT_FOUND )
   target_link_libraries(x86Forward
     ${ROOT_LIBRARIES}
   )
+  target_include_directories(x86Forward PUBLIC ${ROOT_INCLUDE_DIRS})
 endif()