File indexing completed on 2024-11-19 23:20:31
0001 #ifndef cutflowutil_h
0002 #define cutflowutil_h
0003
0004 #include "ttreex.h"
0005 #include "printutil.h"
0006 #include <tuple>
0007 #include <vector>
0008 #include <map>
0009 #include "TH1.h"
0010 #include "TString.h"
0011 #include <iostream>
0012 #include <algorithm>
0013 #include <sys/ioctl.h>
0014 #include <functional>
0015
0016
0017 #define USE_CUTLAMBDA
0018 #define TREEMAPSTRING std::string
0019 #define CUTFLOWMAPSTRING TString
0020 #define DATA c_str
0021 #define THist TH1D
0022
0023 namespace RooUtil {
0024 namespace CutflowUtil {
0025
0026 class CutNameList {
0027 public:
0028 std::vector<TString> cutlist;
0029 CutNameList() {}
0030 CutNameList(const CutNameList& cutnamelist) { cutlist = cutnamelist.cutlist; }
0031 void clear() { cutlist.clear(); }
0032 void addCutName(TString cutname) { cutlist.push_back(cutname); }
0033 void print() {
0034 for (auto& str : cutlist)
0035 std::cout << str << std::endl;
0036 }
0037 };
0038
0039 class CutNameListMap {
0040 public:
0041 std::map<TString, CutNameList> cutlists;
0042 std::vector<TString> cutlist;
0043 CutNameList& operator[](TString name) { return cutlists[name]; }
0044 void clear() { cutlists.clear(); }
0045 void print() {
0046 for (auto& cl : cutlists) {
0047 std::cout << "CutNameList - " << cl.first << std::endl;
0048 cl.second.print();
0049 }
0050 }
0051 std::map<TString, std::vector<TString>> getStdVersion() {
0052 std::map<TString, std::vector<TString>> obj_cutlists;
0053 for (auto& cl : cutlists)
0054 obj_cutlists[cl.first] = cl.second.cutlist;
0055 return obj_cutlists;
0056 }
0057 };
0058
0059 void createCutflowBranches(CutNameListMap& cutlists, RooUtil::TTreeX& tx);
0060 void createCutflowBranches(std::map<TString, std::vector<TString>>& cutlists, RooUtil::TTreeX& tx);
0061 std::tuple<std::vector<bool>, std::vector<float>> getCutflow(std::vector<TString> cutlist, RooUtil::TTreeX& tx);
0062 std::pair<bool, float> passCuts(std::vector<TString> cutlist, RooUtil::TTreeX& tx);
0063 void fillCutflow(std::vector<TString> cutlist, RooUtil::TTreeX& tx, THist* h);
0064 void fillRawCutflow(std::vector<TString> cutlist, RooUtil::TTreeX& tx, THist* h);
0065 std::tuple<std::map<CUTFLOWMAPSTRING, THist*>, std::map<CUTFLOWMAPSTRING, THist*>> createCutflowHistograms(
0066 CutNameListMap& cutlists, TString syst = "");
0067 std::tuple<std::map<CUTFLOWMAPSTRING, THist*>, std::map<CUTFLOWMAPSTRING, THist*>> createCutflowHistograms(
0068 std::map<TString, std::vector<TString>>& cutlists, TString syst = "");
0069 void saveCutflowHistograms(std::map<CUTFLOWMAPSTRING, THist*>& cutflows,
0070 std::map<CUTFLOWMAPSTRING, THist*>& rawcutflows);
0071
0072
0073
0074 }
0075
0076 class CutTree {
0077 public:
0078 TString name;
0079 CutTree* parent;
0080 std::vector<CutTree*> parents;
0081 std::vector<CutTree*> children;
0082 std::vector<TString> systcutnames;
0083 std::vector<CutTree*> systcuts;
0084 std::map<TString, CutTree*> systs;
0085 int pass;
0086 float weight;
0087 std::vector<int> systpasses;
0088 std::vector<float> systweights;
0089 bool pass_this_cut;
0090 float weight_this_cut;
0091 std::function<bool()> pass_this_cut_func;
0092 std::function<float()> weight_this_cut_func;
0093
0094
0095 #ifdef USE_CUTLAMBDA
0096 std::map<TString, std::vector<std::tuple<THist*, std::function<float()>>>> hists1d;
0097 std::map<TString,
0098 std::vector<std::tuple<THist*, std::function<std::vector<float>()>, std::function<std::vector<float>()>>>>
0099 hists1dvec;
0100 std::map<TString, std::vector<std::tuple<TH2F*, std::function<float()>, std::function<float()>>>> hists2d;
0101 std::map<TString,
0102 std::vector<std::tuple<TH2F*,
0103 std::function<std::vector<float>()>,
0104 std::function<std::vector<float>()>,
0105 std::function<std::vector<float>()>>>>
0106 hists2dvec;
0107 #else
0108 std::map<TString, std::vector<std::tuple<THist*, TString>>> hists1d;
0109 std::map<TString, std::vector<std::tuple<TH2F*, TString, TString>>> hists2d;
0110 #endif
0111 std::vector<std::tuple<int, int, unsigned long long>> eventlist;
0112 CutTree(TString n) : name(n), parent(0), pass(false), weight(0) {}
0113 ~CutTree() {
0114 for (auto& child : children) {
0115 if (child)
0116 delete child;
0117 }
0118 }
0119 void printCuts(int indent = 0, std::vector<int> multichild = std::vector<int>()) {
0120 struct winsize w;
0121 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
0122 int colsize = std::min(w.ws_col - 100, 600);
0123 if (indent == 0) {
0124 TString header = "Cut name";
0125 int extra = colsize - header.Length();
0126 for (int i = 0; i < extra; ++i)
0127 header += " ";
0128 header += "|pass|weight|systs";
0129 print(header);
0130 TString delimiter = "";
0131 for (int i = 0; i < w.ws_col - 10; ++i)
0132 delimiter += "=";
0133 print(delimiter);
0134 }
0135 TString msg = "";
0136 for (int i = 0; i < indent; ++i) {
0137 if (std::find(multichild.begin(), multichild.end(), i + 1) != multichild.end()) {
0138 if (indent == i + 1)
0139 msg += " +";
0140 else
0141 msg += " |";
0142 } else
0143 msg += " ";
0144 }
0145 msg += name;
0146 int extrapad = colsize - msg.Length() > 0 ? colsize - msg.Length() : 0;
0147 for (int i = 0; i < extrapad; ++i)
0148 msg += " ";
0149
0150 msg += TString::Format("| %d | %f|", pass, weight);
0151 for (auto& key : systs) {
0152 msg += key.first + " ";
0153 }
0154 print(msg);
0155 if (children.size() > 1)
0156 multichild.push_back(indent + 1);
0157 for (auto& child : children) {
0158 (*child).printCuts(indent + 1, multichild);
0159 }
0160 }
0161 void printEventList() {
0162 print(TString::Format("Print event list for the cut = %s", name.Data()));
0163 for (auto& eventid : eventlist) {
0164 int run = std::get<0>(eventid);
0165 int lumi = std::get<1>(eventid);
0166 unsigned long long evt = std::get<2>(eventid);
0167 TString msg = TString::Format("%d:%d:%llu", run, lumi, evt);
0168 std::cout << msg << std::endl;
0169 }
0170 }
0171 void writeEventList(TString ofilename) {
0172 std::ofstream outFile(ofilename);
0173 for (auto& tuple : eventlist) {
0174 int run = std::get<0>(tuple);
0175 int lumi = std::get<1>(tuple);
0176 unsigned long long evt = std::get<2>(tuple);
0177 outFile << run << ":" << lumi << ":" << evt << std::endl;
0178 }
0179 outFile.close();
0180 }
0181 void addCut(TString n) {
0182 CutTree* obj = new CutTree(n);
0183 obj->parent = this;
0184 obj->parents.push_back(this);
0185 children.push_back(obj);
0186 }
0187 void addSyst(TString syst) {
0188
0189 if (systs.find(syst) != systs.end())
0190 return;
0191
0192 CutTree* obj = new CutTree(this->name + syst);
0193 systs[syst] = obj;
0194 systcutnames.push_back(syst);
0195 systcuts.push_back(obj);
0196 systpasses.push_back(1);
0197 systweights.push_back(1);
0198 obj->children = this->children;
0199 obj->parents = this->parents;
0200 obj->parent = this->parent;
0201 }
0202 #ifdef USE_CUTLAMBDA
0203 void addHist1D(THist* h, std::function<float()> var, TString syst) {
0204 if (syst.IsNull())
0205 hists1d["Nominal"].push_back(std::make_tuple(h, var));
0206 else
0207 hists1d[syst].push_back(std::make_tuple(h, var));
0208 }
0209 void addHist1DVec(THist* h,
0210 std::function<std::vector<float>()> var,
0211 std::function<std::vector<float>()> wgt,
0212 TString syst) {
0213 if (syst.IsNull())
0214 hists1dvec["Nominal"].push_back(std::make_tuple(h, var, wgt));
0215 else
0216 hists1dvec[syst].push_back(std::make_tuple(h, var, wgt));
0217 }
0218 void addHist2D(TH2F* h, std::function<float()> varx, std::function<float()> vary, TString syst) {
0219 if (syst.IsNull())
0220 hists2d["Nominal"].push_back(std::make_tuple(h, varx, vary));
0221 else
0222 hists2d[syst].push_back(std::make_tuple(h, varx, vary));
0223 }
0224 void addHist2DVec(TH2F* h,
0225 std::function<std::vector<float>()> varx,
0226 std::function<std::vector<float>()> vary,
0227 std::function<std::vector<float>()> elemwgt,
0228 TString syst) {
0229 if (syst.IsNull())
0230 hists2dvec["Nominal"].push_back(std::make_tuple(h, varx, vary, elemwgt));
0231 else
0232 hists2dvec[syst].push_back(std::make_tuple(h, varx, vary, elemwgt));
0233 }
0234 #else
0235 void addHist1D(THist* h, TString var, TString syst) {
0236 if (syst.IsNull())
0237 hists1d["Nominal"].push_back(std::make_tuple(h, var));
0238 else
0239 hists1d[syst].push_back(std::make_tuple(h, var));
0240 }
0241 void addHist2D(TH2F* h, TString varx, TString vary, TString syst) {
0242 if (syst.IsNull())
0243 hists2d["Nominal"].push_back(std::make_tuple(h, varx, vary));
0244 else
0245 hists2d[syst].push_back(std::make_tuple(h, varx, vary));
0246 }
0247 #endif
0248 CutTree* getCutPointer(TString n) {
0249
0250 if (name.EqualTo(n)) {
0251 return this;
0252 } else {
0253
0254 for (auto& child : children) {
0255 CutTree* c = child->getCutPointer(n);
0256 if (c)
0257 return c;
0258 }
0259 return 0;
0260 }
0261 }
0262
0263 CutTree& getCut(TString n) {
0264 CutTree* c = getCutPointer(n);
0265 if (c) {
0266 return *c;
0267 } else {
0268 RooUtil::error(TString::Format("Asked for %s cut, but did not find the cut", n.Data()));
0269 return *this;
0270 }
0271 }
0272 std::vector<TString> getCutList(TString n, std::vector<TString> cut_list = std::vector<TString>()) {
0273
0274
0275
0276
0277
0278
0279 CutTree* c = 0;
0280 if (cut_list.size() == 0) {
0281 c = &getCut(n);
0282 cut_list.push_back(c->name);
0283 } else {
0284 c = this;
0285 cut_list.push_back(n);
0286 }
0287 if (c->parent) {
0288 return (c->parent)->getCutList((c->parent)->name, cut_list);
0289 } else {
0290 std::reverse(cut_list.begin(), cut_list.end());
0291 return cut_list;
0292 }
0293 }
0294 std::vector<TString> getEndCuts(std::vector<TString> endcuts = std::vector<TString>()) {
0295 if (children.size() == 0) {
0296 endcuts.push_back(name);
0297 return endcuts;
0298 }
0299 for (auto& child : children)
0300 endcuts = child->getEndCuts(endcuts);
0301 return endcuts;
0302 }
0303 std::vector<TString> getCutListBelow(TString n, std::vector<TString> cut_list = std::vector<TString>()) {
0304
0305 CutTree* c = 0;
0306 if (cut_list.size() == 0) {
0307 c = &getCut(n);
0308 cut_list.push_back(c->name);
0309 } else {
0310 c = this;
0311 cut_list.push_back(n);
0312 }
0313 if (children.size() > 0) {
0314 for (auto& child : c->children) {
0315 cut_list = child->getCutListBelow(child->name, cut_list);
0316 }
0317 return cut_list;
0318 } else {
0319 return cut_list;
0320 }
0321 }
0322 void clear() {
0323 pass = false;
0324 weight = 0;
0325 for (auto& child : children)
0326 child->clear();
0327 }
0328 void addSyst(TString syst,
0329 std::vector<TString> patterns,
0330 std::vector<TString> vetopatterns = std::vector<TString>()) {
0331 for (auto& pattern : patterns)
0332 if (name.Contains(pattern)) {
0333 bool veto = false;
0334 for (auto& vetopattern : vetopatterns) {
0335 if (name.Contains(vetopattern))
0336 veto = true;
0337 }
0338 if (not veto)
0339 addSyst(syst);
0340 }
0341 for (auto& child : children)
0342 child->addSyst(syst, patterns, vetopatterns);
0343 }
0344 void clear_passbits() {
0345 pass = 0;
0346 weight = 0;
0347 for (auto& child : children)
0348 child->clear_passbits();
0349 }
0350 void evaluate(RooUtil::TTreeX& tx,
0351 TString cutsystname = "",
0352 bool doeventlist = false,
0353 bool aggregated_pass = true,
0354 float aggregated_weight = 1) {
0355 #ifdef USE_TTREEX
0356 evaluate_use_ttreex(tx, cutsystname, doeventlist, aggregated_pass, aggregated_weight);
0357 #else
0358 #ifdef USE_CUTLAMBDA
0359 evaluate_use_lambda(tx, cutsystname, doeventlist, aggregated_pass, aggregated_weight);
0360 #else
0361 evaluate_use_internal_variable(tx, cutsystname, doeventlist, aggregated_pass, aggregated_weight);
0362 #endif
0363 #endif
0364 }
0365 void evaluate_use_lambda(RooUtil::TTreeX& tx,
0366 TString cutsystname = "",
0367 bool doeventlist = false,
0368 bool aggregated_pass = true,
0369 float aggregated_weight = 1) {
0370 if (!parent) {
0371 clear_passbits();
0372 pass = 1;
0373 weight = 1;
0374 } else {
0375 if (cutsystname.IsNull()) {
0376 if (pass_this_cut_func) {
0377 pass = pass_this_cut_func() && aggregated_pass;
0378 weight = weight_this_cut_func() * aggregated_weight;
0379 if (!pass)
0380 return;
0381 } else {
0382 TString msg = "cowardly passing the event because cut and weight func not set! cut name = " + name;
0383 warning(msg);
0384 pass = aggregated_pass;
0385 weight = aggregated_weight;
0386 }
0387 } else {
0388 if (systs.find(cutsystname) == systs.end()) {
0389 if (pass_this_cut_func) {
0390 pass = pass_this_cut_func() && aggregated_pass;
0391 weight = weight_this_cut_func() * aggregated_weight;
0392 if (!pass)
0393 return;
0394 } else {
0395 TString msg = "cowardly passing the event because cut and weight func not set! cut name = " + name;
0396 warning(msg);
0397 pass = aggregated_pass;
0398 weight = aggregated_weight;
0399 }
0400 } else {
0401 if (systs[cutsystname]->pass_this_cut_func) {
0402 pass = systs[cutsystname]->pass_this_cut_func() && aggregated_pass;
0403 weight = systs[cutsystname]->weight_this_cut_func() * aggregated_weight;
0404 if (!pass)
0405 return;
0406 } else {
0407 TString msg = "cowardly passing the event because cut and weight func not set! cut name = " + name +
0408 " syst name = " + cutsystname;
0409 warning(msg);
0410 pass = aggregated_pass;
0411 weight = aggregated_weight;
0412 }
0413 }
0414 }
0415 }
0416 if (doeventlist and pass and cutsystname.IsNull()) {
0417 if (tx.hasBranch<int>("run") && tx.hasBranch<int>("lumi") && tx.hasBranch<unsigned long long>("evt")) {
0418 eventlist.push_back(std::make_tuple(
0419 tx.getBranch<int>("run"), tx.getBranch<int>("lumi"), tx.getBranch<unsigned long long>("evt")));
0420 }
0421 }
0422 for (auto& child : children)
0423 child->evaluate_use_lambda(tx, cutsystname, doeventlist, pass, weight);
0424 }
0425 void evaluate_use_internal_variable(RooUtil::TTreeX& tx,
0426 TString cutsystname = "",
0427 bool doeventlist = false,
0428 bool aggregated_pass = true,
0429 float aggregated_weight = 1) {
0430 if (!parent) {
0431 clear_passbits();
0432 pass = 1;
0433 weight = 1;
0434 } else {
0435 if (cutsystname.IsNull()) {
0436 pass = pass_this_cut && aggregated_pass;
0437 weight = weight_this_cut * aggregated_weight;
0438 if (!pass)
0439 return;
0440 } else {
0441 if (systs.find(cutsystname) == systs.end()) {
0442 pass = pass_this_cut && aggregated_pass;
0443 weight = weight_this_cut * aggregated_weight;
0444 if (!pass)
0445 return;
0446 } else {
0447 pass = systs[cutsystname]->pass_this_cut && aggregated_pass;
0448 weight = systs[cutsystname]->weight_this_cut * aggregated_weight;
0449 if (!pass)
0450 return;
0451 }
0452 }
0453 }
0454 if (doeventlist and pass and cutsystname.IsNull()) {
0455 if (tx.hasBranch<int>("run") && tx.hasBranch<int>("lumi") && tx.hasBranch<unsigned long long>("evt")) {
0456 eventlist.push_back(std::make_tuple(
0457 tx.getBranch<int>("run"), tx.getBranch<int>("lumi"), tx.getBranch<unsigned long long>("evt")));
0458 }
0459 }
0460 for (auto& child : children)
0461 child->evaluate_use_internal_variable(tx, cutsystname, doeventlist, pass, weight);
0462 }
0463 void evaluate_use_ttreex(RooUtil::TTreeX& tx,
0464 TString cutsystname = "",
0465 bool doeventlist = false,
0466 bool aggregated_pass = true,
0467 float aggregated_weight = 1) {
0468 if (!parent) {
0469 pass = tx.getBranch<bool>(name);
0470 weight = tx.getBranch<float>(name + "_weight");
0471 } else {
0472 if (cutsystname.IsNull()) {
0473 if (!tx.hasBranch<bool>(name))
0474 return;
0475 pass = tx.getBranch<bool>(name) && aggregated_pass;
0476 weight = tx.getBranch<float>(name + "_weight") * aggregated_weight;
0477 } else {
0478 if (systs.find(cutsystname) == systs.end()) {
0479 if (!tx.hasBranch<bool>(name))
0480 return;
0481 pass = tx.getBranch<bool>(name) && aggregated_pass;
0482 weight = tx.getBranch<float>(name + "_weight") * aggregated_weight;
0483 } else {
0484 if (!tx.hasBranch<bool>(name + cutsystname))
0485 return;
0486 pass = tx.getBranch<bool>(name + cutsystname) && aggregated_pass;
0487 weight = tx.getBranch<float>(name + cutsystname + "_weight") * aggregated_weight;
0488 }
0489 }
0490 }
0491 if (doeventlist and pass and cutsystname.IsNull()) {
0492 if (tx.hasBranch<int>("run") && tx.hasBranch<int>("lumi") && tx.hasBranch<unsigned long long>("evt")) {
0493 eventlist.push_back(std::make_tuple(
0494 tx.getBranch<int>("run"), tx.getBranch<int>("lumi"), tx.getBranch<unsigned long long>("evt")));
0495 }
0496 }
0497 for (auto& child : children)
0498 child->evaluate_use_ttreex(tx, cutsystname, doeventlist, pass, weight);
0499 }
0500 void sortEventList() {
0501 std::sort(
0502 eventlist.begin(),
0503 eventlist.end(),
0504 [](const std::tuple<int, int, unsigned long long>& a, const std::tuple<int, int, unsigned long long>& b) {
0505 if (std::get<0>(a) != std::get<0>(b))
0506 return std::get<0>(a) < std::get<0>(b);
0507 else if (std::get<1>(a) != std::get<1>(b))
0508 return std::get<1>(a) < std::get<1>(b);
0509 else if (std::get<2>(a) != std::get<2>(b))
0510 return std::get<2>(a) < std::get<2>(b);
0511 else
0512 return true;
0513 });
0514 }
0515 void clearEventList() { eventlist.clear(); }
0516 void addEventList(int run, int lumi, unsigned long long evt) {
0517 eventlist.push_back(std::make_tuple(run, lumi, evt));
0518 }
0519 #ifdef USE_CUTLAMBDA
0520 void fillHistograms(TString syst, float extrawgt) {
0521
0522 if (!pass)
0523 return;
0524
0525 if (hists1d.size() != 0 or hists2d.size() != 0 or hists2dvec.size() != 0 or hists1dvec.size() != 0) {
0526 TString systkey = syst.IsNull() ? "Nominal" : syst;
0527 for (auto& tuple : hists1d[systkey]) {
0528 THist* h = std::get<0>(tuple);
0529 std::function<float()> vardef = std::get<1>(tuple);
0530 h->Fill(vardef(), weight * extrawgt);
0531 }
0532 for (auto& tuple : hists2d[systkey]) {
0533 TH2F* h = std::get<0>(tuple);
0534 std::function<float()> varxdef = std::get<1>(tuple);
0535 std::function<float()> varydef = std::get<2>(tuple);
0536 h->Fill(varxdef(), varydef(), weight * extrawgt);
0537 }
0538 for (auto& tuple : hists1dvec[systkey]) {
0539 THist* h = std::get<0>(tuple);
0540 std::function<std::vector<float>()> vardef = std::get<1>(tuple);
0541 std::function<std::vector<float>()> wgtdef = std::get<2>(tuple);
0542 std::vector<float> varx = vardef();
0543 std::vector<float> elemwgts;
0544 if (wgtdef)
0545 elemwgts = wgtdef();
0546 for (unsigned int i = 0; i < varx.size(); ++i) {
0547 if (wgtdef)
0548 h->Fill(varx[i], weight * extrawgt * elemwgts[i]);
0549 else
0550 h->Fill(varx[i], weight * extrawgt);
0551 }
0552 }
0553 for (auto& tuple : hists2dvec[systkey]) {
0554 TH2F* h = std::get<0>(tuple);
0555 std::function<std::vector<float>()> varxdef = std::get<1>(tuple);
0556 std::function<std::vector<float>()> varydef = std::get<2>(tuple);
0557 std::function<std::vector<float>()> wgtdef = std::get<3>(tuple);
0558 std::vector<float> varx = varxdef();
0559 std::vector<float> vary = varydef();
0560 if (varx.size() != vary.size()) {
0561 TString msg =
0562 "the vector input to be looped over do not have same length for x and y! check the variable definition "
0563 "for histogram ";
0564 msg += h->GetName();
0565 warning(msg);
0566 }
0567 std::vector<float> elemwgts;
0568 if (wgtdef)
0569 elemwgts = wgtdef();
0570 for (unsigned int i = 0; i < varx.size(); ++i) {
0571 if (wgtdef)
0572 h->Fill(varx[i], vary[i], weight * extrawgt * elemwgts[i]);
0573 else
0574 h->Fill(varx[i], vary[i], weight * extrawgt);
0575 }
0576 }
0577 }
0578 for (auto& child : children)
0579 child->fillHistograms(syst, extrawgt);
0580 }
0581 #else
0582 void fillHistograms(RooUtil::TTreeX& tx, TString syst, float extrawgt) {
0583
0584 if (!pass)
0585 return;
0586
0587 if (hists1d.size() != 0 or hists2d.size() != 0) {
0588 TString systkey = syst.IsNull() ? "Nominal" : syst;
0589 for (auto& tuple : hists1d[systkey]) {
0590 THist* h = std::get<0>(tuple);
0591 TString varname = std::get<1>(tuple);
0592 h->Fill(tx.getBranch<float>(varname), weight * extrawgt);
0593 }
0594 for (auto& tuple : hists2d[systkey]) {
0595 TH2F* h = std::get<0>(tuple);
0596 TString varname = std::get<1>(tuple);
0597 TString varnamey = std::get<2>(tuple);
0598 h->Fill(tx.getBranch<float>(varname), tx.getBranch<float>(varnamey), weight * extrawgt);
0599 }
0600 }
0601 for (auto& child : children)
0602 child->fillHistograms(tx, syst, extrawgt);
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625
0626
0627
0628
0629
0630 }
0631 #endif
0632 };
0633 }
0634
0635 #endif