From 1dc9de5439fb5f6accc1c10bc85db341b38efe2a Mon Sep 17 00:00:00 2001 From: pospelov <pospelov@fz-juelich.de> Date: Wed, 10 Oct 2012 15:22:33 +0200 Subject: [PATCH] New observer to write intermediate fit results in root tree and analysis examples. --- App/App.pro | 12 +- App/inc/App.h | 2 +- App/inc/AppLinkDef.h | 4 +- App/inc/{FittingHelper.h => FitSuiteHelper.h} | 35 ++-- App/inc/ROOTMinimizer.h | 2 +- .../{EventFrame.h => TreeEventStructure.h} | 55 +++++-- App/src/EventFrame.cpp | 29 ---- App/src/FitSuiteHelper.cpp | 114 +++++++++++++ App/src/FittingHelper.cpp | 48 ------ App/src/TestFittingModule.cpp | 10 +- App/src/TestMesoCrystal2.cpp | 8 +- App/src/TestRootTree.cpp | 7 +- App/src/TreeEventStructure.cpp | 57 +++++++ App/src/main.cpp | 12 +- Core/Core.pro | 3 +- Core/Tools/inc/IObserver.h | 22 +-- Core/Tools/src/IObserver.cpp | 43 +++++ Examples/Analysis/RootMacros/README | 9 + .../ex01_MesoGif}/MesoData.C | 0 .../ex01_MesoGif}/MesoData.h | 0 .../ex01_MesoGif}/analysis1.C | 0 .../ex01_MesoGif}/analysis2.C | 0 .../RootMacros/ex02_ReadFitData/FitData.C | 43 +++++ .../RootMacros/ex02_ReadFitData/FitData.h | 154 ++++++++++++++++++ .../ex02_ReadFitData/analyse_fitdata1.C | 61 +++++++ .../ex02_ReadFitData/analyse_fitdata2.C | 103 ++++++++++++ .../RootMacros/ex02_ReadFitData/make_class.C | 37 +++++ Examples/Performance/perf_history.txt | 4 + Macros/GitUtils/gisasfw_loc.png | Bin 14251 -> 15387 bytes 29 files changed, 736 insertions(+), 138 deletions(-) rename App/inc/{FittingHelper.h => FitSuiteHelper.h} (55%) rename App/inc/{EventFrame.h => TreeEventStructure.h} (51%) delete mode 100644 App/src/EventFrame.cpp create mode 100644 App/src/FitSuiteHelper.cpp delete mode 100644 App/src/FittingHelper.cpp create mode 100644 App/src/TreeEventStructure.cpp create mode 100644 Core/Tools/src/IObserver.cpp create mode 100644 Examples/Analysis/RootMacros/README rename Examples/Analysis/{MesoData => RootMacros/ex01_MesoGif}/MesoData.C (100%) rename Examples/Analysis/{MesoData => RootMacros/ex01_MesoGif}/MesoData.h (100%) rename Examples/Analysis/{MesoData => RootMacros/ex01_MesoGif}/analysis1.C (100%) rename Examples/Analysis/{MesoData => RootMacros/ex01_MesoGif}/analysis2.C (100%) create mode 100644 Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.C create mode 100644 Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.h create mode 100644 Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata1.C create mode 100644 Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata2.C create mode 100644 Examples/Analysis/RootMacros/ex02_ReadFitData/make_class.C diff --git a/App/App.pro b/App/App.pro index e402ee3bfcb..8b09c1fb114 100644 --- a/App/App.pro +++ b/App/App.pro @@ -16,8 +16,6 @@ SOURCES += \ src/AppOptionsDescription.cpp \ src/CommandLine.cpp \ src/DrawHelper.cpp \ - src/EventFrame.cpp \ - src/FittingHelper.cpp \ src/FunctionalTestFactory.cpp \ src/IFunctionalTest.cpp \ src/IsGISAXSTools.cpp \ @@ -43,7 +41,9 @@ SOURCES += \ src/TestMultiLayerRoughness.cpp \ src/TestPerformance.cpp \ src/TestRootTree.cpp \ - src/TestRoughness.cpp + src/TestRoughness.cpp \ + src/TreeEventStructure.cpp \ + src/FitSuiteHelper.cpp HEADERS += \ inc/App.h \ @@ -51,9 +51,7 @@ HEADERS += \ inc/AppOptionsDescription.h \ inc/CommandLine.h \ inc/DrawHelper.h \ - inc/EventFrame.h \ inc/FunctionalTestFactory.h \ - inc/FittingHelper.h \ inc/IFunctionalTest.h \ inc/IsGISAXSTools.h \ inc/ROOTMinimizer.h \ @@ -77,7 +75,9 @@ HEADERS += \ inc/TestMultiLayerRoughness.h \ inc/TestPerformance.h \ inc/TestRootTree.h \ - inc/TestRoughness.h + inc/TestRoughness.h \ + inc/TreeEventStructure.h \ + inc/FitSuiteHelper.h INCLUDEPATH += ./inc ../Core/Algorithms/inc ../Core/FormFactors/inc ../Core/Geometry/inc ../Core/Samples/inc ../Core/Tools/inc ../Core/PythonAPI/inc DEPENDPATH += ./inc ../Core/Algorithms/inc ../Core/FormFactors/inc ../Core/Geometry/inc ../Core/Samples/inc ../Core/Tools/inc ../Core/PythonAPI/inc diff --git a/App/inc/App.h b/App/inc/App.h index e6244f69a1a..dee1d9fdea6 100644 --- a/App/inc/App.h +++ b/App/inc/App.h @@ -16,6 +16,6 @@ #include "ISingleton.h" #include "DrawHelper.h" -#include "EventFrame.h" +#include "TreeEventStructure.h" #endif // APP_H diff --git a/App/inc/AppLinkDef.h b/App/inc/AppLinkDef.h index b297e139e77..e93d4c2ad0f 100644 --- a/App/inc/AppLinkDef.h +++ b/App/inc/AppLinkDef.h @@ -20,7 +20,7 @@ #pragma link C++ class ISingleton<DrawHelper>+; #pragma link C++ class DrawHelper+; - -#pragma link C++ class EventFrame+; +#pragma link C++ class TreeEventOutputData+; +#pragma link C++ class TreeEventFitData+; #endif diff --git a/App/inc/FittingHelper.h b/App/inc/FitSuiteHelper.h similarity index 55% rename from App/inc/FittingHelper.h rename to App/inc/FitSuiteHelper.h index 81a3b2eb809..7000380a897 100644 --- a/App/inc/FittingHelper.h +++ b/App/inc/FitSuiteHelper.h @@ -1,5 +1,5 @@ -#ifndef FITTINGHELPER_H -#define FITTINGHELPER_H +#ifndef FITSUITEHELPER_H +#define FITSUITEHELPER_H // ******************************************************************** // * The BornAgain project * // * Simulation of neutron and x-ray scattering at grazing incidence * @@ -9,7 +9,7 @@ // * eget quam orci. Quisque porta varius dui, quis posuere nibh * // * mollis quis. Mauris commodo rhoncus porttitor. * // ******************************************************************** -//! @file FittingHelper.h +//! @file FitSuiteHelper.h //! @brief Collection of helper classes for fitting from App //! @author Scientific Computing Group at FRM II //! @date 08.10.2012 @@ -18,17 +18,15 @@ #include "IObserver.h" #include <string> -namespace FittingHelper { - //- ------------------------------------------------------------------- -//! @class ObserveAndDraw -//! @brief Draw fit progress at the end of each FitSuite's minimization function call +//! @class FitSuiteObserverDraw +//! @brief Draw fit progress at the end of each FitSuite's iteration //- ------------------------------------------------------------------- -class ObserveAndDraw : public IObserver +class FitSuiteObserverDraw : public IObserver { public: - ObserveAndDraw(std::string canvas_name) : m_ncall(0), m_canvas_name(canvas_name) {} + FitSuiteObserverDraw(std::string canvas_name) : m_ncall(0), m_canvas_name(canvas_name) {} void update(IObservable *subject); private: int m_ncall; //! number of call of given observer @@ -36,7 +34,22 @@ private: }; -} +//- ------------------------------------------------------------------- +//! @class FitSuiteObserverWrite +//! @brief Save results of each fit iteration to the disk in the form of ROOT tree +//! If tree exist, data will be appended to it +//- ------------------------------------------------------------------- +class FitSuiteObserverWriteTree : public IObserver +{ +public: + FitSuiteObserverWriteTree(std::string file_name) : m_ncall(0), m_file_name(file_name) {} + void update(IObservable *subject); +private: + int m_ncall; + std::string m_file_name; //! canvas name were to draw +}; + + -#endif // FITTINGHELPER_H +#endif // FITSUITEHELPER_H diff --git a/App/inc/ROOTMinimizer.h b/App/inc/ROOTMinimizer.h index 94ea46df617..3d7d86717ed 100644 --- a/App/inc/ROOTMinimizer.h +++ b/App/inc/ROOTMinimizer.h @@ -44,7 +44,7 @@ public: //! return minimum function value virtual double getMinValue() { return m_root_minimizer->MinValue(); } - //! return pointer to the parameters values at the minimum + //! return value of variable corresponding the minimum of the function virtual double getValueOfVariableAtMinimum(size_t i) { if(i >= m_root_minimizer->NDim() ) throw OutOfBoundsException("ROOTMinimizer::getVariableAtMinimum() -> Wrong number of the variable"); return m_root_minimizer->X()[i]; diff --git a/App/inc/EventFrame.h b/App/inc/TreeEventStructure.h similarity index 51% rename from App/inc/EventFrame.h rename to App/inc/TreeEventStructure.h index 25f582eedfd..8782679774a 100644 --- a/App/inc/EventFrame.h +++ b/App/inc/TreeEventStructure.h @@ -1,5 +1,5 @@ -#ifndef EVENTFRAME_H -#define EVENTFRAME_H +#ifndef TREEEVENTSTRUCTURE_H +#define TREEEVENTSTRUCTURE_H // ******************************************************************** // * The BornAgain project * // * Simulation of neutron and x-ray scattering at grazing incidence * @@ -9,24 +9,27 @@ // * eget quam orci. Quisque porta varius dui, quis posuere nibh * // * mollis quis. Mauris commodo rhoncus porttitor. * // ******************************************************************** -//! @file EventFrame.h -//! @brief Definition of EventFrame class for writing in root files +//! @file TreeEventStructure.h +//! @brief Collection of classes to write output data in ROOT trees //! @author Scientific Computing Group at FRM II -//! @date 19.07.2012 - +//! @date 09.10.2012 #include "TObject.h" #include <vector> +#include <string> + + //- ------------------------------------------------------------------- -//! @class EventFrame -//! @brief event structure to save in the root file +//! @class TreeEventOutputData +//! @brief Data structure respresenting OutputData and mesocrystal settings +//! for writing/reading in/from the ROOT tree. //- ------------------------------------------------------------------- -class EventFrame +class TreeEventOutputData { public: - EventFrame(); - virtual ~EventFrame(){} + TreeEventOutputData(); + virtual ~TreeEventOutputData() { } void clear(); @@ -49,9 +52,35 @@ public: std::vector<std::vector<double > > valpha_f; // values of alpha_f for the frame std::vector<std::vector<double > > vphi_f; // values of phi_f for the frames - ClassDef(EventFrame,1) + ClassDef(TreeEventOutputData,1) +}; + + +//- ------------------------------------------------------------------- +//! @class TreeEventFitData +//! @brief Represent fit results after each iteration for writing/reading +//! in/from the ROOT tree +//- ------------------------------------------------------------------- +class TreeEventFitData +{ +public: + TreeEventFitData(); + virtual ~TreeEventFitData() { } + void clear(); + + int niter; // number of iteration + std::vector<std::vector<double > > real_data; // real data + std::vector<std::vector<double > > fit_data; // current fit iteration data + std::vector<std::vector<double > > diff; // chi2 difference between real and fit data + std::vector<std::vector<double > > axis0; // values of phi_f (made in 2D for convenient drawing) + std::vector<std::vector<double > > axis1; // values of alpha_f (made in 2D for convenient drawing) + double chi2; // current value of the function to minimize + std::vector<double> parvalues; // vector of minimization parameters + std::vector<std::string> parnames; // names of parameters + + ClassDef(TreeEventFitData,1) }; -#endif // EventFrame_H +#endif // TREEEVENTSTRUCTURE_H diff --git a/App/src/EventFrame.cpp b/App/src/EventFrame.cpp deleted file mode 100644 index e291a06a9b0..00000000000 --- a/App/src/EventFrame.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "EventFrame.h" - -EventFrame::EventFrame() -{ - clear(); -} - -void EventFrame::clear() -{ - nframe = 0; - alpha_i = 0; - phi_i = 0; - - nphi_f = 0; - phi_f_min = 0; - phi_f_max = 0; - - nalpha_f = 0; - alpha_f_min = 0; - alpha_f_max = 0; - - malpha = 0; - mphi = 0; - npR = 0; - - valpha_f.clear(); - vphi_f.clear(); - vi.clear(); -} diff --git a/App/src/FitSuiteHelper.cpp b/App/src/FitSuiteHelper.cpp new file mode 100644 index 00000000000..ae42c0d5233 --- /dev/null +++ b/App/src/FitSuiteHelper.cpp @@ -0,0 +1,114 @@ +#include "FitSuite.h" +#include "FitSuiteHelper.h" +#include "TreeEventStructure.h" +#include "IsGISAXSTools.h" + +#include "TCanvas.h" +#include "TPaveText.h" +#include "ChiSquaredModule.h" +#include "TROOT.h" +#include "TFile.h" +#include "TTree.h" + + +/* ************************************************************************* */ +// draw results of fit iteration in ROOT's canvas +/* ************************************************************************* */ +void FitSuiteObserverDraw::update(IObservable *subject) +{ + FitSuite *fitSuite = dynamic_cast<FitSuite *>(subject); + if( !fitSuite ) throw NullPointerException("FitSuiteObserverDraw::update() -> Error! Can't cast FitSuite"); + + std::cout << "FitObserver: " << " ncall" << m_ncall << " chi2:" << fitSuite->getChiSquaredModule()->getValue(); + // printing parameter values + for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { + std::cout << " " << (*it)->getName() << " " << (*it)->getValue(); + // std::cout << *(*it); + } + std::cout << std::endl; + + TCanvas *c1 = dynamic_cast<TCanvas *>( gROOT->FindObject(m_canvas_name.c_str()) ); + if(!c1) throw NullPointerException("FitSuiteObserverDraw::update() -> No access to canvas"); + c1->cd(3); + gPad->SetLogz(); + IsGISAXSTools::drawOutputDataInPad(*fitSuite->getChiSquaredModule()->getSimulationData(), "CONT4 Z", "current simulated data"); + c1->cd(4); + IsGISAXSTools::drawOutputDataDifference2D(*fitSuite->getChiSquaredModule()->getSimulationData(), *fitSuite->getChiSquaredModule()->getRealData(), "CONT4 Z", "difference"); + c1->cd(5); + + TPaveText *pt = new TPaveText(.05,.1,.95,.8); + char str[256]; + sprintf(str,"Iteration %d",m_ncall); + pt->AddText(str); + sprintf(str,"chi2 %e",fitSuite->getChiSquaredModule()->getValue()); + pt->AddText(str); + //pt->AddLine(.0,.5,1.,.5); + for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { + sprintf(str,"%s %f", (*it)->getName().c_str(), (*it)->getValue()); + pt->AddText(str); + } + pt->Draw(); + + m_ncall++; +} + + +/* ************************************************************************* */ +// Save results of each fit iteration to the disk in the form of ROOT tree +/* ************************************************************************* */ +void FitSuiteObserverWriteTree::update(IObservable *subject) +{ + std::string tree_name("FitSuiteTree"); + + FitSuite *fitSuite = dynamic_cast<FitSuite *>(subject); + if( !fitSuite ) throw NullPointerException("FitSuiteObserverWriteTree::update() -> Error! Can't cast FitSuite"); + + // preparing root file for writing + // if it is first call the file will be opened in 'recreate' mode, otherwise in 'update' mode + TFile *top(0); + if(m_ncall == 0) { + top = new TFile(m_file_name.c_str(),"RECREATE"); + } else { + top = new TFile(m_file_name.c_str(),"UPDATE"); + } + if( !top->IsOpen() ) { + throw RuntimeErrorException("FitSuiteObserverWriteTree::update() -> Can't open file "+ m_file_name + " for writing"); + } + + + // data object to write in the tree + TreeEventFitData *event = new TreeEventFitData(); + + // creating new tree + TTree *tree = dynamic_cast<TTree *>(top->Get(tree_name.c_str())); + if( tree == 0) { + // tree doesn't exist due to new file, making new tree + tree = new TTree(tree_name.c_str(),"Oh, my data"); + tree->Branch("Event",&event,16000,2); + } else { + // tree exists, pointing it to the new data + tree->SetBranchAddress("Event", &event); + } + + // filling data object with data from FitSuite + const OutputData<double > *real_data = fitSuite->getChiSquaredModule()->getRealData(); + const OutputData<double > *simu_data = fitSuite->getChiSquaredModule()->getSimulationData(); + IsGISAXSTools::exportOutputDataInVectors2D(*real_data, event->real_data, event->axis0, event->axis1); + IsGISAXSTools::exportOutputDataInVectors2D(*simu_data, event->fit_data, event->axis0, event->axis1); + event->chi2 = fitSuite->getChiSquaredModule()->getValue(); + for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { + event->parvalues.push_back( (*it)->getValue() ); + event->parnames.push_back( (*it)->getName().c_str() ); + } + event->niter = m_ncall; + + // appending data to the tree + tree->Fill(); + tree->Write(tree_name.c_str(), TObject::kOverwrite); + top->Close(); + delete top; // there is no need to delete tree since ROOT file takes care about all objects opened afterwards + delete event; + + m_ncall++; +} + diff --git a/App/src/FittingHelper.cpp b/App/src/FittingHelper.cpp deleted file mode 100644 index 320daa8cadf..00000000000 --- a/App/src/FittingHelper.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "FittingHelper.h" -#include "FitSuite.h" -#include "IsGISAXSTools.h" - -#include "TCanvas.h" -#include "TPaveText.h" -#include "ChiSquaredModule.h" -#include "TROOT.h" - -void FittingHelper::ObserveAndDraw::update(IObservable *subject) -{ - FitSuite *fitSuite = dynamic_cast<FitSuite *>(subject); - if( !fitSuite ) throw NullPointerException("LocalFitObserver::IObserver() -> Error! Can't cast FitSuite"); - - std::cout << "FitObserver: " << " ncall" << m_ncall << " chi2:" << fitSuite->getChiSquaredModule()->getValue(); - // printing parameter values - for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { - std::cout << " " << (*it)->getName() << " " << (*it)->getValue(); - // std::cout << *(*it); - } - std::cout << std::endl; - - TCanvas *c1 = dynamic_cast<TCanvas *>( gROOT->FindObject(m_canvas_name.c_str()) ); - if(!c1) throw NullPointerException("LocalFitIbserver::update() -> No access to canvas"); - c1->cd(3); - gPad->SetLogz(); - IsGISAXSTools::drawOutputDataInPad(*fitSuite->getChiSquaredModule()->getSimulationData(), "CONT4 Z", "current simulated data"); - c1->cd(4); - IsGISAXSTools::drawOutputDataDifference2D(*fitSuite->getChiSquaredModule()->getSimulationData(), *fitSuite->getChiSquaredModule()->getRealData(), "CONT4 Z", "difference"); - c1->cd(5); - - TPaveText *pt = new TPaveText(.05,.1,.95,.8); - char str[256]; - sprintf(str,"Iteration %d",m_ncall); - pt->AddText(str); - sprintf(str,"chi2 %e",fitSuite->getChiSquaredModule()->getValue()); - pt->AddText(str); - //pt->AddLine(.0,.5,1.,.5); - for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { - sprintf(str,"%s %f", (*it)->getName().c_str(), (*it)->getValue()); - pt->AddText(str); - } - pt->Draw(); - - m_ncall++; -} - - diff --git a/App/src/TestFittingModule.cpp b/App/src/TestFittingModule.cpp index 5068d977157..e19dd3e2dbc 100644 --- a/App/src/TestFittingModule.cpp +++ b/App/src/TestFittingModule.cpp @@ -12,7 +12,7 @@ #include "FormFactors.h" #include "Exceptions.h" #include "DrawHelper.h" -#include "FittingHelper.h" +#include "FitSuiteHelper.h" #include "IObserver.h" #include "FitSuite.h" @@ -80,10 +80,14 @@ void TestFittingModule::execute() fitSuite->addFitParameter("/MultiLayer/Layer0/thickness", 12*Units::nanometer, 1*Units::nanometer, TRange<double>(1.0, 20.0) ); fitSuite->addFitParameter("*/FormFactorCylinder/radius", 2*Units::nanometer, 1*Units::nanometer, TRange<double>(1.0, 20.0) ); - FittingHelper::ObserveAndDraw *observer = new FittingHelper::ObserveAndDraw(canvas_name); - fitSuite->attachObserver(observer); + FitSuiteObserverDraw *drawObserver = new FitSuiteObserverDraw(canvas_name); + fitSuite->attachObserver(drawObserver); + FitSuiteObserverWriteTree *writeObserver = new FitSuiteObserverWriteTree("fitsuite.root"); + fitSuite->attachObserver(writeObserver); fitSuite->runFit(); + delete drawObserver; + delete writeObserver; std::cout << "------ RESULTS ---------" << std::endl; std::cout << "FitSuite > MinValue:" << fitSuite->getMinimizer()->getMinValue() << " " << fitSuite->getMinimizer()->getValueOfVariableAtMinimum(0) << std::endl; diff --git a/App/src/TestMesoCrystal2.cpp b/App/src/TestMesoCrystal2.cpp index 3e94b822e9f..af309a4ad91 100644 --- a/App/src/TestMesoCrystal2.cpp +++ b/App/src/TestMesoCrystal2.cpp @@ -24,7 +24,7 @@ #include "SampleFactory.h" #include "FitMultiParameter.h" #include "TRange.h" -#include "FittingHelper.h" +#include "FitSuiteHelper.h" #include "TCanvas.h" #include "TH2D.h" @@ -83,9 +83,9 @@ void TestMesoCrystal2::execute() fitSuite->setMinimizer( new ROOTMinimizer("Minuit2", "Migrad") ); fitSuite->addFitParameter("/MultiLayer/LayerInterface1/roughness/sigma", 0.1, 0.05, TRange<double>(0.0, 10.0) ); - FittingHelper::ObserveAndDraw *observer = new FittingHelper::ObserveAndDraw(canvas_name); - fitSuite->attachObserver(observer); - + FitSuiteObserverDraw *drawObserver = new FitSuiteObserverDraw(canvas_name); + fitSuite->attachObserver(drawObserver); +\ fitSuite->runFit(); for(FitSuite::fitparameters_t::iterator it = fitSuite->fitparams_begin(); it!=fitSuite->fitparams_end(); ++it) { diff --git a/App/src/TestRootTree.cpp b/App/src/TestRootTree.cpp index a2b7ddea02c..1b0c907d2cb 100644 --- a/App/src/TestRootTree.cpp +++ b/App/src/TestRootTree.cpp @@ -16,7 +16,7 @@ #include "TRandom.h" #include "TCanvas.h" #include "IsGISAXSTools.h" -#include "EventFrame.h" +#include "TreeEventStructure.h" #include "TestMesoCrystal1.h" #include <vector> @@ -71,7 +71,7 @@ void TestRootTree::complex_write() // creating new tree TTree *tree = new TTree(tree_name.c_str(),"Oh, my data"); - EventFrame *event = new EventFrame(); + TreeEventOutputData *event = new TreeEventOutputData(); tree->Branch("Event",&event,16000,2); // preparing set of meso parameters to simulate @@ -181,7 +181,8 @@ void TestRootTree::complex_read() throw RuntimeErrorException("TestRootTree::complex_read() -> Can't get tree with name '" + tree_name + "' from root file"); } - EventFrame *event = 0; + TreeEventOutputData *event = 0; + tree->SetBranchAddress("Event", &event); // reading data from the tree diff --git a/App/src/TreeEventStructure.cpp b/App/src/TreeEventStructure.cpp new file mode 100644 index 00000000000..15fcebc5e99 --- /dev/null +++ b/App/src/TreeEventStructure.cpp @@ -0,0 +1,57 @@ +#include "TreeEventStructure.h" + + +/* ************************************************************************* */ +// Output data +/* ************************************************************************* */ +TreeEventOutputData::TreeEventOutputData() +{ + clear(); +} + + +void TreeEventOutputData::clear() +{ + nframe = 0; + alpha_i = 0; + phi_i = 0; + + nphi_f = 0; + phi_f_min = 0; + phi_f_max = 0; + + nalpha_f = 0; + alpha_f_min = 0; + alpha_f_max = 0; + + malpha = 0; + mphi = 0; + npR = 0; + + valpha_f.clear(); + vphi_f.clear(); + vi.clear(); +} + + +/* ************************************************************************* */ +// Fit data +/* ************************************************************************* */ +TreeEventFitData::TreeEventFitData() +{ + clear(); +} + +void TreeEventFitData::clear() +{ + niter = 0; + real_data.clear(); + fit_data.clear(); + diff.clear(); + axis0.clear(); + axis1.clear(); + chi2=0; + parvalues.clear(); + parnames.clear(); +} + diff --git a/App/src/main.cpp b/App/src/main.cpp index f7edbe472b3..3f253b35fdf 100644 --- a/App/src/main.cpp +++ b/App/src/main.cpp @@ -1,16 +1,18 @@ #include "FunctionalTestFactory.h" #include "DrawHelper.h" -#include "CommandLine.h" -#include "Utils.h" +//#include "CommandLine.h" +//#include "Utils.h" #include <iostream> #include <string> #include "TROOT.h" #include "TApplication.h" #include "ProgramOptions.h" -#include "AppOptionsDescription.h" -#include "Macros.h" -#include "GISASExperiment.h" +//#include "AppOptionsDescription.h" +//#include "Macros.h" +//#include "GISASExperiment.h" + + int main(int argc, char **argv) { diff --git a/Core/Core.pro b/Core/Core.pro index 3d2da509071..d42095ce000 100644 --- a/Core/Core.pro +++ b/Core/Core.pro @@ -110,7 +110,8 @@ SOURCES += \ PythonAPI/src/PythonListConverter.cpp \ PythonAPI/src/PythonModule.cpp \ PythonAPI/src/PythonPlusplusHelper.cpp \ - PythonAPI/src/PythonOutputData.cpp + PythonAPI/src/PythonOutputData.cpp \ + Tools/src/IObserver.cpp HEADERS += \ Algorithms/inc/Beam.h \ diff --git a/Core/Tools/inc/IObserver.h b/Core/Tools/inc/IObserver.h index 509ee6849ee..5d4abff69a5 100644 --- a/Core/Tools/inc/IObserver.h +++ b/Core/Tools/inc/IObserver.h @@ -25,11 +25,16 @@ class IObservable; //- ------------------------------------------------------------------- class IObserver { public: - virtual ~IObserver(){} - //! method which is used by observable object to notify change in status + //! destructor detach observer from observed subject + virtual ~IObserver(); + //! method which is used by observable subject to notify change in status virtual void update (IObservable *subject) = 0; + //! set pointer to observed subject + virtual void setObservedSubject(IObservable *subject); protected: - IObserver(){} + IObserver() : m_observed_subject(0) {} +private: + IObservable *m_observed_subject; }; @@ -44,18 +49,13 @@ public: virtual ~IObservable(){} //! attach observer to the list of observers - virtual void attachObserver(IObserver *obj) { m_observers.push_back(obj); } + virtual void attachObserver(IObserver *obj); //! detach observer from observers list - virtual void detachObserver(IObserver *obj) { m_observers.remove(obj); } + virtual void detachObserver(IObserver *obj); //! notify observers about change in status - virtual void notifyObservers() - { - for(observers_t::iterator it = m_observers.begin(); it!=m_observers.end(); ++it) { - (*it)->update(this); - } - } + virtual void notifyObservers(); protected: IObservable(){} diff --git a/Core/Tools/src/IObserver.cpp b/Core/Tools/src/IObserver.cpp new file mode 100644 index 00000000000..42c874f96cd --- /dev/null +++ b/Core/Tools/src/IObserver.cpp @@ -0,0 +1,43 @@ +#include "IObserver.h" + + + +/* ************************************************************************* */ +// Observer +/* ************************************************************************* */ +IObserver::~IObserver() +{ + if(m_observed_subject) m_observed_subject->detachObserver(this); +} + + +void IObserver::setObservedSubject(IObservable *subject) +{ + m_observed_subject = subject; +} + + + +/* ************************************************************************* */ +// Observable +/* ************************************************************************* */ +void IObservable::attachObserver(IObserver *obj) +{ + obj->setObservedSubject(this); + m_observers.push_back(obj); +} + + +void IObservable::detachObserver(IObserver *obj) +{ + m_observers.remove(obj); +} + + +void IObservable::notifyObservers() +{ + for(observers_t::iterator it = m_observers.begin(); it!=m_observers.end(); ++it) { + (*it)->update(this); + } +} + diff --git a/Examples/Analysis/RootMacros/README b/Examples/Analysis/RootMacros/README new file mode 100644 index 00000000000..d5c6dc996c4 --- /dev/null +++ b/Examples/Analysis/RootMacros/README @@ -0,0 +1,9 @@ +Directory contains exmpales with ROOT analysis macros + +ex01_MesoGif - analysis of the data produced by TestRootTree +ex02_ReadFitData - analysis of the data produced by TestFittingModule + + + +To clean remainings from ROOT session +rm -f */*.so */*.d */*~ diff --git a/Examples/Analysis/MesoData/MesoData.C b/Examples/Analysis/RootMacros/ex01_MesoGif/MesoData.C similarity index 100% rename from Examples/Analysis/MesoData/MesoData.C rename to Examples/Analysis/RootMacros/ex01_MesoGif/MesoData.C diff --git a/Examples/Analysis/MesoData/MesoData.h b/Examples/Analysis/RootMacros/ex01_MesoGif/MesoData.h similarity index 100% rename from Examples/Analysis/MesoData/MesoData.h rename to Examples/Analysis/RootMacros/ex01_MesoGif/MesoData.h diff --git a/Examples/Analysis/MesoData/analysis1.C b/Examples/Analysis/RootMacros/ex01_MesoGif/analysis1.C similarity index 100% rename from Examples/Analysis/MesoData/analysis1.C rename to Examples/Analysis/RootMacros/ex01_MesoGif/analysis1.C diff --git a/Examples/Analysis/MesoData/analysis2.C b/Examples/Analysis/RootMacros/ex01_MesoGif/analysis2.C similarity index 100% rename from Examples/Analysis/MesoData/analysis2.C rename to Examples/Analysis/RootMacros/ex01_MesoGif/analysis2.C diff --git a/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.C b/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.C new file mode 100644 index 00000000000..84e6e5e8fbb --- /dev/null +++ b/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.C @@ -0,0 +1,43 @@ +#define FitData_cxx +#include "FitData.h" +#include <TH2.h> +#include <TStyle.h> +#include <TCanvas.h> + +void FitData::Loop() +{ +// In a ROOT session, you can do: +// Root > .L FitData.C +// Root > FitData t +// Root > t.GetEntry(12); // Fill t data members with entry number 12 +// Root > t.Show(); // Show values of entry 12 +// Root > t.Show(16); // Read and show values of entry 16 +// Root > t.Loop(); // Loop on all entries +// + +// This is the loop skeleton where: +// jentry is the global entry number in the chain +// ientry is the entry number in the current Tree +// Note that the argument to GetEntry must be: +// jentry for TChain::GetEntry +// ientry for TTree::GetEntry and TBranch::GetEntry +// +// To read only selected branches, Insert statements like: +// METHOD1: +// fChain->SetBranchStatus("*",0); // disable all branches +// fChain->SetBranchStatus("branchname",1); // activate branchname +// METHOD2: replace line +// fChain->GetEntry(jentry); //read all branches +//by b_branchname->GetEntry(ientry); //read only this branch + if (fChain == 0) return; + + Long64_t nentries = fChain->GetEntriesFast(); + + Long64_t nbytes = 0, nb = 0; + for (Long64_t jentry=0; jentry<nentries;jentry++) { + Long64_t ientry = LoadTree(jentry); + if (ientry < 0) break; + nb = fChain->GetEntry(jentry); nbytes += nb; + // if (Cut(ientry) < 0) continue; + } +} diff --git a/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.h b/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.h new file mode 100644 index 00000000000..f8875b1b44c --- /dev/null +++ b/Examples/Analysis/RootMacros/ex02_ReadFitData/FitData.h @@ -0,0 +1,154 @@ +////////////////////////////////////////////////////////// +// This class has been automatically generated on +// Wed Oct 10 15:14:35 2012 by ROOT version 5.34/01 +// from TTree FitSuiteTree/Oh, my data +// found on file: ../../../../fitsuite.root +////////////////////////////////////////////////////////// + +#ifndef FitData_h +#define FitData_h + +#include <TROOT.h> +#include <TChain.h> +#include <TFile.h> + +// Header file for the classes stored in the TTree if any. + +// Fixed size dimensions of array or collections stored in the TTree if any. + +class FitData { +public : + TTree *fChain; //!pointer to the analyzed TTree or TChain + Int_t fCurrent; //!current Tree number in a TChain + + // Declaration of leaf types + //TreeEventFitData *Event; + Int_t niter; + vector<vector<double> > real_data; + vector<vector<double> > fit_data; + vector<vector<double> > diff; + vector<vector<double> > axis0; + vector<vector<double> > axis1; + Double_t chi2; + vector<double> params; + vector<string> parnames; + + // List of branches + TBranch *b_Event_niter; //! + TBranch *b_Event_real_data; //! + TBranch *b_Event_fit_data; //! + TBranch *b_Event_diff; //! + TBranch *b_Event_axis0; //! + TBranch *b_Event_axis1; //! + TBranch *b_Event_chi2; //! + TBranch *b_Event_params; //! + TBranch *b_Event_parnames; //! + + FitData(TTree *tree=0); + virtual ~FitData(); + virtual Int_t Cut(Long64_t entry); + virtual Int_t GetEntry(Long64_t entry); + virtual Long64_t LoadTree(Long64_t entry); + virtual void Init(TTree *tree); + virtual void Loop(); + virtual Bool_t Notify(); + virtual void Show(Long64_t entry = -1); +}; + +#endif + +#ifdef FitData_cxx +FitData::FitData(TTree *tree) : fChain(0) +{ +// if parameter tree is not specified (or zero), connect the file +// used to generate this class and read the Tree. + if (tree == 0) { + TFile *f = (TFile*)gROOT->GetListOfFiles()->FindObject("../../../../fitsuite.root"); + if (!f || !f->IsOpen()) { + f = new TFile("../../../../fitsuite.root"); + } + f->GetObject("FitSuiteTree",tree); + + } + Init(tree); +} + +FitData::~FitData() +{ + if (!fChain) return; + delete fChain->GetCurrentFile(); +} + +Int_t FitData::GetEntry(Long64_t entry) +{ +// Read contents of entry. + if (!fChain) return 0; + return fChain->GetEntry(entry); +} +Long64_t FitData::LoadTree(Long64_t entry) +{ +// Set the environment to read one entry + if (!fChain) return -5; + Long64_t centry = fChain->LoadTree(entry); + if (centry < 0) return centry; + if (fChain->GetTreeNumber() != fCurrent) { + fCurrent = fChain->GetTreeNumber(); + Notify(); + } + return centry; +} + +void FitData::Init(TTree *tree) +{ + // The Init() function is called when the selector needs to initialize + // a new tree or chain. Typically here the branch addresses and branch + // pointers of the tree will be set. + // It is normally not necessary to make changes to the generated + // code, but the routine can be extended by the user if needed. + // Init() will be called many times when running on PROOF + // (once per file to be processed). + + // Set branch addresses and branch pointers + if (!tree) return; + fChain = tree; + fCurrent = -1; + fChain->SetMakeClass(1); + + fChain->SetBranchAddress("niter", &niter, &b_Event_niter); + fChain->SetBranchAddress("real_data", &real_data, &b_Event_real_data); + fChain->SetBranchAddress("fit_data", &fit_data, &b_Event_fit_data); + fChain->SetBranchAddress("diff", &diff, &b_Event_diff); + fChain->SetBranchAddress("axis0", &axis0, &b_Event_axis0); + fChain->SetBranchAddress("axis1", &axis1, &b_Event_axis1); + fChain->SetBranchAddress("chi2", &chi2, &b_Event_chi2); + fChain->SetBranchAddress("params", ¶ms, &b_Event_params); + fChain->SetBranchAddress("parnames", &parnames, &b_Event_parnames); + Notify(); +} + +Bool_t FitData::Notify() +{ + // The Notify() function is called when a new file is opened. This + // can be either for a new TTree in a TChain or when when a new TTree + // is started when using PROOF. It is normally not necessary to make changes + // to the generated code, but the routine can be extended by the + // user if needed. The return value is currently not used. + + return kTRUE; +} + +void FitData::Show(Long64_t entry) +{ +// Print contents of entry. +// If entry is not specified, print current entry + if (!fChain) return; + fChain->Show(entry); +} +Int_t FitData::Cut(Long64_t entry) +{ +// This function may be called from Loop. +// returns 1 if entry is accepted. +// returns -1 otherwise. + return 1; +} +#endif // #ifdef FitData_cxx diff --git a/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata1.C b/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata1.C new file mode 100644 index 00000000000..ac1c306b6dc --- /dev/null +++ b/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata1.C @@ -0,0 +1,61 @@ +// Simple drawing of fit data stored in the FitSuiteTree using TTree::Draw() method +// (data produced by FitSuiteObserverWriteTree un TestFittingModule) +// Usage: +// +// Way #1 (script mode) +// > root -l analyse_fitdata1.C +// +// Way #2 (precompiled mode) +// > root -l +// > .L analyse_fitdata1.C++ +// analyse_fitdata1() + +#include <iostream> +#include <string> +#include <vector> +#include "TFile.h" +#include "TTree.h" +#include "TCanvas.h" + +#include "FitData.h" + + +void analyse_fitdata1() +{ + const char *file_name ="../../../../fitsuite.root"; + const char *tree_name = "FitSuiteTree"; + + // opening root file + TFile *top = new TFile(file_name,"READ"); + if( !top->IsOpen() ) { + std::cout << "Can't open file '" << file_name << "'" << std::endl; + return; + } + + // access tree stored in the file + TTree *tree = (TTree *) top->Get(tree_name); + if( !tree ) { + std::cout << "Can't get tree '" << tree_name << "'" << std::endl; + return; + } + + TCanvas *c1 = new TCanvas("c1","c1",1024, 768); + c1->Divide(2,2); + + // loop over tree entries and drawing + for(int i=0; i<tree->GetEntries(); i++){ + // defining criteria for Draw method -> it will draw only data corresponding to the event with given number + char criteria[128]; + sprintf(criteria,"Entry$==%d",i); + c1->cd(1); gPad->SetLogz(); + tree->Draw("real_data:axis1:axis0",criteria,"prof COLZ"); + c1->cd(2); gPad->SetLogz(); + tree->Draw("fit_data:axis1:axis0",criteria,"prof COLZ"); + c1->Update(); + } + + + +} + + diff --git a/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata2.C b/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata2.C new file mode 100644 index 00000000000..14561630356 --- /dev/null +++ b/Examples/Analysis/RootMacros/ex02_ReadFitData/analyse_fitdata2.C @@ -0,0 +1,103 @@ +// Simple drawing of fit data stored in the FitSuiteTree (using FitData class) +// (data produced by FitSuiteObserverWriteTree un TestFittingModule) +// Usage: +// +// Way #1 (script mode) +// > root -l analyse_fitdata1.C +// +// Way #2 (precompiled mode) +// > root -l +// > .L analyse_fitdata1.C++ +// analyse_fitdata1() + +#include <iostream> +#include <string> +#include <vector> +#include "TFile.h" +#include "TChain.h" +#include "TCanvas.h" +#include "TH2D.h" +#include "TSystem.h" +#include "TPaveText.h" + +using namespace std; +#include "FitData.C" + + +void analyse_fitdata2() +{ + const char *file_name ="../../../../fitsuite.root"; + const char *tree_name = "FitSuiteTree"; + + TChain *chain = new TChain(tree_name); + chain->Add(file_name); + + FitData *event = new FitData(chain); + + // reading first event + chain->GetEntry(0); + + // finding histogram limits + std::vector<double> axis0; + for(size_t i=0; i<event->axis0.size(); ++i) axis0.push_back(event->axis0[i][0]); + std::vector<double> axis1 = event->axis1[0]; + double dphi = (axis0.back()-axis0.front())/double(axis0.size()-1); + double phi1 = axis0.front() - dphi/2.; + double phi2 = axis0.back() + dphi/2.; + double dalpha = (axis1.back()-axis1.front())/double(axis1.size()-1); + double alpha1 = axis1.front() - dalpha/2.; + double alpha2 = axis1.back() + dalpha/2.; + std::cout << "dphi: " << dphi << " phi1:" << phi1 << " phi2:" << phi2 << std::endl; + std::cout << "dalpha: " << dalpha << " alpha1:" << alpha1 << " alpha2:" << alpha2 << std::endl; + + TH2D *myHisto = new TH2D("myHisto","myHisto", axis0.size(), phi1, phi2, axis0.size(), alpha1, alpha2); + myHisto->SetContour(50); + myHisto->SetTitle(""); + myHisto->SetMinimum(1); + myHisto->SetStats(0); + + TCanvas *c1 = new TCanvas("c1","c1",1024, 768); + c1->Divide(2,2); + // loop over entries + int i_entry(0); + while(chain->GetEntry(i_entry++)){ + for(size_t i=0; i<axis0.size(); i++){ + for(size_t j=0; j<axis1.size(); j++) { + myHisto->Fill(event->axis0[i][j], event->axis1[i][j], event->real_data[i][j]); + } + } + c1->cd(1); gPad->SetLogz(); + myHisto->DrawCopy("colz"); + myHisto->Reset(); + for(size_t i=0; i<axis0.size(); i++){ + for(size_t j=0; j<axis1.size(); j++) { + myHisto->Fill(event->axis0[i][j], event->axis1[i][j], event->fit_data[i][j]); + } + } + c1->cd(2); gPad->SetLogz(); + myHisto->DrawCopy("colz"); + myHisto->Reset(); + + // printing parameters + c1->cd(3); + TPaveText *pt = new TPaveText(.05,.1,.95,.8); + char str[256]; + sprintf(str,"Iteration %d",event->niter); + pt->AddText(str); + sprintf(str,"chi2 %e",event->chi2); + pt->AddText(str); + for(size_t i =0; i<event->params.size(); ++i) { + sprintf(str,"%s %f", event->parnames[i].c_str(), event->params[i]); + pt->AddText(str); + } + pt->Draw(); + + c1->Update(); + gSystem->Sleep(5); // wait a bit for smooth animation + } // loop over recorder events + + + +} + + diff --git a/Examples/Analysis/RootMacros/ex02_ReadFitData/make_class.C b/Examples/Analysis/RootMacros/ex02_ReadFitData/make_class.C new file mode 100644 index 00000000000..13349119275 --- /dev/null +++ b/Examples/Analysis/RootMacros/ex02_ReadFitData/make_class.C @@ -0,0 +1,37 @@ +// Generates classes to read ROOT tree from existing ROOT file +// +// Usage: +// Way 1 (script mode): +// > root -l -q .x make_class.C +// Way 2 (precompiled mode): +// > root -l +// > .L make_class.C++ +// > make_class() + + +#include "TFile.h" +#include "TTree.h" +#include <iostream> + +void make_class(const char *file_name ="../../../../fitsuite.root" + ,const char *tree_name = "FitSuiteTree" + ,const char *class_name = "FitData") +{ + + // open root file + TFile *top = new TFile(file_name,"READ"); + + // access to the tree stored in the file + if( !top->IsOpen() ) { + std::cout << "File is absent " << file_name << std::endl; + return; + } + TTree *tree = (TTree *)top->Get(tree_name); + if( !tree ) { + std::cout << "Tree is absent " << tree_name << std::endl; + return; + } + + // generating class files (*.h, *.C) + tree->MakeClass(class_name); +} diff --git a/Examples/Performance/perf_history.txt b/Examples/Performance/perf_history.txt index 78c703f8950..721c463396b 100644 --- a/Examples/Performance/perf_history.txt +++ b/Examples/Performance/perf_history.txt @@ -120,3 +120,7 @@ # current status of the art (not possible to trace what was changed) 2012-10-08 17:36:40 | jcnsopc73 | macosx64, 2800 MHz | 270270 | 13.986 | 13.8889 | 4.7619 | 2012-10-08 17:37:28 | jcnsopc73 | macosx64, 2800 MHz | 266667 | 13.986 | 13.986 | 4.7619 | +2012-10-09 14:00:42 | jcnsopc73 | macosx64, 2800 MHz | 266667 | 14.1844 | 13.986 | 4.7619 | + + + diff --git a/Macros/GitUtils/gisasfw_loc.png b/Macros/GitUtils/gisasfw_loc.png index 5cccb7fa5df8baea7f69757132fd19cfd486030a..59831b72b9d474f8d4c73429b2be3d0e5acf99db 100644 GIT binary patch literal 15387 zcmcJ02{_c<+y7Xiq9`I{DTT@wlAX$uh$LHKlzrcq3}Z{mzGTZ*)*|cJ_sG6X$TG%| zEMx2j!`SBkMS6Ok_kG^qdtLAKcU`!Q`JQv1``qVqf9}tH=FLMDg;OUPPl7<8Q;PRw zAAvx`kAZKA<HW!#<$9@#z%R1rN(!>TZ{Q1mx5^6yVgo74-hS+sxPXG^jWkDZuFSGx zspE6g#Ur0n{&+!*JC?5fh|a~Rk?5y2HF=>igq}gOVX>YB;u3F9+$rIE>T(n}N81k3 zyIoS4gH>bT;0ua9x|TBC>`MqMVT451=B^C28{#q_Ra;)8`i`TPvz|Bz6f;TassW5j zWNM=V6^LAx2G#!z-vNPw?qW$m+^7HLMKA<A*%rmAI;|h1pDghLi|yd6+rf5N)9lPB zNN@FoL1{xPya;=1b2lpn^&tx-gZkUcjZ~((<ab*XuZ1;&(zI9H1z-1DOeOYtNwm5L z22io?;mYc)6PjDFu3j6iUfamAWUoCuVY`kX4!<9|*hnSqv4L{k4HI1CxZd){agn}m z)8BD1wr(e5$BnSJD^0+|yIb20EymtKj(M|tpvA``e1K8K&^BYWl5?|(VQ>%WopCC( z9?f<A7gNz=z;w(g5^a%4R&M+}46Y0LGB}M#r?__SVpP4}S(J49M^4dtZS|$h;9e?V z{UlrHJQ&g?gMHbFSlVn=Y-{n;eOangVqa?@8_80hvx!yq6R(0-SR~!o)-Zicw`@Gt zdK6F-tP*ZN61OK<37a^iEny|hYmJ>S1>S3-^1@HWc2@)iQApq3cVyi*YRPDGyfI*@ zjsTr7f~s{_Y;P<P=IZt=H!)R9wcCJcW0lTxepFK7y3TV0xrEIy!ptZeU(H%CI>l=- zpix{L*oRBTDUOS-z+k2+o-W(Cg;M9BkvcDeE#S_wx?Srr9IDE7X={D14&P-v>NXKV z*vcnFM(|ZHH`qc~6P$1<JCmGX&-H#VVM_}=vOYJMuUCb$CE#pB>f~_m9T%HJn0Ao} z*U=YfV87S4r+)tYsa3ZXO;`gqIOr8&oON$}Wuk?dVt=c=2-{;Fc1RPiolSd@5Z2H& ziSvn$W80$}J1chf_A4m_d<wJWOyc$wpBEmJl_Iq<;^<(Ux1DBKws1Ya8Vl@fljsKU zb1GU(AK&d-%dWMzx57&35k>RuLN}_$oV#~7ya;8K+j`uF!}9Wrqcz+8THv<jByrch z-7!K91od7n1g~2MMTu@SGuftZPlenXU5)}g02g(gXbv&MIaPNi;1&P})!EG6porx# zoP6?R2DgChS@K+)>G@)a$%xf==x!s7PPBv}g`40|9Bk1zvtPgRxf=Ig%f3^)D*)%~ zLwA6X0U`jpTWLl326!z^4pjrWrI%wlnCIVDjh{{kxlWi)ur1B~;A(Fl(d}k$f2Qf| z);N{dP+|SkGMfyT?9*C%Yp};klbISucq2eT1c9`zLzUMr)&sktfgW*$OU6Pk!)(it zp(P8Wr9EFAKDKUL(Y=9RSsw(4WybD8_*2DQ7t{M>Tgbj&!gpP@^0<QZfAdY%#R?l0 zVOz7VsW*7bX5@HXyZ|=0Y&4Sf%2oN*t#s@tWJ%6&mTeVPZa>kJrXbKvpp({#!`YxJ z*L#)tP?gR(*5aF;uB+2sX3FKtby8HUQXWe+8)Cv%LWYJ|n*G301<pui9@uUCm~=CI zMsN)9uW_&~SEF#ZEwwZUiY>%r8nXPuCwS$}S9Bef)aH-<5RMS=;J2eU5>KJ%GY7ny z(iTS#_PD{QP96`po#m&`tXl&jYoTl`^ylp9I{SAEE=-}UrPf}1t0^7BqqUyfXlX)4 zu$2<D=oZ*(cU??$Fd`yE9A#Py_@FfoIBYE#qlZD0w>VRFQdb0e>AhMw>de}k$&|7i zokl+<V)2>VTHkN?T$SFM98_D7Tg{AC5R<Y9t)idt0tSu5Ng$Y?t<aZxGj}Wvy<t8U z2zQh+bqbaq8X0t6*uKLwI0*zswx0z3gQ!s3k?&Fj{EbL-!=sxQmZIF#(8-W)(T&-h ze3#%0rPgzmG20SgDBf5)lLs$XF*{shiQ!vUyzy?(wAO4V%xh;N)Y*?rVp2*P>=-PU zxAVdw^Fp+DuP}a&OaJ=<es6n82||w&Sj=B^i+s=*bfeb7d?I0fttjfdm)B+^Yh^6I zwv#{Ku&l%FR?%4f@hDu6DqHHP3Eo#O*8J|?C9s=6Ty!G`yn7#~f|NrD7ZIi9nV?62 zc+SAOXfgF=RhsF$mWUg<W@%~k>D#}#BsQsQhp#a7zXJE%!eou#!>>$)NuO3=sj?1> z#6hca^hR)L`Ns%oue~gLd9QQS7WR!n@`L$c!W@|J&LlH=XMyKKv!&9fwa{0{GFvYk z!qfrwW{K|sQ`MIpX5LR!(mTuh2ft-Yj$DzQ>z2q%61B+H9a11R!<k|=bNWM=ZY`p! z2D9{VZG5{kKYuoi5%;B7wXMAayYKb5=`BxpB>>07pIg?&)xB$JcVmo@<+a60m=Is8 zW+wssJhf4~QIj?>AiA;A!s>-><9vqQ{z3))kr$czx^zL!_3S$Bl~MrKSe5mlzRgMv zp_8UWZMIOgFCXIJEs{*{o8?P;x)N@Y=k+}dL2rlj!nRvu!O$1>_7|DZn7*vKz4d&r z-8t|E-1k$m={3KQJ>O!(pvOxU=;ZCSK2@(}Kh}GnHrhB-M9`D^=g(Lpvs6=9oL}8I zlY-pa2P+8iM>%XGwe>BJX*3WD)}|X5KlSeC3?QIIJAJz=7?3kC^Q!UH_gY}j67CLF zDf}d`0l1}$K`hXRRn`C;54bgQDzo9oy$i@Y=|A<TduEjb`G9qAIe@Qr^V06eZSzu? zxkq+>gl!P3r2FMu$F)|I>_S^un!?)qW}C?+0G6?x89SSsD-*?=6t1_222l!5h@v|1 z&QE${oyr{3?GczU*sQ)Zsk_8-uyzZG<l3#ktgZQ<ujmDPmAutG@O|{SfHpW<!fOQ% z9PKX_C5+$nw>3o`I5WqRz-3O>Ht*#CF3#7yUO~4x(~~B&9457OZ;@xd-0nI|h(j79 zC*fjvpVP6jr<Sn0MA+M?i+%be9a6Cn;ZjG9K$&BMPE)0<_EJEXs#m)$O11zBu<e(_ zgIMfSm5!EzFKf~xO2>0LlN?z+*CHgeMK@D7F{vFdFNz9pt<f3iUzR5Es5$2%tg;r< zu<SxcUwv<Q>^S>%8s}Rz>jO_6&e>@fUgB|Zvh4s)!PLOlQuJ8XG)3*q><=Js>b1Sb zwo@T!Ra2xwDt&ji-G^hA#(d5iA5Xb=y7uywOB*ksdwBHtn3o-V^w>3m*Gdz8&!<<H z;}UM!wpY9!L+yd6d&b9otHgG@`0gxL&N@oG)Y`uEtb{%^wy#!-zGF&~FKzW0_-z~j zTh5}wVnyu5aZ6VzJXT-DHm{8L4M4rxPT4|-Zc66R^E}|7NHNX#Lyz=<#}gr&%g@}@ zA!`L%<pLW^V^o7Js_Y?FEq)7U>N<hRYd3!e?d{PMh9DvcE;v4e&v69~pwf7iG>{e{ z8;g2lAMq`tb3cd#D$X<#+?Kb`>0bl3?zS71_1ZERZkyxP{iRO3o-mWO0vBz~Jk1I@ zPCv!Eq8t`rc0XmDmzS4C!i}1v+V48|&O%a;px62dq_Bqw`UjJK14D*c5X;`Gf-ma? ztGR18s=^W7og}_DbdPn<@6P2q(REd*Gd3><FhUUxFK^?s!tTU5b@fFp&J=DKMuGJP zF{e_j!)j7^@cquaTXXqYrYp~;X`ZExl;0k%-o+u+UeMNF%iu{pZN5|}G0Jj3Wi?1e z;45UQhF)?Zymn0{J*+l8tj6{A=<HeZky1~6X8M{)laWczUPHf5(_>zp1LrY>_$`iG z6~&IrB|TDpp>BkrV$|whAy!@*-=;e90RWVx5pU+ps8@bV7ZerIrW;CR?lud_Eq+V* z3pY+Ky|os<nZj5>Xrhi}1sT|vk01Nr)8zmAJI5?8Jk{tof!TNG4q7rC&oCUATX(NH zZAu>k>o}X89qD7&Z01{vAOeXrW#`SgeYZFlaw6+f_AC}F@a{d=Nkx<HiY^lv%BE4( zwMs8;JasByRhU=$9W%(ArU{k&$UNFuXV6)1unN^JO0VJEU4epm;IgwVs!()tYGC_r z|2Ad166C^UUlI4lwx{-J+gEEC35ef!O9#`M%GGeACoEzO_@7?*LZag{i(sgsP$9fl zih%@VS6PmYDjbj7g^sNlUfzA2Eq3+P6yYP4IyU1TXZUylm=>^rxg6QI;rog`C{;Uu zFz33O$;qhr!HAuPwSgF&DnVWlNWr`o*(X}2pMPbFR_+YSp<-+)D3z<DC_8%>>h<D< zPRta-rbBtDg$UG{qq3OvIPW?haFe2~z>mb7?ECGt{QbF_ISVexq@Ar;n-Kqrt46V( zHLsJMH}EDR<6yKKDCUq(=}adbu@c5A>$~G~xdL~gd|^{1Jh@)?=b|`_9a)>;4ImOq z{`&fI5;|sRqmo-iUJnCH!(5a&PqMqvC2H&Ave6Q$cBBGjB6#Pz#~D@Cj#?T9LP1sc z!$Zn!UeOd^sgT0=uZiA!EmM4vS_AvK;Y{oNtZ4@Ol&0#7Ufv5fiB~)y^0{OA^lB-R zca&0QmFVO01VIM!bO}>#Ay*Kzau@S$R;WZh1pOQ%Q;dl5H}0z}&Yt6knp=0+Cg{ba zbnBPd8V5bCJL6+^mo0npj9iiy8=axK_Qcw!S~K5|jsnVhV7_8UoEaW7KaTA~@YooG zTH~5b@N-NW>LFC=3N%jOz`10D<@vqoCm?S!x&&iC%aNjJ9-Ccx($rHvA0riLdGlow zzaaJJt#_?L&x2b1noQ#BCQpO36FnkRddPXykKPw5Gi{QuS)vUlk!j`@?#NxjegC{7 z+c=U*4(e<&Dl=WZ%OtF{MElE9fA;VSM2-f}z#u66Mo?E#TRRoVTYWNWCpc#GDsj&% zna6GHCl%NnVOlv)KIap$83HcxxF!t(xwmuT8F*^`ZCd<eC<wInRuwDw#69b{EEBQX zl`yUgKAm0qxfX@BWZuC#RxvNiIDxaQ>;l2ssL~vlO^_h|FLN13MYDoHS*`!)xmG^` z<@C(elu6U#HI*~3g-KKIv#5y>$0yW0>3{Bd3d8^#;zq0Ei9p|?+9t~^Zfv-R`27_u zBlG>)baR%VxKN`M2Bh47_LG5_6XbNb>J&MM!U(qb4lLCMH^U_Tj#Tzzsm~10qu6m4 z8bRwfLBZXeNr<PiL0t|~z;e06OVZQi{_zASd8(=4A5Yv<4ah1uG`aq)S%-)G)Ze$v z)q%}jAf-q0dA_DK^4l~qk+&g+dz;$@0<~N`cfMgnlg#@}4{36!jjHp1vpZ^?dyMe? z=>NB^9!T8D&VG_V2;d+LA*51(url(^&So@?J@Kk*k<%=pCZZy>>eNw;iA#m|-|u;< zcSnBX-dzRjcg<<$sV4$ck2?;Tzz{ZN<D^w6n;QL%#G35vj*gB+M2L+g=sbr-bjeL0 zzy<)rp676PROZc9jxM-#SMYDNF58qv0Rs#zZAd{hG)ho5L8%|}MbHgKbY1!}Q_wo9 z>k{$h*0|I1e*`)c?L9rOUDb0rkaVb~SP|FB4J+Z3j!%U+-f8zHP!#D+%tK?$o~L+v z3dCS6LE4n)+P$opCKJ)T9V06dle)3(XIFbtmFyqwWY7e_?wNBD0-V!37X$iE7?m&d zvVkTaps(u**!{3k9iw0j4KC8UtZbsMfTier`c7O9xf$NyD}I}8nELg#7qa7_l(LS~ zf|UwSqQ=~7^m8>Gh~TmAA?M;Ee|{eDaJuk2@YX55%09VKsZbdS)`{uOK<Le`_hdQI zg!mg8Ia5kg>_eh=6A}9HXL&T^aNpK*OYj1KycHK=+n=h4JU@hNrMETHglM2^N}cUY zL$E|ab0Z>b5F=c_h$ovwu`-q0{Q=W9wKcr2NS3ii6?t%GeWR_LBHy5#WTNTJ@_dwd z-_k3!BV5UIW!q}JS*h@Ia&T8ax*5y+xxYbNS}yLUxI>o5_n1kQCD&u1yH=X&ncv~} z720!?_Y}o9=Kb7z9NjQ}?ifSg9&F>WOx5)a3gy8HNVVwExa%Ke5*Nw0?}U7ktvdwU zLga!=Rgzch2W#JEBMwYEoQ0#K8Id-^fzP+ciKX3T1-*3aB76qI|JG;wwa4JMklqsw z-`Cggs3u6x?<kv=l}8i34_WWjLR!T<T?)7IV3U0Je*2N`2C1K^ZB-6z+eY>7QP7WQ zu<Z@&pN|SIKASPJU~ZE&BpzHOx<*v5nQUoTsMC!OLlArOjP#18{S9W7X!8mi`!1zH zz8>Nk=!|io1fw8ug6HPCge)a`_{%Uv+_Hw|vOzcbqT0f$Bp<L*TVs!Z)ud%MhikGi z&)_7Y_Hq0LMbw?q5FpMFMgUl}8#_uX<&`7VnGc}1@SL2&H0<0x@-&~JLyRt>k8xMJ zz762I6NI?l&Kv<N?lh%ECC^gtLt0=qm)O9R!L<6h)~mr}z|r5&0{q#kjVgXdz*@4f zB;NK2Zu4(SQiD6(zcc5X)EPGp+f?hAmPI=!Nb&<=1xnDB;nA}AmN6zVlAtaDR5FOJ z%LL`C9zTz>$|>qfau+IcJKYZ?(>C_-Gih<R7N!ctFQAf5qq@$J7|4gv<5Qt3m6DL6 zA@06~69)DT=x=@UPo+w9;P3PIz1S-*cW@H26na+3Vx$b4BY~MsND2>A443^f%CI&k zN(#OQER)05jR@pAd9z|_$60dic3GC#LEKzY`>d&;8x_Q%8HvXZF?U<B9YU%1nGATk zt8_+ksI<knNb7T$GLT~gQ;G8yN;{pD2{ED%GFs36a+iZjuN(pgk1`tI%(yAl`K>R# zDfkVlC!j~>7utJLZf@UJABk!Mr%3`k6mO#nx<;uasxDNq=D|z!dz2j_C&vSCcWvhm z)C=_VnK1&scy4o@#4K(3gar4|I|ckpnM5F+AGre6savam%`tkFYWz0menJ68pAKJr zfAVPDVLod&D)d#VWSDsI4aV1pd5W7lW7I+0Dh7ii<rf$|b!vA8_c#E^Q{n7h9k&{@ zoNP<^Qcxd)j+S5`x=Aua@oVq-#S?CVVz~ZFfxSh=1Ja^j!NjwEGSkNSnp3Fc`uWNP z@!B9I!$M@=Br18jR3Q9H?dyV|Uui-yMzH-k8NtORfLH$4e)(7}mj#3aTfLt>;~3+q z5Pu|V+37Bi`P0_HWWBmMni?ett3=1S>*HBiHLiD=l|}mbeQ%6UXlS&_6ffKPHX{HM zBE#b5%^adE+~IsFDU(}?fUzl4;^Yc!P<<F<yaxO+rwtXcod6hApEH>qS$Dq&VSnlt zfJ6uQBf3S)P&OG49lP7=7j?$7kPE_{rri}lMlr)nr9>M8ytNUl!eNt8<98T>+!EA; zwaUn@f@Rc)@P3(8qqxwwk2<p&Q@MI8s@K%7l6sapDt@BhYc~72RyHWCdIMCIKD<is zetVD=(j({Y#cc2HeN@fJj_Q557siWKtfj?}3d~<u0-?XMa^A^ftdwN3wLPIgCs(MS zucejRNy{Oa&yM%U<nq8=Om&}?tf>I!Ht5u4kAi1`(keoo&s_ta$diaJf4r-qvAwHb zl*D@V(?@QlHpkf5*pM=(WS!i}oEHMbJtpQpvbLWFOw6n9N~ifaU8&2?o-~PChQ_3F zwHLiLy_lVyoxk`Rvv<?;{;eKu5qZs)Wf*@wh=iFWL^MAy$??fRI8g}+T?t2Bm&t@} z^&0!&T!|Oyw<xE6Pn|r~`!t@tMic!Vp)1d5x5yj#A5gW@8}26!gL4v%M(*|EA`gb( zpNyvPz8{(S-w`K7jhVnAp7Toncod@gDJ(~IaUPb<vA?jB>6(t#`CX|l0Vm{1%<n<x z)`z8Y=shpn>S45X)o9f)bfc5KZgVCgx1UCe--RBF>71?*w*%@LY`WeWm;Rdh=65}9 z!oUNnSvl3cj5?tmXN<ZhhPcmpk;V43j~VQxOk$GhW=w&&mAUejttLLBs8M7w@hac= z+Y^8rSf?s~*p#g{#VfO!ch)U8X+e5FJfZd16|p*??2E}YAQA)GNBEVd27Zo&DSjFk zptK$ERkH{ltzoeCEq)!WzZc)l!YiGGVvuM82{~5R9Lp*k;-*p^B+n-}tMa?yO4TW& zvREDf+*OxEF@H7wwZI_inU)hW`y8$m3g9SzS|%Fr?E~Q0en$k<+57}@EcjL3WFBDs zL80&${q`0f8GUmGV8w?_wap0ov?LBv)BKw>95=E##JKmTP|4>ber1gMt5`+opETz; zpZ6Aaf;^Y~A2{5vDq;BB!XNYgD?>KBTgEB>3rRrlv;M?!fYBTn*I#<dnwQ!@{$O4p z<LzB>b$rSI`VDD+r`-RG0r$!A(P~9g_A8U*e_idh+QOd+FVz5Sd>}P>+TeTwdZvE! z5FOjt!ux0Ey!7>+@@xwXG5a2YolIIYlSp4a1(-HC)CMkiqb%A7rjx6y7q#k5a`@y8 z`g_H-wE=y)Y#|3l<F7j42W4*}d1Ao+7L;*%A6MlADSSA{+075xQ1S4GY|MYY+rXka zheq%hLV~`j|AX0+3fzK}WH)Uw#?iV~8R#Ewn8Enxn^N_O|F+ut^zwe1Rba>jkJg&Q z;!ghAYB5t>e47ot^35T<#T`$FSERg=Io<w81?PkLhJJ$p!t8-26!fyo(Z&FZzbu`W z;TkAY+{1bg?r5{?vlgL1`&Si7^8rr72)H%jI)E|<!pEqUu!cqkjg>G{{QH%@>}(_7 zE|b3Uokheh>Ao;w7r9p2=Jc+JTbzR{@X}5(cZZoT%4X0Dhq7_C!!=h)BAXNLvJHPc z^|d0Ou%4`M&Zf}D9w?SC!`*x~-yVR4(jb9w<!`vO^gR9)Ipmdh`U1vVbHmWjf@04B zxgr|xjRDM=xTz8qGAWpn())zx0(%9|&g0$$xJw9ZTPO8R-<Upfq`jW@9`s?ubJzv& zDnKaBThch`#{mK_r#8O<VZf2}a64G5?t$|Md*-1cNiwgHyr*6TkJ`TliZmxO0fP)D zGKbGcqC%S|*ZBE1ee*jOG`|p>rz<Jd+T&xXOTHQtagV$sn?mlpX_>8#16PYLCxZ6@ zyp}64^yFtRdorM;*K5n)uWpoX$v>Y{uYA+oaHB}<Rf@llAXV$~e2)DVi2N*x$89mP zov_m4eOP9X&o@;Rf6rH}250Hs+u^26eDS`vk}}W8lZ=W>B0N#OkdZP`AiR(ME5rB1 z9pQv7mnvt)$03KzJw@DSssJ$!x+ZkF4p6F4Hd8(?*V3G}3N$H}c4)^R@G@7ZOIav6 zpo}y4|6ZTxL1QG$=_Lc{1EoTe8<kkU2LXbJmw9<Tpkz4tU;sY&Gt{Gyg8wE!b$5gx zwT+t}<KRO&X-THaskO3GW1Mg6_@);EG4UL{aI?D>x!_^V35zwpYP%O~5_`g;5>jB` zklYmua*wl$X;dCsaf4_t$k{F(L9T=dB+k7Lq1SO$9_i)?kaTMwO5W|_V*<)7YTsWR zdwwHk9P?xk+xX)I8Qca-OLCqnnzb0fWF}ohzo!i6gX+q>`H0ql%3w8ir2itdH7%LI z=OnfBt)O2R)mze}PH8~<*7IB2b66r!pL4$~kth4s`Ul!_otRuJdB~HWMGXQmecitc zaJKc$pXd39)5zqBl6`J^UE09hD)}ji`|sjgUX_hA`RUw=N!lY&+{ZAxOIs2j;sMt1 zKVw+^8=%c?@}Ci#GZjhm?l4n$>mnoy$sTtc=_4VLGAh!Antxhs%`~jbCd0bJz&>MI zh+KW@m|sr~@H)For(YQ8Izh65qj*@A{!TFb=Qm#5k^ZGdfFyq}?jK^1&lkX4=zliX zuV>-JF5s?zNJ0q=Bn%t3(1i+r`qtq5mc%n(09YrhB$-r&I<4J7^}2vUH1OcR)uVi# z0VbRP)Uy8|6E%KYI6?DYP57rO_38tXh6BKs{~#7o0;y1WKgIycUjH+09tSYxeZKR8 z6p-ou0?wmJfiMAqR3eU~u!IU?hTXyGf2<n>tlJ+D1nW`BbL*Aa*=Ca_Fh$O%`Gb2c zv+eFRDx5J_TA$xacpzdZbQ?Jj+!RT@uBUW^>A^#d{;4TrA!9XKDFqwh1fWR^twb&~ zP}29ej#r0tsG8d9ng(UDga8x;n70PoVHh@f#=qk_wdwLR*nlO}8dV<o(FKl0<YZ?r zxICrx_agoxO3xvvXjp|(k>cras2dBIN>UyXhLAfZ)c#g<*uxPSRGrbrh#R-nXhR%J zJg+IO0{ojkQ@*aBkuz`3y$mUeZZD$DvfW;gQAx}Z9opa3iVxV;j0B^iR%qSQy$np& zqkcs|M^T{1H;hH9ur*?wTi*7Fhdw$|1^`zMgzwB&E~*I2seK-t>yI}3+58sqTbs@U zFZlJv$@~$&cavA69zTxBm;_kA(qbDa=)8k27FZcqU=eptc4XF>%V>o%*UYWAHmQYn z!Jk9NO`rYfBoUC<wn&uXAM}|tA39DMEOh{?P9tDvGmrWFBQ?5fh%@8O8+VbpAKi>( z=S3{oBu2Y&N{u+#MtK8opHRNnO*^+5=DNfjMzy!lDr_XRS5EZudf#A1Eg+I+(@zGQ z*lV3AS{dlF^DVtR$XWM9V%<!ZZ8%Xh93uc`fm}WiNAqk3ao_a+BE1xfUZ0Kx>7kl# zZ}V=?T`K;s&tV?Gs`87pbYieJ^haWJWMxCv@NV29!Wa;lOS*C?*JP*t<bdOu76nd{ z*kQ@c%cp02{YWdR`Zh<Bq%Kqpg48T6UuOcuP*Vz{*&@C$Ng#Zk^?kI1jW-iCsPqX3 zf^?4VC>h1e^0s3x0OFj=EBum6>Ewp{^a?*fa=!gJtP|{@1r(s7PubMVSYFIW9AwN} z5X|t;t7PA9zdL=}V#xSVdf+a4(MuT@`}-IOk~$<uO-tbJ9qF(o)^`&qNbS~MT%w?> zk_>|smYfA}3M6x`QgR>4GbtuJpK=`-`PUf>nOT)(`OgRYQFSK`;+I@Xr(=1;Tlu%b zZ4xMBf)M`8@&hWiochYwkbwbs0w7q-{DF1)q+EyEL)VPY_jQT*LudVEhi}$!#Fc|8 z9^OoI$}xcj#7`fEN_M6>WL8H_hhO|r2B6&B`y9*#LR>l|AxP$6QiOy>9MZO6rq*C| z%Rdb2=;=9aH*`r0fVxUaANwJR-D7lt2Ss^;pU-E_m7J%Hgf8B{JBQt{x{wlB0-(WA zwv0`Z1g9Qw)Jz^~$A8+-E<G*4%Flc#LyRt*xFRwGO8X_u3!MIHX7mqpUi=6vAPLz| zfmM<|jzur@Dc%PG%v1ib5XqeVux>c@cYup)9=k|fd6=~R5aN$>M7FT}sro;or-G4? z|4ME>5qNLi-|2@lN(MW`{%2n0PMs%l|3$3T3veN1--7;KypOw^!uc)buxv^UD9IP} zu05jHoxD%%t9)d!#}AL;VUOY!4-hmWfxol%>3u4N5`n@H02Hme>uG3gnQLeWd@+GB z@K>e0x5gc=zSFG4Vsp-uj>P<}0^6andL`K}->OjiKcSV2Ydm0Tr#OceT%Mg>NIp`$ zyZQ$UE=dTBwtFW7Ud@dLKNX0TC`K;K?yKJW5VQo-($GLIlyU`b1v>I7oAk^Z>nv_; z7c@k@L7I?*3`m_*xjRBn01ZH!pk|;=*L4X1<UD&9X(b{1!_J~sjIoGkWjFIg*TrxK z(T%P;gY+61#;G&F`8#A*uiD|UHj!<29;M2~{!HJDdL~WXbt&V+4y&F5rI(VJ4`rDi zBW3?U;G!_8Kvg9)6W(XT7ID<GvAhR$)^p`dD6KM(eJ7x2dL(?i0XHl~7S#fp(}(Ir z68;JAE}7`Vrl4W^Lhb;wZI!SP^(%9C8L_)2;E+D@*~)<e#;jj5wGr-^J(;KF=VPa4 zD6GG8J~%xz;B2Q6Lqs-AHmM}JZ2@TExkN#sIXjFH5%{-p1nfeh`_1m(TKbvRkyn@N z?rG=vyFvq(IbuUcuCkU~saPy}6emS~;!LaVaBaghv3b<drrnJMZ+`Sk=OvgKyc=!E zVxR1r+2z~b9Yt5KsRi1}|Dd#jMi5>2WD_e@s1KG5(5GzzdJ>%9(#(y5J!Ux<lO;W} z^_t%rBSfl{zm=Dw3eVqRgW0vg7QQIN5M#pkXHi3+d)=fRssgm;PsZ^(d)9oDd0V;F z$>IXdggqT;|CZ0QO0+LwO6*vtYiKxc$h+)I4$)t3eY_*;RT9QdalZaJ%*rn0X=zbY z?$~RY?s<c0*xA<G2Q=hw-9j^&F7m`~V5H%@i{3orey0X%OF=5mnS%*QJ_oHs2Qmd? zsV<)jSHfg|v#|r-7)PO(R9_B=)igjQ?;ef^iBw9SyA=NBvnGjPN~sK42YdK0#6Q5z z)xg?>3sUvvzmVbCXHQ8YW&i)K$JAV%w<o(WGDl<tB$0h0E>!eF^Q6I(qhd7Eu3hI- zwv0-|(S96iA11OXoCmeXexvL!YKkeGG)>k{VTAnZ1OKY1*~PF)-#xQP`GHm|V)E`{ z2q)vTuw?hEyCgDSflesu>wZrzd_~jX^HhNX?Vm7oBXtjzJc2%PSmaNUUocuVqOFfV z${O~I=?S9b1WYRKSkd1f>;Z2{2I5>4$~e6$kH-Ow4grwE)<;OpgoX20_EQbRgVgmE zJqw?@iAmmwiOy{$ctL&BrkMwcH(s0k32nn`pN3LL25@o{??Kbg3w1J)G^;aXY789J z&7GzTO5_GmfVxL{vXO{gLuSUOMhQmmvK0Q=VtpmDQcBD6gxm?h$G(L(j_0Rrlr6!$ zAoqA>sI^9Eg?gAg9sC0$UM6%)obu#UnSwG<yT)p<Ip#M|ABb0-XSG!!psoEJ_<83& zt7;O-x8N$t{z8^khGa2m!8yp+zi+<##g8~|<HOm`gyTR%cL1@zlo&HJSqyYA{@(xX zKHJX%dUmj?yUUa$pguruQE+7IZDZI=S_|#W5)DuLdrjiZGJ_`lcBtpOpU9B}73_B| z*GPYgiw5LIB+IDC?zi24scEawob`FJ(}u$Q-hdXB{H7xJ^Mw;s^}TV*(2)JM2jlhd z#k|**Hc1Abb6Z(@I1Yk->Rjtc4*$!V+?1{}f+cCE@0D_Se>r3pX5Zap9OgK@{l&%b z8v9>D4lsnQbh}VOmr0HOE#42=+3W7iT|=fgVLDYw#*nkEU!>rI24QB0V1hkqw5W0Z z=S=R{c{HF$>Rbcvgaq>I9I$ny@X{BAp1eY5y3zHbBv7JHlgW!#M<}3CBu~L&Dd%0P z_7eba;B%n=IIp46?^4ARm|FWYv8uXyO)1obZ=avv(hQg}Se2jzb$^tKbj$<NfNJ9i zUC7nIOIsnpWimG8&2E;i-qLQ8=RKIOGKt&MW7R|K!;^mj|HL)vPvgURjf!vR&RIO# zX)9oTZ4%KW%&UtvW-Td6t?Ih!v*t0i_M$*xzu|MJiCo;5Gy0>8Rg1RiqMMBdB6cQQ z*65hh%L8aY?u?N;rATX_WocM}vf1Ui7IJmg3ehpctnlfX4_7dB93uPSC~t`K?q3$E z;3xxb$0t1SbY@uk3VGDK_{}DVgLy))0J28)@)_=eTj{j=8UMO%%Y7C4eV*khKrr#n zGV+YMAZG%mHvMJ~T(?qGsg$9jb0OKfBTjjc8iYHDT>Fuqj68vxCO%V?+CtqaLXo|> zg#oKpv2(pNchf>o$y$#Dq1Y`Bk|1yy0NH~|1{9P>l{aHtpT(NEp|g(LI$&CpJV#fb zZ(X=7#7*tr!{-skb(Gm%)^GIcOqS9UCQ~4|+knhM4`r0>*Wv`k>JHZjs(Z{O-3*Qv zEEtjkBY=lb3}_d{UM@h{a7w>wYYU4w!5D=ogaxz}*^IT*l|j>_d|sNX0tOAUbNsP6 zX@f&(=`+#B7*e&_>=d5xfG+5~82YR1Fi8OyFsP3$yQJGyl|8e&8)sj2)e3Bq^%uSz zjE=251^Ni|FNMpSd7dJE4%mJ_^ZK&%bv52+VMCDzkcvI|m7#i}X><TS{hFa(6H8J+ z4Gd?Wyh;M95C2=Q^N*S~48U^GNr0V&LCUhu9>OWefEQqa39Q2fI{(d04;FvvdO8M4 zP|bcyxL$V!=HgA{Eq{pe**B<zzD(69aJT<0i)=Xoz$?S^5B=ej5OrFFakWD5Ki*6W zh%qvv`^+_9B({FKf)+F+Q<lY>(?3YK<7{0t4-~v2#A+OWTs~PGaG9Jucg5QZSO*4| zcuJHCKv)5Mi3Wm0MUrt)QrA8R{cyivK8VrZkXVai=a9T()l39}A7Uc+Nx_s7KT?oG zBPuy6XmL~`4*#9Zft&ajpa6se=<&Upom+cz4AdI-!32?pbUd3b3Hlan1CPi^N^CKa zy5f@<deBAvokX!)@3icRwHc?n9MXWx7aE_L$v_3%fWJ;<Y;fhgzZkCeccGzQ7reSL zK*SwBZBp?-Dtc_*DtQ-Fe-;4w96HDRSVzORp7)7GnSFj?(V@muvVPMu85E;v4IBBg z-**0&CPM$fAkbyj<|yfMARk>2B~CyCxC=-|$(u?ogu9;d+tfg*{(!~jH}FP(AKXEa zfvE4ln@an(@Zx3jDw8GT0!XF|?PDyp$|vvxer|m`gX6zE2iF9vqlsi#oggNy!>5md zF36XeB75a^hYIV4jacsh()1DT+=_}GlSK|svyg%=bQxhS=XF^U&mgDM8ficd#sc_| zj1@8!kPI#+Zm0DHsKBn{J``}c_tPiX)&0MX7+IY^W?LLE_%0K{CamW)lU-P6AuJKi z@k8+QIUnV%>&J~A1?^^1#Wl@1QjmOvaC^ZJR%J*#<>FV?`DaBo1BvQX8z)_@He0$K zUV$UJo}7yFFL~!gH*mi{Uj)wG2=%wDH~|*aiv|SX1G@hE+RD1SXS+b!EypLw+aT{1 zO!f|-r2{urv=mQ%vFlWe25&I)TBxSQRA`czJj>-s@-KO`e!W|eB6>^$x9zD$JAVG^ zwo3ITrx)~Ds&oSg#IK-*RZ;}R4U6e@5XeYY4I4%2`tzI=2y|Kip9n^Fl|Pu63&m7P zGg{ImZ3NBf<`BUOG^4s3o_yLmhmJSCs470B1oh+1amb|tEuWGDkS}&)hT<3~SVav> zv%M>|6ER5+I-is`$q5&I*~AKb28mxxuTAw43ecPOlVF?RlCe&AjXucx^Nf3AkYnC~ zyiG9FiJ_?(Wds#y&qk(jMvskH5`kL7M}e{GWu5c@booJf4+`>bx(M90q6+|C*1m9X zrSq>B6)*r!msJ8)Rw;S}ki530W7pjm#wf1w-CGShiFeIP8o#&|DkCN}%kVm-{sCdD z4@3NBFEk)QBqp@RYl}s+940TM#)et6Q(ue_oDW^~=wtF4{1So?4EI|Yf2~3#D<s^5 z9$^pYZ=ZJ&*SYbc%~_2WBZO6(2p%WJdI=eqFDo`Kolc)fOZIsgWHEBzFkUPi#vG-_ z2HecF8RTqE_K{`9`y)?YMlsVg39K)^rjy#*Vx{~VW-DF|qm*r>h)-+GRJC+bS<SzF zSz^FAA;y^D!ME}SpeM<Yw!p&6-~JA>VIj-x^D^fmtG|L`icEE=`pZIP=6hRL;_q!i zcH6Unm-SL)Qo|VG@MZyXHdLLZEJ{tbrN01W|B9>EzpP}wkvKl5Et~w<PcKSGQ<0(t z*RDHy*z19pMvTlmp3=%0c08i>pQnni52J$L2ZT6!mItDFp)#qBm!5=3`5YHb<_Jkm z76}*nh$;4D>MLeuHt-}?tn2JiM84)Yb(!|2_Qb5&g|yO}!=;|7_efH%gqszadB7*G zjnGCx!75Z=z20d;dsGtFzRfD?VJYOm(?D8l3}MP!u=|9x4039J^U|kys=zLHSQCCl z?=En^Yj%>JQIeW)<!Xn3*v<HNw$y!%<}z&0WYMA@tL;fv7`OE@X{NI-Vboqbd|;iC z%4=9L-3}Gv7;d(yPYb;skvyhsD#NzkFAA<q4$;|39NT!?TO~P_Is-(bPRw4fS&MOR z^IV}dO>2Wzj)oeo`dC~zS#M4DF3B6=?(ZBY(@*aCPPkOnR98#D!3Ejb?SxOQLW!m0 zrE+*};3E$%fQ4l_{j)!U)6U-tUsgbzL60$G4b^CK`YlSHv|U7NrJ2$ubT#wcuEhZV z`LHjQHhp<|FI6r>J(=^Q>m4>`^5DP(UE=#oI47;Az|~z`#Z5C9B7Gb4qXD?IJHewk z@$M3}PIo+^cihQma|!NVGip<2D#Bg?Fu5$LqSHW!-$D2q7$R*?xNBL3S_C5VUGebx zS1$qMRGF`cy-+7we@7b_0%%^D8%=#~Wp$8D3m@7&+r9cgmE;W8@ZIGt(nm2<Ko>}> zOuc>id*<=Wsh7@lYdhn5)Z7CMM_{o*5En=?Y^=g`>@g5pEPjrFte!>|$-6_TXT8^M zFH~DzdEx=D0p$?8>0*EUC2GP1qL$H|7Sl}0<ddvc4g){4Kr&<6HzOon1I9iGIOI)a zFP^zq9R3mMn74-%zF#F-v&U&k;x4=GG#1iC?y&q8ZNOIa5l~_=y@;g^;Hrext`$33 zuUAYV&^i>&crf@O`x^d!Ix90}`pWI%zJWFGH^eLHu9m?!7kbb8FO{tsruHu~$d$ir zjx?5KuDUCf=LnbCIh~s?b~aPIvia2IUV^to6vJ7Ysx_zgD{pxib)+C^){nVeM&yK1 z2nAQRyeX5?J)e1E0fa32p*p!kspL;DTSFb4;*lQr$84U9DHod3eKr}gYWp*I0z;WT zbuw!?LnsCW!<XgTOEW^I)?})k{1liUqvxG1)2`$DwX%fAcVA+fLxgpmJYJ$W)adWW zaf)Sce<C@dl}5RkJj;5|Y}N*@ND^-x_Hsu-?IE<>l<isLGqG^@pPeYdz-0+IxpTYy zO+Dc3e^-3I%1%z&o0KR-`ZJ>~-xR=$*+}rN6|1Wu#n~%$&8rcmS1#yoz7~UOZAM5R z4?THV_bo=I8>Y~3?s!{Pt>@YpfyieKq4SD3-fVXpP!k+<`*}nS?b!OR1Q!yYF~{b> z*N<UTflr84je62rtGjT2AT+2Bdfg-=bx?^SOTFCO8cCwY6~;Atj8tHen?|RfW-R0s w$Xj@L2}cStaQa_<2=)JdyZ-<0M5_cM6eIN;uLqIX{m=L+%Bjfa-7)a}e^z(O?*IS* literal 14251 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 -- GitLab