#include "TMVA/Timer.h"
#include "QFramework/TQGridScanner.h"
#include "QFramework/TQGridScanPoint.h"
#include "QFramework/TQGridScanObservable.h"
#include "QFramework/TQGridScanStyle.h"
#include "TLine.h"
#include <algorithm>
#include <iostream>
#include "TCanvas.h"
#include "QFramework/TQSignificanceEvaluator.h"
#include "QFramework/TQHistogramUtils.h"
#include "QFramework/TQTHnBaseUtils.h"
#include "QFramework/TQStringUtils.h"
#include "QFramework/TQFolder.h"
#include "QFramework/TQUtils.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQPlotter.h"
#include "QFramework/TQPathManager.h"
#include "TLatex.h"
#include "TList.h"
#include "TPaletteAxis.h"
#include "TLegend.h"
#include "TSystem.h"
#include "TStyle.h"
#include "TROOT.h"
#include "THnBase.h"
#include "QFramework/TQLibrary.h"
#include <math.h>
#include <limits>

#define _DEBUG_

////////////////////////////////////////////////////////////////////////////////////////////////
//
// TQGridScanner
//
// The TQGridScanner is the last element in the chain of classes
// that can be used for a full scan cut optimization.
//
// the workflow here could be as follows
// - create a multidimensional histogram during the analyze with the desired variables
//   for optimization
// - run the runGridScanner.py script for creating a TQGridScanner
//   and supplying it with a TQSignificanceEvaluator
//   that fits your needs regarding precision/runtime requirements
// - sort the results using the TQGridScanner::sortPoints method
// - retrieve the first (best) entry with TQGridScanner::printPoint(0)
//
// Plotting options:
// - ext=pdf,ps,...
// file extension. Works for all "plotAndSave..."-functions
// accepts all extension that are accepted by TCanvas->SaveAs(...)
// - showmax=true,false
// show the maximum. works for plotAndSave[All]SignficanceProfile[s]
// will additionally show the absolute maximum for each bin
// this option is ignored if topNumber is 1
// - cut.$VARNAME=X. Works if set on the GridScanner itself.
// Will draw a vertical line at X. Can be used to mark the baseline cut value.
//
////////////////////////////////////////////////////////////////////////////////////////////////

#define PRECISION 0.000000001 // for double/float comparison

using Bound = TQGridScanBound;
using Bounds = TQGridScanBounds;
//using FixedBoundType = TQGridScanObservable::FixedBoundType;
using BoundsType = TQGridScanBound::Type;
using BoundRange = TQGridScanBound::Range;
//using RangeBoundBin = TQGridScanObservable::RangeBound;
using BinExtrema = TQGridScanBound::BinExtrema;
using ObsVec = TQGridScanner::ObsVec;
using CutBounds = TQGridScanner::CutBounds;
using SplitCutVals = TQGridScanner::SplitCutVals;
using SplitCutBins = TQGridScanner::SplitCutBins;
using BoundDirection = TQGridScanner::BoundDirection;

using std::cout;
using std::endl;
using std::vector;
using std::pair;
using std::tie;
using std::unique_ptr;
//using FixedBoundUser = TQGridScanner::FixedBoundUser;
//using RangeBoundUser = TQGridScanner::RangeBoundUser;

ClassImp(TQGridScanner)

TQGridScanner::TQGridScanner(const TString& resultsName, TQSignificanceEvaluator* evaluator):
  TNamed("TQGridScanner", "TQGridScanner"),
  m_evaluator(evaluator),
  m_results(resultsName),
  m_runTimer("Scanning observables"),
  m_nDimHistName(resultsName)
{
  if (not evaluator) throw std::invalid_argument("Missing significance evaluator in TQGridScanner constructor");
  init();
}

TQGridScanner::TQGridScanner(const TString& resultsName, TQSignificanceEvaluator* evaluator, TList* obsToScan):
  TNamed("TQGridScanner", "TQGridScanner"),
  m_evaluator(evaluator),
  m_results(resultsName),
  m_runTimer("Scanning observables"),
  m_nDimHistName(resultsName)
{
  if (not evaluator) throw std::invalid_argument("Missing significance evaluator in TQGridScanner constructor");
  for(int i=0; i<=obsToScan->LastIndex(); i++) {
    m_obsNamesToScan.push_back(obsToScan->At(i)->GetName());
  }
  init();
}

TQGridScanner::~TQGridScanner() {
  for (auto obs : m_observables) {
    delete obs.second;
    obs.second = NULL;
  }
}

void TQGridScanner::init() {
  // TQGridScanner has a convoluted relationship with TQSignificanceEvaluator
  if (not m_evaluator->initialize(this)) {
    BREAKclass("Significance evaluator failed to initialize in TQGridScanner constructor");
  }

  INFOclass("Initializing observables");
  
  // Initialize the observables
  std::vector<TAxis*> axes;
  std::vector<TString> obs_names;
  if (m_evaluator->isSimple()) {
    // simple evaluator is used. Here we have one signal and one background definition which are
    // added to m_observables for the different regions
    // we only make the distinction between signal and background for plotting
    for (int i=0; i<m_evaluator->signalHists().at(0)->GetNdimensions(); ++i) {
      axes.clear();
      obs_names.clear();
      for(size_t ireg=0; ireg<m_evaluator->regions().size(); ireg++) {
        axes.push_back(m_evaluator->signalHists().at(ireg)->GetAxis(i));
        axes.push_back(m_evaluator->bkgHists().at(ireg)->GetAxis(i));
        obs_names.push_back(axes.back()->GetName());
        obs_names.push_back(axes.back()->GetName()); // add this twice for easier looping later on (could of course be avoided!)
      }
      m_observables.emplace(obs_names.back(), new TQGridScanObservable(obs_names, axes));
    }
  }
  else {
    // cl evaluator is used (with likelihood fit)
    // we don't need to make a distinction between signal and bkg
    // since the ranges for each cut configuration need to be the same on any process
    // and this is the only task to do with the axes.
    // simply add all observables/axes. The detailed configurations are done in TSCLSignificanceEvaluator
    // All n-dim histograms need to be consistent!
    for (int idim=0; idim<m_evaluator->multiDimHists().at(0)->GetNdimensions(); ++idim) {
      axes.clear();
      obs_names.clear();
      // this loops over all systematic variations
      for(size_t ichan=0; ichan<m_evaluator->multiDimHists().size(); ichan++) {
        axes.push_back(m_evaluator->multiDimHists().at(ichan)->GetAxis(idim));
        obs_names.push_back(axes.back()->GetName());
      }
      m_observables.emplace(obs_names.back(), new TQGridScanObservable(obs_names, axes));
    }
  }
  
}

TQGridScanObservable* TQGridScanner::getObs(const TString& obsName) {
  if ( m_observables.find(obsName) == m_observables.end() ) {
    BREAKclass("Observable with name %s not found in input histogram! Please check your input configuration and try again.", obsName.Data());
    return nullptr;
  } else {
    return m_observables.at(obsName);
  }
}

bool TQGridScanner::addSignalHist(THnBase* hist) {
  m_signalHists.push_back(hist);
  if (m_signalHists.size() > 1) {
    return TQTHnBaseUtils::checkConsistency(hist, m_signalHists[0], true);
  }
  return true;
}

bool TQGridScanner::addBkgHist(THnBase* hist) {
  m_bkgHists.push_back(hist);
  if (m_bkgHists.size() > 1) {
    return TQTHnBaseUtils::checkConsistency(hist, m_bkgHists[0], true);
  }
  return true;
}

void TQGridScanner::addFOMDefinitions(const std::vector <TString>& definitions) {
  // Add names for FOMs that are being evaluated
  this->results()->FOMDefinitions = definitions;
}

void TQGridScanner::extractInputHistogramProjections() {
  DEBUGclass("This function will save the input distributions in the TQGridScanResults for the first defined region only!");
  // TODO: Add support for adding input hists for the multiple regions!

  gROOT->SetBatch(true);
  int ireg = 0;
  for (auto reg : m_evaluator->regions()) {
    TQGridScanResults::InputHists newInputHists;
    newInputHists.regionName = reg;
    m_results.inputHistsSig()->push_back(newInputHists);
    m_results.inputHistsBkg()->push_back(newInputHists);
    for (int i=0; i<m_evaluator->signalHists().at(0)->GetNdimensions(); i++) {
      auto hist_sig = unique_ptr<TH1F>();
      auto hist_bkg = unique_ptr<TH1F>();
      hist_sig = unique_ptr<TH1F>(reinterpret_cast<TH1F*>(m_evaluator->signalHists().at(ireg)->Projection(i)));
      hist_sig->SetName(m_evaluator->signalHists().at(ireg)->GetAxis(i)->GetName());
      m_results.inputHistsSig()->back().hists.push_back(*hist_sig.get());
      hist_sig->SetName(TString::Format("%s_%d", hist_sig->GetName(), i)); // to get rid of memory leak warnings
      hist_bkg = unique_ptr<TH1F>(reinterpret_cast<TH1F*>(m_evaluator->bkgHists().at(ireg)->Projection(i)));
      hist_bkg->SetName(m_evaluator->bkgHists().at(ireg)->GetAxis(i)->GetName());
      m_results.inputHistsBkg()->back().hists.push_back(*hist_bkg.get());
      hist_bkg->SetName(TString::Format("%s_%d", hist_bkg->GetName(), i)); // to get rid of memory leak warnings
    }
    ireg++;
  }
  gROOT->SetBatch(false);

}

void TQGridScanner::dumpInputHistogramProjections(TQTaggable& tags) {
  // This function dumps the input distributions of the first defined region
  // This function is only useful for debugging when the scan is not working
  // For proper plotting of the input distributions TQGridScanResults::plotInputDistributions is used!

  DEBUGclass("This function will plot the input distributions for the first defined region only!");
  gROOT->SetBatch(true);
  TStyle *style = TQHistogramUtils::ATLASstyle();
  style->SetName("ATLAS");
  gROOT->SetStyle("ATLAS");
  gROOT->ForceStyle();

  for (int i=0; i<m_signalHists[0]->GetNdimensions(); i++) {
    auto hist_sig = unique_ptr<TH1F>();
    auto hist_bkg = unique_ptr<TH1F>();
    auto c = TQGridScanStyle::defaultCanvas();
    c->cd();

    hist_sig = unique_ptr<TH1F>(reinterpret_cast<TH1F*>(m_signalHists[0]->Projection(i)));
    hist_bkg = unique_ptr<TH1F>(reinterpret_cast<TH1F*>(m_bkgHists[0]->Projection(i)));

    TQGridScanStyle::setStyle(c.get(), hist_sig.get(), tags);
    TQGridScanStyle::setStyleInputHists(hist_sig.get(), hist_bkg.get(), tags);

    // Add generic legend
    unique_ptr<TLegend> leg (new TLegend(0.65, 0.75, 0.9, 0.9));
    leg->AddEntry(hist_sig.get(), "Signal", "F");
    leg->AddEntry(hist_bkg.get(), "Total Bkg", "F");

    // Draw not normalized histogram
    hist_bkg->GetYaxis()->SetTitle("Events");
    hist_bkg->Draw("hist");
    hist_sig->Draw("histsame");
    leg->Draw("same");

    // Save
    TString baseFilePath = tags.getTagStringDefault("plotDirectory", "plots/");
    baseFilePath = TQFolder::concatPaths(baseFilePath, "input-observables");
    TString obsName = TQFolder::makeValidIdentifier(m_signalHists[0]->GetAxis(i)->GetName());
    TString extension = tags.getTagStringDefault("plotFormat","pdf");
    TQUtils::ensureDirectory(TQPathManager::getPathManager()->getTargetPath(baseFilePath));
    TString outputname =  TQFolder::concatPaths(baseFilePath, "inputDistribution_"+obsName);
    c->SaveAs( TQPathManager::getPathManager()->getTargetPath(outputname+"."+extension).c_str() );

    // Save normalized histogram
    c->Clear();
    hist_sig->Scale(1./hist_sig->GetSumOfWeights());
    hist_bkg->Scale(1./hist_bkg->GetSumOfWeights());
    double ymax1 = TQHistogramUtils::getHistogramMaximum(2, hist_sig.get(), hist_bkg.get());
    hist_sig->GetYaxis()->SetRangeUser(0, ymax1*3/2.);
    hist_sig->GetYaxis()->SetTitle("Normalized Events");
    hist_sig->Draw("hist");
    hist_bkg->Draw("histsame");
    leg->Draw("same");
    c->SaveAs( TQPathManager::getPathManager()->getTargetPath(outputname+"_norm."+extension).c_str() );

  }
  gROOT->SetBatch(false);
}

int TQGridScanner::prepare() {
  for (auto& obsPair : m_observables) {
    auto& obs = obsPair.second;
    const auto& bounds = *obs->bounds();
    // If no bounds have been set, skip this obs
    if (bounds) {
      const auto boundsType = bounds->which();
      if (boundsType == BoundsType::normal) {
        const auto& normalBounds = *obs->normalBounds();
        const auto& lowerBounds = normalBounds.lower();
        const auto& upperBounds = normalBounds.upper();
        // If fixed cuts are applied on both sides, there's nothing to scan
        if (lowerBounds.isFixed() and upperBounds.isFixed()) {
          int lowerBin = lowerBounds.range()[0];
          int upperBin = upperBounds.range()[0];
          obs->setAxesRange(lowerBin, upperBin);
          // print info
          float lowerVal = obs->axis()->GetBinLowEdge(lowerBin);
          float upperVal = obs->axis()->GetBinUpEdge(upperBin);
          TString info = "Setting a fixed cut on variable '%f";
          if (lowerBin == TQGridScanBound::BinExtrema::min) info += " (underflow bin)";
          info += " < %s < %f";
          if (upperBin == TQGridScanBound::BinExtrema::max) info += " (overflow bin)";
          info += "'";
          INFOclass(TString::Format(info, lowerVal, obs->name().Data(), upperVal));
        } else {
          m_nPoints *= lowerBounds.nPoints();
          m_nPoints *= upperBounds.nPoints();
          m_normalObs.push_back(obs);
          // this is just a dummy value
          m_normalBounds.push_back({0, 0});
          m_results.normalObs()->push_back(new TQGridScanNormalObservable(obs->axes(),normalBounds));
        }
      } else {
        const auto splitBounds = *obs->splitBounds();
        if (splitBounds.isFixed()) {
          m_splitObs.push_back(obs);
          m_splitBins.push_back(-1);
        } else {
          m_nPoints *= splitBounds.nPoints();
          m_splitScanObs.push_back(obs);
          m_splitScanBins.push_back(-1);
          m_results.splitObs()->push_back(new TQGridScanSplitObservable(obs->axes(),splitBounds));
        }
      }
    }
  }
  
  return true;
}

void TQGridScanner::run(std::vector<int> chunk) {
  DEBUGclass("Running gridscanner for points in range [%d, %d]", chunk[0], chunk[1]);
  m_runTimer.Init(m_nPoints);
  m_heartbeat = TQUtils::getCurrentTime();
  m_nPointsProcessed = 0;
  
  if (m_nPoints > 1) {
    INFOclass("Scanning %d points", m_nPoints);
    scan(m_normalBounds.begin(), m_normalObs.begin(), chunk);
  }  else if (m_nPoints == 1) {
    // This is the case where no cut is specified to be scanned
    // This can be intended in order to extract the
    // statistics model for one specific cut configuration.
    // So let's do this, but nevertheless, throw a warning before
    WARNclass("No cuts are specified to be scanned in your configuration! However, if you use the cl evaluator you are lucky; the gridscanner will dump the statistics model corresponding to your specified cuts. Maybe you even intended this behavior? Then: well done, Sir!");
    TString outFileName = this->getTagStringDefault("cl.workspaceOutFileName", "workspace.root");
    m_evaluator->exportWorkspace(outFileName);
  }
}

void TQGridScanner::scan(CutBounds::iterator obsValsIter, ObsVec::iterator obsIter, std::vector<int> chunk) {
  auto obs = *obsIter;
  const auto& bounds = *obs->normalBounds();
  const auto lowerBounds = bounds.lower();
  const auto lowerBoundsRange = lowerBounds.range();
  const auto upperBounds = bounds.upper();
  const auto upperBoundsRange = upperBounds.range();
  for (auto lowerBin = lowerBoundsRange[0]; lowerBin <= lowerBoundsRange[1]; lowerBin += lowerBoundsRange[2]) {
    for (auto upperBin = upperBoundsRange[0]; upperBin <= upperBoundsRange[1]; upperBin += upperBoundsRange[2]) {
      // Only bins from the set ranges are kept when the ndim hist is projected onto any number of
      //   axes. There's an option to ignore the set ranges, but we don't want that.
      // See SetRange documentation for why we can't pass it (0,0)
      // TODO: in setAxesRange function we should not only set all axes range for the different regions but
      // also the ones for the different systematic variations, if available!
      if (upperBin == 0) {
        obs->setAxesRange(-1, 0);
      } else {
        obs->setAxesRange(lowerBin, upperBin);
      }

      obsValsIter->first = lowerBin;
      obsValsIter->second = upperBin;

      updateHeartbeat();
      auto isFinalLevel = (obsIter + 1 == m_normalObs.end());
      bool isPointInChunk = false;
      if (isFinalLevel) {
        if (m_splitScanObs.empty()) {
          isPointInChunk = evaluatePoint(chunk);
          updateProgress();
        }
        if (not m_splitScanObs.empty()) { // split scan observable used
          scanSplit(m_splitScanBins.begin(), m_splitScanObs.begin(), chunk);
        } else if (not m_splitObs.empty()) { // fixed split observable used
          if (isPointInChunk) { // only evaluate when we are in specified chunk of points
            auto signif2 = splitSignif2(m_splitBins.begin(), m_splitObs.begin(), false);
            auto signif = sqrt(signif2);
            m_evaluator->info += TString::Format("FOM=%f", signif);
            m_results.points()->emplace_back(m_normalBounds, signif, m_evaluator->info);
          }
        } else { // only upper and lower observable scans used
          if (isPointInChunk) { // only evaluate when we are in specified chunk of points
            auto foms = m_evaluator->evaluateMultiple();
            if (foms.empty()) {BREAKclass("No valid figure of merits could be retrieved from evaluator. Please check your configuration and try again!");}
            m_results.points()->emplace_back(m_normalBounds, foms, m_evaluator->info);
          }
        }
      } else {
        scan(obsValsIter + 1, obsIter + 1, chunk);
      }
    }
  }
}

double TQGridScanner::splitSignif2(SplitCutBins::iterator obsBinsIter, ObsVec::iterator obsIter, bool isScanSplit) {
  double signifQuadrature = 0;
  auto obs = *obsIter;
  //auto axis = obs->axis();
  const auto bin = *obsBinsIter;
  auto isFinalLevel = (obsIter + 1 == (isScanSplit ? m_splitScanObs.end() : m_splitObs.end()));

  // sums in quadrature the significance both below and above the split point
  // below
  obs->setAxesRange(BinExtrema::min, bin - 1);
  signifQuadrature += splitSignifHandleRecursion(isFinalLevel, isScanSplit, obsBinsIter, obsIter);

  // above
  obs->setAxesRange(bin, BinExtrema::max);
  signifQuadrature += splitSignifHandleRecursion(isFinalLevel, isScanSplit, obsBinsIter, obsIter);

  return signifQuadrature;
}

double TQGridScanner::splitSignifHandleRecursion(
    bool isFinalLevel,
    bool isScanSplit,
    SplitCutBins::iterator obsBinsIter,
    ObsVec::iterator obsIter
) {
  double signifQuadrature = 0;
  if (isFinalLevel) {
    // if we are currently iterating over scan split observables, iterate over non-scan split observables
    // if there are any
    if (isScanSplit and not m_splitObs.empty()) {
      auto signif2 = splitSignif2(m_splitBins.begin(), m_splitObs.begin(), false);
      signifQuadrature += signif2;
    } else {
      double signif = 0;
      // not actually compatible for multiple FOMs with a split cut! Only first specified FOM is used
      if (m_evaluator->m_multipleFOMsCompatible) {
        auto foms = m_evaluator->evaluateMultiple();
        if (foms.empty()) {BREAKclass("No valid figure of merits could be retrieved from evaluator. Please check your configuration and try again!");}
        signif = foms[0]; 
      } else {
        signif = m_evaluator->evaluate();
      }
      signifQuadrature += signif*signif;
    }
  } else {
    auto signif2 = splitSignif2(obsBinsIter + 1, obsIter + 1, isScanSplit);
    signifQuadrature += signif2;
  }
  return signifQuadrature;
}

// Iterate over split points for scan split observables. For each set of points, evaluate the significance
void TQGridScanner::scanSplit(SplitCutBins::iterator obsBinsIter, ObsVec::iterator obsIter, std::vector<int> chunk) {
  auto obs = *obsIter;
  const auto bound = *(obs->splitBounds());
  const auto boundRange = bound.range();
  for (auto bin = boundRange[0]; bin <= boundRange[1]; bin += boundRange[2]) {
    *obsBinsIter = bin;
    updateHeartbeat();
    //m_runTimer.DrawProgressBar(++m_nPointsProcessed);
    auto isFinalLevel = ((obsIter + 1) == m_splitScanObs.end());
    bool isPointInChunk = true;
    if (isFinalLevel) {
      isPointInChunk = evaluatePoint(chunk);
      updateProgress();
      if (isPointInChunk) {
        auto signif2 = splitSignif2(m_splitScanBins.begin(), m_splitScanObs.begin(), true);
        auto signif = sqrt(signif2);
        m_evaluator->info += TString::Format("FOM=%f", signif);
        std::vector<double> foms = {signif}; // fill in vector here to have simpler code later on
        m_results.points()->emplace_back(m_normalBounds, m_splitScanBins, foms, m_evaluator->info);
      }
    } else {
      scanSplit(obsBinsIter + 1, obsIter + 1, chunk);
    }
  }
}

bool TQGridScanner::updateHeartbeat() {
  // this routine will submit the heartbeat if neccessary
  if(m_heartBeatInterval == 0)
    return false;
  if(m_heartBeatCommand.IsNull())
    return false;
  unsigned long t = TQUtils::getCurrentTime();
  if(m_heartbeat + m_heartBeatInterval > t)
    return false;
  m_heartbeat = t;
  gSystem->Exec(m_heartBeatCommand.Data());
  return true;
}

bool TQGridScanner::evaluatePoint(std::vector<int> chunk) {
  // checks whether we are at a point in the specified chunk
  if (chunk.empty()) {
    // no chunk specified, return true
    DEBUGclass("No chunk specified, evaluting point!");
    return true;
  } else
    if (chunk[0] >= m_nPointsProcessed && m_nPointsProcessed < chunk[1]) {
      DEBUGclass("Current point within specified chunk, evaluate point!");
      return true;
    } else {
      DEBUGclass("Current point not within specified chunk, don't evaluate point!");
      return false;
    }
}

void TQGridScanner::updateProgress() {
  ++m_nPointsProcessed;
  m_runTimer.DrawProgressBar(m_nPointsProcessed);

  if ( ( (m_nPoints < 10 )|| (m_nPointsProcessed % (m_nPoints / 10) == 0) ) && (m_nPoints > 0)) {
    INFOclass("%d percent points processed!", (int)((float)(m_nPointsProcessed)/m_nPoints*100+1));

  }
}
 TQGridScanner.cxx:1
 TQGridScanner.cxx:2
 TQGridScanner.cxx:3
 TQGridScanner.cxx:4
 TQGridScanner.cxx:5
 TQGridScanner.cxx:6
 TQGridScanner.cxx:7
 TQGridScanner.cxx:8
 TQGridScanner.cxx:9
 TQGridScanner.cxx:10
 TQGridScanner.cxx:11
 TQGridScanner.cxx:12
 TQGridScanner.cxx:13
 TQGridScanner.cxx:14
 TQGridScanner.cxx:15
 TQGridScanner.cxx:16
 TQGridScanner.cxx:17
 TQGridScanner.cxx:18
 TQGridScanner.cxx:19
 TQGridScanner.cxx:20
 TQGridScanner.cxx:21
 TQGridScanner.cxx:22
 TQGridScanner.cxx:23
 TQGridScanner.cxx:24
 TQGridScanner.cxx:25
 TQGridScanner.cxx:26
 TQGridScanner.cxx:27
 TQGridScanner.cxx:28
 TQGridScanner.cxx:29
 TQGridScanner.cxx:30
 TQGridScanner.cxx:31
 TQGridScanner.cxx:32
 TQGridScanner.cxx:33
 TQGridScanner.cxx:34
 TQGridScanner.cxx:35
 TQGridScanner.cxx:36
 TQGridScanner.cxx:37
 TQGridScanner.cxx:38
 TQGridScanner.cxx:39
 TQGridScanner.cxx:40
 TQGridScanner.cxx:41
 TQGridScanner.cxx:42
 TQGridScanner.cxx:43
 TQGridScanner.cxx:44
 TQGridScanner.cxx:45
 TQGridScanner.cxx:46
 TQGridScanner.cxx:47
 TQGridScanner.cxx:48
 TQGridScanner.cxx:49
 TQGridScanner.cxx:50
 TQGridScanner.cxx:51
 TQGridScanner.cxx:52
 TQGridScanner.cxx:53
 TQGridScanner.cxx:54
 TQGridScanner.cxx:55
 TQGridScanner.cxx:56
 TQGridScanner.cxx:57
 TQGridScanner.cxx:58
 TQGridScanner.cxx:59
 TQGridScanner.cxx:60
 TQGridScanner.cxx:61
 TQGridScanner.cxx:62
 TQGridScanner.cxx:63
 TQGridScanner.cxx:64
 TQGridScanner.cxx:65
 TQGridScanner.cxx:66
 TQGridScanner.cxx:67
 TQGridScanner.cxx:68
 TQGridScanner.cxx:69
 TQGridScanner.cxx:70
 TQGridScanner.cxx:71
 TQGridScanner.cxx:72
 TQGridScanner.cxx:73
 TQGridScanner.cxx:74
 TQGridScanner.cxx:75
 TQGridScanner.cxx:76
 TQGridScanner.cxx:77
 TQGridScanner.cxx:78
 TQGridScanner.cxx:79
 TQGridScanner.cxx:80
 TQGridScanner.cxx:81
 TQGridScanner.cxx:82
 TQGridScanner.cxx:83
 TQGridScanner.cxx:84
 TQGridScanner.cxx:85
 TQGridScanner.cxx:86
 TQGridScanner.cxx:87
 TQGridScanner.cxx:88
 TQGridScanner.cxx:89
 TQGridScanner.cxx:90
 TQGridScanner.cxx:91
 TQGridScanner.cxx:92
 TQGridScanner.cxx:93
 TQGridScanner.cxx:94
 TQGridScanner.cxx:95
 TQGridScanner.cxx:96
 TQGridScanner.cxx:97
 TQGridScanner.cxx:98
 TQGridScanner.cxx:99
 TQGridScanner.cxx:100
 TQGridScanner.cxx:101
 TQGridScanner.cxx:102
 TQGridScanner.cxx:103
 TQGridScanner.cxx:104
 TQGridScanner.cxx:105
 TQGridScanner.cxx:106
 TQGridScanner.cxx:107
 TQGridScanner.cxx:108
 TQGridScanner.cxx:109
 TQGridScanner.cxx:110
 TQGridScanner.cxx:111
 TQGridScanner.cxx:112
 TQGridScanner.cxx:113
 TQGridScanner.cxx:114
 TQGridScanner.cxx:115
 TQGridScanner.cxx:116
 TQGridScanner.cxx:117
 TQGridScanner.cxx:118
 TQGridScanner.cxx:119
 TQGridScanner.cxx:120
 TQGridScanner.cxx:121
 TQGridScanner.cxx:122
 TQGridScanner.cxx:123
 TQGridScanner.cxx:124
 TQGridScanner.cxx:125
 TQGridScanner.cxx:126
 TQGridScanner.cxx:127
 TQGridScanner.cxx:128
 TQGridScanner.cxx:129
 TQGridScanner.cxx:130
 TQGridScanner.cxx:131
 TQGridScanner.cxx:132
 TQGridScanner.cxx:133
 TQGridScanner.cxx:134
 TQGridScanner.cxx:135
 TQGridScanner.cxx:136
 TQGridScanner.cxx:137
 TQGridScanner.cxx:138
 TQGridScanner.cxx:139
 TQGridScanner.cxx:140
 TQGridScanner.cxx:141
 TQGridScanner.cxx:142
 TQGridScanner.cxx:143
 TQGridScanner.cxx:144
 TQGridScanner.cxx:145
 TQGridScanner.cxx:146
 TQGridScanner.cxx:147
 TQGridScanner.cxx:148
 TQGridScanner.cxx:149
 TQGridScanner.cxx:150
 TQGridScanner.cxx:151
 TQGridScanner.cxx:152
 TQGridScanner.cxx:153
 TQGridScanner.cxx:154
 TQGridScanner.cxx:155
 TQGridScanner.cxx:156
 TQGridScanner.cxx:157
 TQGridScanner.cxx:158
 TQGridScanner.cxx:159
 TQGridScanner.cxx:160
 TQGridScanner.cxx:161
 TQGridScanner.cxx:162
 TQGridScanner.cxx:163
 TQGridScanner.cxx:164
 TQGridScanner.cxx:165
 TQGridScanner.cxx:166
 TQGridScanner.cxx:167
 TQGridScanner.cxx:168
 TQGridScanner.cxx:169
 TQGridScanner.cxx:170
 TQGridScanner.cxx:171
 TQGridScanner.cxx:172
 TQGridScanner.cxx:173
 TQGridScanner.cxx:174
 TQGridScanner.cxx:175
 TQGridScanner.cxx:176
 TQGridScanner.cxx:177
 TQGridScanner.cxx:178
 TQGridScanner.cxx:179
 TQGridScanner.cxx:180
 TQGridScanner.cxx:181
 TQGridScanner.cxx:182
 TQGridScanner.cxx:183
 TQGridScanner.cxx:184
 TQGridScanner.cxx:185
 TQGridScanner.cxx:186
 TQGridScanner.cxx:187
 TQGridScanner.cxx:188
 TQGridScanner.cxx:189
 TQGridScanner.cxx:190
 TQGridScanner.cxx:191
 TQGridScanner.cxx:192
 TQGridScanner.cxx:193
 TQGridScanner.cxx:194
 TQGridScanner.cxx:195
 TQGridScanner.cxx:196
 TQGridScanner.cxx:197
 TQGridScanner.cxx:198
 TQGridScanner.cxx:199
 TQGridScanner.cxx:200
 TQGridScanner.cxx:201
 TQGridScanner.cxx:202
 TQGridScanner.cxx:203
 TQGridScanner.cxx:204
 TQGridScanner.cxx:205
 TQGridScanner.cxx:206
 TQGridScanner.cxx:207
 TQGridScanner.cxx:208
 TQGridScanner.cxx:209
 TQGridScanner.cxx:210
 TQGridScanner.cxx:211
 TQGridScanner.cxx:212
 TQGridScanner.cxx:213
 TQGridScanner.cxx:214
 TQGridScanner.cxx:215
 TQGridScanner.cxx:216
 TQGridScanner.cxx:217
 TQGridScanner.cxx:218
 TQGridScanner.cxx:219
 TQGridScanner.cxx:220
 TQGridScanner.cxx:221
 TQGridScanner.cxx:222
 TQGridScanner.cxx:223
 TQGridScanner.cxx:224
 TQGridScanner.cxx:225
 TQGridScanner.cxx:226
 TQGridScanner.cxx:227
 TQGridScanner.cxx:228
 TQGridScanner.cxx:229
 TQGridScanner.cxx:230
 TQGridScanner.cxx:231
 TQGridScanner.cxx:232
 TQGridScanner.cxx:233
 TQGridScanner.cxx:234
 TQGridScanner.cxx:235
 TQGridScanner.cxx:236
 TQGridScanner.cxx:237
 TQGridScanner.cxx:238
 TQGridScanner.cxx:239
 TQGridScanner.cxx:240
 TQGridScanner.cxx:241
 TQGridScanner.cxx:242
 TQGridScanner.cxx:243
 TQGridScanner.cxx:244
 TQGridScanner.cxx:245
 TQGridScanner.cxx:246
 TQGridScanner.cxx:247
 TQGridScanner.cxx:248
 TQGridScanner.cxx:249
 TQGridScanner.cxx:250
 TQGridScanner.cxx:251
 TQGridScanner.cxx:252
 TQGridScanner.cxx:253
 TQGridScanner.cxx:254
 TQGridScanner.cxx:255
 TQGridScanner.cxx:256
 TQGridScanner.cxx:257
 TQGridScanner.cxx:258
 TQGridScanner.cxx:259
 TQGridScanner.cxx:260
 TQGridScanner.cxx:261
 TQGridScanner.cxx:262
 TQGridScanner.cxx:263
 TQGridScanner.cxx:264
 TQGridScanner.cxx:265
 TQGridScanner.cxx:266
 TQGridScanner.cxx:267
 TQGridScanner.cxx:268
 TQGridScanner.cxx:269
 TQGridScanner.cxx:270
 TQGridScanner.cxx:271
 TQGridScanner.cxx:272
 TQGridScanner.cxx:273
 TQGridScanner.cxx:274
 TQGridScanner.cxx:275
 TQGridScanner.cxx:276
 TQGridScanner.cxx:277
 TQGridScanner.cxx:278
 TQGridScanner.cxx:279
 TQGridScanner.cxx:280
 TQGridScanner.cxx:281
 TQGridScanner.cxx:282
 TQGridScanner.cxx:283
 TQGridScanner.cxx:284
 TQGridScanner.cxx:285
 TQGridScanner.cxx:286
 TQGridScanner.cxx:287
 TQGridScanner.cxx:288
 TQGridScanner.cxx:289
 TQGridScanner.cxx:290
 TQGridScanner.cxx:291
 TQGridScanner.cxx:292
 TQGridScanner.cxx:293
 TQGridScanner.cxx:294
 TQGridScanner.cxx:295
 TQGridScanner.cxx:296
 TQGridScanner.cxx:297
 TQGridScanner.cxx:298
 TQGridScanner.cxx:299
 TQGridScanner.cxx:300
 TQGridScanner.cxx:301
 TQGridScanner.cxx:302
 TQGridScanner.cxx:303
 TQGridScanner.cxx:304
 TQGridScanner.cxx:305
 TQGridScanner.cxx:306
 TQGridScanner.cxx:307
 TQGridScanner.cxx:308
 TQGridScanner.cxx:309
 TQGridScanner.cxx:310
 TQGridScanner.cxx:311
 TQGridScanner.cxx:312
 TQGridScanner.cxx:313
 TQGridScanner.cxx:314
 TQGridScanner.cxx:315
 TQGridScanner.cxx:316
 TQGridScanner.cxx:317
 TQGridScanner.cxx:318
 TQGridScanner.cxx:319
 TQGridScanner.cxx:320
 TQGridScanner.cxx:321
 TQGridScanner.cxx:322
 TQGridScanner.cxx:323
 TQGridScanner.cxx:324
 TQGridScanner.cxx:325
 TQGridScanner.cxx:326
 TQGridScanner.cxx:327
 TQGridScanner.cxx:328
 TQGridScanner.cxx:329
 TQGridScanner.cxx:330
 TQGridScanner.cxx:331
 TQGridScanner.cxx:332
 TQGridScanner.cxx:333
 TQGridScanner.cxx:334
 TQGridScanner.cxx:335
 TQGridScanner.cxx:336
 TQGridScanner.cxx:337
 TQGridScanner.cxx:338
 TQGridScanner.cxx:339
 TQGridScanner.cxx:340
 TQGridScanner.cxx:341
 TQGridScanner.cxx:342
 TQGridScanner.cxx:343
 TQGridScanner.cxx:344
 TQGridScanner.cxx:345
 TQGridScanner.cxx:346
 TQGridScanner.cxx:347
 TQGridScanner.cxx:348
 TQGridScanner.cxx:349
 TQGridScanner.cxx:350
 TQGridScanner.cxx:351
 TQGridScanner.cxx:352
 TQGridScanner.cxx:353
 TQGridScanner.cxx:354
 TQGridScanner.cxx:355
 TQGridScanner.cxx:356
 TQGridScanner.cxx:357
 TQGridScanner.cxx:358
 TQGridScanner.cxx:359
 TQGridScanner.cxx:360
 TQGridScanner.cxx:361
 TQGridScanner.cxx:362
 TQGridScanner.cxx:363
 TQGridScanner.cxx:364
 TQGridScanner.cxx:365
 TQGridScanner.cxx:366
 TQGridScanner.cxx:367
 TQGridScanner.cxx:368
 TQGridScanner.cxx:369
 TQGridScanner.cxx:370
 TQGridScanner.cxx:371
 TQGridScanner.cxx:372
 TQGridScanner.cxx:373
 TQGridScanner.cxx:374
 TQGridScanner.cxx:375
 TQGridScanner.cxx:376
 TQGridScanner.cxx:377
 TQGridScanner.cxx:378
 TQGridScanner.cxx:379
 TQGridScanner.cxx:380
 TQGridScanner.cxx:381
 TQGridScanner.cxx:382
 TQGridScanner.cxx:383
 TQGridScanner.cxx:384
 TQGridScanner.cxx:385
 TQGridScanner.cxx:386
 TQGridScanner.cxx:387
 TQGridScanner.cxx:388
 TQGridScanner.cxx:389
 TQGridScanner.cxx:390
 TQGridScanner.cxx:391
 TQGridScanner.cxx:392
 TQGridScanner.cxx:393
 TQGridScanner.cxx:394
 TQGridScanner.cxx:395
 TQGridScanner.cxx:396
 TQGridScanner.cxx:397
 TQGridScanner.cxx:398
 TQGridScanner.cxx:399
 TQGridScanner.cxx:400
 TQGridScanner.cxx:401
 TQGridScanner.cxx:402
 TQGridScanner.cxx:403
 TQGridScanner.cxx:404
 TQGridScanner.cxx:405
 TQGridScanner.cxx:406
 TQGridScanner.cxx:407
 TQGridScanner.cxx:408
 TQGridScanner.cxx:409
 TQGridScanner.cxx:410
 TQGridScanner.cxx:411
 TQGridScanner.cxx:412
 TQGridScanner.cxx:413
 TQGridScanner.cxx:414
 TQGridScanner.cxx:415
 TQGridScanner.cxx:416
 TQGridScanner.cxx:417
 TQGridScanner.cxx:418
 TQGridScanner.cxx:419
 TQGridScanner.cxx:420
 TQGridScanner.cxx:421
 TQGridScanner.cxx:422
 TQGridScanner.cxx:423
 TQGridScanner.cxx:424
 TQGridScanner.cxx:425
 TQGridScanner.cxx:426
 TQGridScanner.cxx:427
 TQGridScanner.cxx:428
 TQGridScanner.cxx:429
 TQGridScanner.cxx:430
 TQGridScanner.cxx:431
 TQGridScanner.cxx:432
 TQGridScanner.cxx:433
 TQGridScanner.cxx:434
 TQGridScanner.cxx:435
 TQGridScanner.cxx:436
 TQGridScanner.cxx:437
 TQGridScanner.cxx:438
 TQGridScanner.cxx:439
 TQGridScanner.cxx:440
 TQGridScanner.cxx:441
 TQGridScanner.cxx:442
 TQGridScanner.cxx:443
 TQGridScanner.cxx:444
 TQGridScanner.cxx:445
 TQGridScanner.cxx:446
 TQGridScanner.cxx:447
 TQGridScanner.cxx:448
 TQGridScanner.cxx:449
 TQGridScanner.cxx:450
 TQGridScanner.cxx:451
 TQGridScanner.cxx:452
 TQGridScanner.cxx:453
 TQGridScanner.cxx:454
 TQGridScanner.cxx:455
 TQGridScanner.cxx:456
 TQGridScanner.cxx:457
 TQGridScanner.cxx:458
 TQGridScanner.cxx:459
 TQGridScanner.cxx:460
 TQGridScanner.cxx:461
 TQGridScanner.cxx:462
 TQGridScanner.cxx:463
 TQGridScanner.cxx:464
 TQGridScanner.cxx:465
 TQGridScanner.cxx:466
 TQGridScanner.cxx:467
 TQGridScanner.cxx:468
 TQGridScanner.cxx:469
 TQGridScanner.cxx:470
 TQGridScanner.cxx:471
 TQGridScanner.cxx:472
 TQGridScanner.cxx:473
 TQGridScanner.cxx:474
 TQGridScanner.cxx:475
 TQGridScanner.cxx:476
 TQGridScanner.cxx:477
 TQGridScanner.cxx:478
 TQGridScanner.cxx:479
 TQGridScanner.cxx:480
 TQGridScanner.cxx:481
 TQGridScanner.cxx:482
 TQGridScanner.cxx:483
 TQGridScanner.cxx:484
 TQGridScanner.cxx:485
 TQGridScanner.cxx:486
 TQGridScanner.cxx:487
 TQGridScanner.cxx:488
 TQGridScanner.cxx:489
 TQGridScanner.cxx:490
 TQGridScanner.cxx:491
 TQGridScanner.cxx:492
 TQGridScanner.cxx:493
 TQGridScanner.cxx:494
 TQGridScanner.cxx:495
 TQGridScanner.cxx:496
 TQGridScanner.cxx:497
 TQGridScanner.cxx:498
 TQGridScanner.cxx:499
 TQGridScanner.cxx:500
 TQGridScanner.cxx:501
 TQGridScanner.cxx:502
 TQGridScanner.cxx:503
 TQGridScanner.cxx:504
 TQGridScanner.cxx:505
 TQGridScanner.cxx:506
 TQGridScanner.cxx:507
 TQGridScanner.cxx:508
 TQGridScanner.cxx:509
 TQGridScanner.cxx:510
 TQGridScanner.cxx:511
 TQGridScanner.cxx:512
 TQGridScanner.cxx:513
 TQGridScanner.cxx:514
 TQGridScanner.cxx:515
 TQGridScanner.cxx:516
 TQGridScanner.cxx:517
 TQGridScanner.cxx:518
 TQGridScanner.cxx:519
 TQGridScanner.cxx:520
 TQGridScanner.cxx:521
 TQGridScanner.cxx:522
 TQGridScanner.cxx:523
 TQGridScanner.cxx:524
 TQGridScanner.cxx:525
 TQGridScanner.cxx:526
 TQGridScanner.cxx:527
 TQGridScanner.cxx:528