text: ////////////////////////////////////////////////////////////////////////////////
  //
  //    Compares output files from PrintGeomInfo created using DDD and DD4hep
  //    inputs. Usage:
  //
  //    SimFileCompare infile1 infile2 type files mode debug
  //    infile1  (const char*)   First file name
  //    infile2  (const char*)   Second file name
  //    type     (int)           Type of file: material (0), solid (1),
  //                             LogicalVolume (2), PhysicalVolume (3)
  //    files    (int)           Double digits each inidicating the file source
  //                             (0 for DDD, 1 for DD4HEP). So if first file is
  //                             DDD and second is DD4HEP, it will be 10
  //    mode     (int)           Treat (0) or not treat (1) names from DDD
  //    deug     (int)           Single digit number (0 minimum printout)
  //
  ////////////////////////////////////////////////////////////////////////////////
  
  #include <algorithm>
  #include <cstdlib>
  #include <fstream>
  #include <iomanip>
  #include <iostream>
  #include <map>
  #include <string>
  #include <vector>
  
  struct materials {
    int occ;
    double radl, intl;
    materials(int oc = 1, double rd = 0, double in = 0) : occ(oc), radl(rd), intl(in) {}
  };
  
  struct solids {
    int occ;
    double volume;
    solids(int oc = 1, double vol = 0) : occ(oc), volume(vol) {}
  };
  
  struct lvs {
    int occ;
    double mass;
    lvs(int oc = 1, double m = 0) : occ(oc), mass(m) {}
  };
  
  struct pvs {
    int occ;
    double xx, yy, zz;
    pvs(int oc = 1, double x = 0, double y = 0, double z = 0) : occ(oc), xx(x), yy(y), zz(z) {}
  };
  
  std::string removeExtraName(const std::string& name, int debug) {
    std::string nam(name);
    std::string nam1 = name.substr(0, 2);
    if (((nam1 == "GE") || (nam1 == "GH") || (nam1 == "MB") || (nam1 == "ME") || (nam1 == "RE") || (nam1 == "RR") ||
         (nam1 == "RT")) &&
        (name.size() > 5)) {
      uint32_t loc = name.size() - 5;
      if ((name.substr(0, 15) != "MBCables_Wheels") && (name.substr(loc, 1) == "_")) {
        std::string nam2 = (name.substr(loc + 3, 1) == "0") ? name.substr(loc + 4, 1) : name.substr(loc + 3, 2);
        nam = name.substr(0, loc + 1) + nam2;
      }
    }
    if (debug)
      std::cout << name << " : " << nam1 << " " << nam << std::endl;
    return nam;
  }
  
  std::string reducedName(const std::string& name, int debug) {
    std::string nam(name);
    uint32_t first = ((name.find(":") == std::string::npos) ? 0 : (name.find(":") + 1));
    uint32_t last(name.size() + 1);
    uint32_t loc(first);
    while (1) {
      if (name.find("_", loc) == std::string::npos)
        break;
      if (((loc + 5) < name.size()) && (name.substr(loc, 5) == "shape")) {
        last = loc;
        break;
      }
      loc = name.find("_", loc) + 1;
      if (loc > name.size())
        break;
    }
    nam = name.substr(first, last - first - 1);
    if ((last < name.size()) && (name.substr(name.size() - 5, 5) == "_refl"))
      nam += "_refl";
    if (debug > 0)
      std::cout << name << " col " << first << ":" << last << " " << nam << std::endl;
    return nam;
  }
  
  std::vector<std::string> splitString(const std::string& fLine) {
    std::vector<std::string> result;
    int start = 0;
    bool empty = true;
    for (unsigned i = 0; i <= fLine.size(); i++) {
      if (fLine[i] == ' ' || i == fLine.size()) {
        if (!empty) {
          std::string item(fLine, start, i - start);
          result.push_back(item);
          empty = true;
        }
        start = i + 1;
      } else {
        if (empty)
          empty = false;
      }
    }
    return result;
  }
  
  template <typename T>
  void myPrint1(std::map<std::string, T> const& obj) {
    for (auto it : obj) {
      if (it.second.occ > 1)
        std::cout << it.first << " : " << it.second.occ << std::endl;
    }
  }
  
  template <typename T>
  void myPrint2(std::map<std::string, T> const& obj1, std::map<std::string, T> const& obj2) {
    for (auto it : obj1) {
      if (obj2.find(it.first) == obj2.end())
        std::cout << it.first << " appearing " << it.second.occ << " times" << std::endl;
    }
  }
  
  void CompareFiles(const char* fileFile1, const char* fileFile2, int type, int files, int mode, int debug) {
    std::map<std::string, materials> matFile1, matFile2;
    std::map<std::string, solids> solidFile1, solidFile2;
    std::map<std::string, lvs> lvFile1, lvFile2;
    std::map<std::string, pvs> pvFile1, pvFile2;
    bool typeFile1 = ((files % 10) == 0);
    bool typeFile2 = (((files / 10) % 10) == 0);
    char buffer[100];
    std::string name;
    std::ifstream fInput1(fileFile1);
    unsigned int sizeFile1(0), sizeFile2(0);
    if (!fInput1.good()) {
      std::cout << "Cannot open file " << fileFile1 << std::endl;
    } else {
      while (fInput1.getline(buffer, 100)) {
        std::vector<std::string> items = splitString(std::string(buffer));
        if (typeFile1) {
          name = ((mode == 1) ? removeExtraName(items[0], debug) : items[0]);
        } else {
          name = reducedName(items[0], debug);
        }
        double r1 = (items.size() > 1) ? atof(items[1].c_str()) : 0;
        double r2 = (items.size() > 2) ? atof(items[2].c_str()) : 0;
        double r3 = (items.size() > 3) ? atof(items[3].c_str()) : 0;
        if (type == 0) {
          auto it = matFile1.find(name);
          if (it == matFile1.end())
            matFile1[name] = materials(1, r1, r2);
          else
            ++((it->second).occ);
        } else if (type == 1) {
          auto it = solidFile1.find(name);
          if (it == solidFile1.end())
            solidFile1[name] = solids(1, r1);
          else
            ++((it->second).occ);
        } else if (type == 2) {
          auto it = lvFile1.find(name);
          if (it == lvFile1.end())
            lvFile1[name] = lvs(1, r1);
          else
            ++((it->second).occ);
        } else {
          auto it = pvFile1.find(name);
          if (it == pvFile1.end())
            pvFile1[name] = pvs(1, r1, r2, r3);
          else
            ++((it->second).occ);
        }
      }
      fInput1.close();
      sizeFile1 = ((type == 0) ? matFile1.size()
                               : ((type == 1) ? solidFile1.size() : ((type == 2) ? lvFile1.size() : pvFile1.size())));
    }
    std::ifstream fInput2(fileFile2);
    if (!fInput2.good()) {
      std::cout << "Cannot open file " << fileFile2 << std::endl;
    } else {
      while (fInput2.getline(buffer, 100)) {
        std::vector<std::string> items = splitString(std::string(buffer));
        if (typeFile2) {
          name = ((mode == 1) ? removeExtraName(items[0], debug) : items[0]);
        } else {
          name = reducedName(items[0], debug);
        }
        double r1 = (items.size() > 1) ? atof(items[1].c_str()) : 0;
        double r2 = (items.size() > 2) ? atof(items[2].c_str()) : 0;
        double r3 = (items.size() > 3) ? atof(items[3].c_str()) : 0;
        if (type == 0) {
          auto it = matFile2.find(name);
          if (it == matFile2.end())
            matFile2[name] = materials(1, r1, r2);
          else
            ++((it->second).occ);
        } else if (type == 1) {
          auto it = solidFile2.find(name);
          if (it == solidFile2.end())
            solidFile2[name] = solids(1, r1);
          else
            ++((it->second).occ);
        } else if (type == 2) {
          auto it = lvFile2.find(name);
          if (it == lvFile2.end())
            lvFile2[name] = lvs(1, r1);
          else
            ++((it->second).occ);
        } else {
          auto it = pvFile2.find(name);
          if (it == pvFile2.end())
            pvFile2[name] = pvs(1, r1, r2, r3);
          else
            ++((it->second).occ);
        }
      }
      fInput2.close();
      sizeFile2 = ((type == 0) ? matFile2.size()
                               : ((type == 1) ? solidFile2.size() : ((type == 2) ? lvFile2.size() : pvFile2.size())));
    }
    std::cout << "Reads " << sizeFile1 << " names from " << fileFile1 << " and " << sizeFile2 << " names from "
              << fileFile2 << std::endl;
  
    std::cout << "\nMore than one entry for a given name in " << fileFile1 << std::endl;
    if (type == 0) {
      myPrint1(matFile1);
    } else if (type == 1) {
      myPrint1(solidFile1);
    } else if (type == 2) {
      myPrint1(lvFile1);
    } else {
      myPrint1(pvFile1);
    }
  
    std::cout << "\nMore than one entry for a given name in " << fileFile2 << std::endl;
    if (type == 0) {
      myPrint1(matFile2);
    } else if (type == 1) {
      myPrint1(solidFile2);
    } else if (type == 2) {
      myPrint1(lvFile2);
    } else {
      myPrint1(pvFile2);
    }
  
    std::cout << "\nEntry in " << fileFile1 << " not in " << fileFile2 << std::endl;
    if (type == 0) {
      myPrint2(matFile1, matFile2);
    } else if (type == 1) {
      myPrint2(solidFile1, solidFile2);
    } else if (type == 2) {
      myPrint2(lvFile1, lvFile2);
    } else {
      myPrint2(pvFile1, pvFile2);
    }
  
    std::cout << "\nEntry in " << fileFile2 << " not in " << fileFile1 << std::endl;
    if (type == 0) {
      myPrint2(matFile2, matFile1);
    } else if (type == 1) {
      myPrint2(solidFile2, solidFile1);
    } else if (type == 2) {
      myPrint2(lvFile2, lvFile1);
    } else {
      myPrint2(pvFile2, pvFile1);
    }
  
    //Now type specific changes
    std::cout << "\nEntries in " << fileFile1 << " and " << fileFile2 << " do not match in the content\n";
    const double denmin = 0.0001;
    int kount1(0), kount2(0);
    double difmax1(0), difmax2(0);
    std::string nameMax("");
    if (type == 0) {
      const double tol1 = 0.00001;
      for (auto it1 : matFile1) {
        auto it2 = matFile2.find(it1.first);
        if (it2 != matFile2.end()) {
          ++kount1;
          double rdif =
              0.5 * (it1.second.radl - it2->second.radl) / std::max(denmin, (it1.second.radl + it2->second.radl));
          double idif =
              0.5 * (it1.second.intl - it2->second.intl) / std::max(denmin, (it1.second.intl + it2->second.intl));
          if (std::abs(rdif) > difmax1) {
            difmax1 = std::abs(rdif);
            difmax2 = std::abs(idif);
            nameMax = it1.first;
          }
          if ((std::abs(rdif) > tol1) || (std::abs(idif) > tol1)) {
            ++kount2;
            std::cout << it1.first << " X0 " << it1.second.radl << ":" << it2->second.radl << ":" << rdif << " #L "
                      << it1.second.intl << ":" << it2->second.intl << ":" << idif << std::endl;
          }
        }
      }
      std::cout << "\n " << kount2 << " out of " << kount1 << " entries having discrpancies at the level of " << tol1
                << " or more; the maximum happens for " << nameMax << " with " << difmax1 << ":" << difmax2 << "\n";
    } else if (type == 1) {
      const double tol2 = 0.0001;
      for (auto it1 : solidFile1) {
        auto it2 = solidFile2.find(it1.first);
        if (it2 != solidFile2.end()) {
          ++kount1;
          double vdif =
              0.5 * (it1.second.volume - it2->second.volume) / std::max(denmin, (it1.second.volume + it2->second.volume));
          if (std::abs(vdif) > difmax1) {
            difmax1 = std::abs(vdif);
            nameMax = it1.first;
          }
          if (std::abs(vdif) > tol2) {
            ++kount2;
            std::cout << it1.first << " Volume " << it1.second.volume << ":" << it2->second.volume << ":" << vdif
                      << std::endl;
          }
        }
      }
      std::cout << "\n " << kount2 << " out of " << kount1 << " entries having discrpancies at the level of " << tol2
                << " or more; the maximum happens for " << nameMax << " with " << difmax1 << "\n";
    } else if (type == 2) {
      const double tol3 = 0.0001;
      for (auto it1 : lvFile1) {
        auto it2 = lvFile2.find(it1.first);
        if (it2 != lvFile2.end()) {
          ++kount1;
          double vdif =
              0.5 * (it1.second.mass - it2->second.mass) / std::max(denmin, (it1.second.mass + it2->second.mass));
          if (std::abs(vdif) > difmax1) {
            difmax1 = std::abs(vdif);
            nameMax = it1.first;
          }
          if (std::abs(vdif) > tol3) {
            ++kount2;
            std::cout << it1.first << " Mass " << it1.second.mass << ":" << it2->second.mass << ":" << vdif << std::endl;
          }
        }
      }
      std::cout << "\n " << kount2 << " out of " << kount1 << " entries having discrpancies at the level of " << tol3
                << " or more; the maximum happens for " << nameMax << " with " << difmax1 << "\n";
    } else {
      const double tol4 = 0.0001;
      for (auto it1 : pvFile1) {
        auto it2 = pvFile2.find(it1.first);
        if (it2 != pvFile2.end()) {
          ++kount1;
          double xdif = (it1.second.xx - it2->second.xx);
          double ydif = (it1.second.yy - it2->second.yy);
          double zdif = (it1.second.zz - it2->second.zz);
          double vdif = std::max(std::abs(xdif), std::abs(ydif));
          vdif = std::max(vdif, std::abs(zdif));
          if (vdif > difmax1) {
            difmax1 = vdif;
            nameMax = it1.first;
          }
          if ((std::abs(xdif) > tol4) || (std::abs(ydif) > tol4) || (std::abs(zdif) > tol4)) {
            ++kount2;
            std::cout << it1.first << " x " << it1.second.xx << ":" << it2->second.xx << ":" << xdif << " y "
                      << it1.second.yy << ":" << it2->second.yy << ":" << ydif << " z " << it1.second.zz << ":"
                      << it2->second.zz << ":" << zdif << std::endl;
          }
        }
      }
      std::cout << "\n " << kount2 << " out of " << kount1 << " entries having discrpancies at the level of " << tol4
                << " or more; the maximum happens for " << nameMax << " with " << difmax1 << "\n";
    }
  }
  
  int main(int argc, char* argv[]) {
    if (argc <= 5) {
      std::cout << "Please give a minimum of 2 arguments \n"
                << "name of the first input file\n"
                << "name of the second input file\n"
                << "type (Material:0, Solid:1, LV:2, PV:3\n"
                << "files (10 if first file from DDD and second from DD4HEP)\n"
                << "mode (treat the name for DDD or not == needed for PV)\n"
                << "debug flag (0 for minimum printout)\n"
                << std::endl;
      return 0;
    }
  
    const char* infile1 = argv[1];
    const char* infile2 = argv[2];
    int type = ((argc > 3) ? atoi(argv[3]) : 0);
    if (type < 0 || type > 3)
      type = 0;
    int files = ((argc > 4) ? atoi(argv[4]) : 10);
    int mode = ((argc > 5) ? atoi(argv[5]) : 0);
    int debug = ((argc > 6) ? atoi(argv[6]) : 0);
    CompareFiles(infile1, infile2, type, files, mode, debug);
    return 0;
  }
  