text: /**
  * UGENE - Integrated Bioinformatics Tools.
  * Copyright (C) 2008-2020 UniPro <ugene@unipro.ru>
  * http://ugene.net
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  * MA 02110-1301, USA.
  */
  
  #include "HmmerBuildTaskTest.h"
  
  #include <QDomElement>
  #include <QFile>
  
  #include <U2Core/AppContext.h>
  #include <U2Core/IOAdapterUtils.h>
  #include <U2Core/TextUtils.h>
  
  namespace U2 {
  
  /**************************
  * GTest_UHMMER3Build
  **************************/
  
  const QString GTest_UHMMER3Build::INPUT_FILE_TAG = "inputFile";
  const QString GTest_UHMMER3Build::OUTPUT_FILE_TAG = "outputFile";
  const QString GTest_UHMMER3Build::OUTPUT_DIR_TAG = "outputDir";
  const QString GTest_UHMMER3Build::DEL_OUTPUT_TAG = "delOutput";
  
  const QString GTest_UHMMER3Build::SEED_OPTION_TAG = "seed";
  
  const QString GTest_UHMMER3Build::MODEL_CONSTRUCTION_OPTION_TAG = "mc";
  const QString GTest_UHMMER3Build::RELATIVE_WEIGHTING_OPTION_TAG = "rw";
  const QString GTest_UHMMER3Build::EFFECTIVE_WEIGHTING_OPTION_TAG = "ew";
  const QString GTest_UHMMER3Build::E_VALUE_CALIBRATION_OPTION_TAG = "eval";
  
  float infinity() {
      return std::numeric_limits<float>::infinity();
  }
  
  bool isfin(float x) {
      return !qIsNaN(x) && qIsInf(x);
  }
  
  #ifdef _WINDOWS
  
  int isnan(float x) {
      return x != x;
  }
  
  int isinf(float x) {
      float inf = infinity();
      return inf == x || inf == -x;
  }
  
  #    if (!defined(_MSC_VER) || _MSC_VER < 1800)
  float roundf(float x) {
      if (isnan(x) || isinf(x)) {
          -return x;
      }
      return (float)(x >= 0.0 ? (int)(x + 0.5) : (int)(x - (int)(x - 1) + 0.5) + (int)(x - 1));
  }
  #    endif
  
  #endif    // _WINDOWS
  
  static void setSeedOption(HmmerBuildSettings &settings, TaskStateInfo &stateInfo, const QString &str) {
      if (str.isEmpty()) {
          return;
      }
  
      bool ok = false;
      int num = str.toInt(&ok);
      if (!ok || !(0 <= num)) {
          stateInfo.setError("cannot_parse_option_seed");
          return;
      }
      settings.seed = num;
  }
  
  static void setModelConstructionOption(HmmerBuildSettings &settings, TaskStateInfo &stateInfo, const QString &s) {
      QString str = s.toLower();
      if (str.startsWith("fast")) {
          settings.modelConstructionStrategy = HmmerBuildSettings::p7_ARCH_FAST;
          QString numStr = str.mid(4).trimmed();
  
          if (!numStr.isEmpty()) {
              bool ok = false;
              float num = numStr.toFloat(&ok);
              if (!ok || !(0 <= num && 1 >= num)) {
                  stateInfo.setError("cannot_parse_mc_option: symfrac");
                  return;
              }
              settings.symfrac = num;
          }
          return;
      }
      if (str.startsWith("hand")) {
          settings.modelConstructionStrategy = HmmerBuildSettings::p7_ARCH_HAND;
          return;
      }
      if (!str.isEmpty()) {
          stateInfo.setError("unrecognized_mc_option");
      }
  }
  
  static void setRelativeWeightingOption(HmmerBuildSettings &settings, TaskStateInfo &stateInfo, const QString &s) {
      QString str = s.toLower();
      if (str.startsWith("wgsc")) {
          settings.relativeSequenceWeightingStrategy = HmmerBuildSettings::p7_WGT_GSC;
          return;
      }
      if (str.startsWith("wblosum")) {
          settings.relativeSequenceWeightingStrategy = HmmerBuildSettings::p7_WGT_BLOSUM;
          QString numStr = str.mid(7).trimmed();
          if (!numStr.isEmpty()) {
              bool ok = false;
              float num = numStr.toFloat(&ok);
              if (!ok || !(0 <= num && 1 >= num)) {
                  stateInfo.setError("cannot_parse_rw_option:wid");
                  return;
              }
              settings.wid = num;
          }
          return;
      }
      if (str.startsWith("wpb")) {
          settings.relativeSequenceWeightingStrategy = HmmerBuildSettings::p7_WGT_PB;
          return;
      }
      if (str.startsWith("wnone")) {
          settings.relativeSequenceWeightingStrategy = HmmerBuildSettings::p7_WGT_NONE;
          return;
      }
      if (str.startsWith("wgiven")) {
          settings.relativeSequenceWeightingStrategy = HmmerBuildSettings::p7_WGT_GIVEN;
          return;
      }
      if (!str.isEmpty()) {
          stateInfo.setError("unrecognized_rw_option");
      }
  }
  
  static void setEffectiveWeightingOption(HmmerBuildSettings &settings, TaskStateInfo &stateInfo, const QString &s) {
      QString str = s.toLower();
      if (str.startsWith("eent")) {
          settings.effectiveSequenceWeightingStrategy = HmmerBuildSettings::p7_EFFN_ENTROPY;
          QString numStr = str.mid(4).trimmed();
  
          if (!numStr.isEmpty()) {
              QStringList nums = numStr.split(" ", QString::SkipEmptyParts);
  
              if (nums.isEmpty()) {
                  stateInfo.setError("cannot_parse_ew_option: eent");
                  return;
              }
              bool ok = false;
              double num = nums[0].toDouble(&ok);
              if (!ok) {
                  stateInfo.setError("cannot_parse_ew_option: ere");
                  return;
              }
              if (num > 0) {
                  settings.ere = num;
                  return;
              }
          }
          return;
      }
      if (str.startsWith("eclust")) {
          settings.effectiveSequenceWeightingStrategy = HmmerBuildSettings::p7_EFFN_CLUST;
          QString numStr = str.mid(6).trimmed();
  
          if (!numStr.isEmpty()) {
              bool ok = false;
              double num = numStr.toDouble(&ok);
              if (!ok || !(0 <= num && 1 >= num)) {
                  stateInfo.setError("cannot_parse_ew_option: eid");
                  return;
              }
              settings.eid = num;
          }
          return;
      }
      if (str.startsWith("enone")) {
          settings.effectiveSequenceWeightingStrategy = HmmerBuildSettings::p7_EFFN_NONE;
          return;
      }
      if (str.startsWith("eset")) {
          settings.effectiveSequenceWeightingStrategy = HmmerBuildSettings::p7_EFFN_SET;
          QString numStr = str.mid(4).trimmed();
          bool ok = false;
          double num = numStr.toDouble(&ok);
  
          if (!ok || 0 > num) {
              stateInfo.setError("cannot_parse_ew_option: eset");
              return;
          }
          settings.eset = num;
          return;
      }
      if (!str.isEmpty()) {
          stateInfo.setError("unrecognized_ew_option");
      }
  }
  
  static void setEvalueCalibrationOption(HmmerBuildSettings &settings, TaskStateInfo &stateInfo, const QString &s) {
      QString str = s.toLower();
      if (str.isEmpty()) {
          return;
      }
      QStringList l = str.split(" ", QString::SkipEmptyParts);
      if (5 != l.size()) {
          stateInfo.setError("error_parsing_eval_option");
          return;
      }
      settings.evl = l[0].toInt();
      settings.evn = l[1].toInt();
      settings.efl = l[2].toInt();
      settings.efn = l[3].toInt();
      settings.eft = l[4].toDouble();
  }
  
  void GTest_UHMMER3Build::init(XMLTestFormat *tf, const QDomElement &el) {
      Q_UNUSED(tf);
  
      inFile = el.attribute(INPUT_FILE_TAG);
      outFile = el.attribute(OUTPUT_FILE_TAG);
      outputDir = el.attribute(OUTPUT_DIR_TAG);
  
      QString delOutStr = el.attribute(DEL_OUTPUT_TAG);
      delOutFile = !delOutStr.isEmpty() && delOutStr.toLower() != "no" && delOutStr.toLower() != "n";
      setBuildSettings(bldSettings, el, stateInfo);
  }
  
  void GTest_UHMMER3Build::setBuildSettings(HmmerBuildSettings &settings, const QDomElement &el, TaskStateInfo &ti) {
      setModelConstructionOption(settings, ti, el.attribute(MODEL_CONSTRUCTION_OPTION_TAG));
      setRelativeWeightingOption(settings, ti, el.attribute(RELATIVE_WEIGHTING_OPTION_TAG));
      setEffectiveWeightingOption(settings, ti, el.attribute(EFFECTIVE_WEIGHTING_OPTION_TAG));
      setEvalueCalibrationOption(settings, ti, el.attribute(E_VALUE_CALIBRATION_OPTION_TAG));
      setSeedOption(settings, ti, el.attribute(SEED_OPTION_TAG));
  }
  
  void GTest_UHMMER3Build::setAndCheckArgs() {
      if (hasError()) {
          return;
      }
  
      if (inFile.isEmpty()) {
          stateInfo.setError(tr("No input file given"));
          return;
      }
      inFile = env->getVar("COMMON_DATA_DIR") + "/" + inFile;
  
      if (outFile.isEmpty()) {
          stateInfo.setError(tr("No output file given"));
          return;
      }
  
      outFile = env->getVar("TEMP_DATA_DIR") + "/" + outFile;
  
      bldSettings.profileUrl = outFile;
      bldSettings.workingDir = env->getVar("TEMP_DATA_DIR");
  }
  
  void GTest_UHMMER3Build::prepare() {
      setAndCheckArgs();
      if (hasError()) {
          return;
      }
  
      buildTask = new HmmerBuildTask(bldSettings, inFile);
      addSubTask(buildTask);
  }
  
  Task::ReportResult GTest_UHMMER3Build::report() {
      if (buildTask->hasError()) {
          if (delOutFile) {
              QFile::remove(outFile);
          }
          stateInfo.setError(buildTask->getError());
      }
      return ReportResult_Finished;
  }
  
  void GTest_UHMMER3Build::cleanup() {
      if (!hasError() && delOutFile) {
          QFile::remove(outFile);
      }
  
      XmlTest::cleanup();
  }
  
  /**************************
  * GTest_CompareHmmFiles
  **************************/
  
  const QString GTest_CompareHmmFiles::FILE1_NAME_TAG = "file1";
  const QString GTest_CompareHmmFiles::FILE2_NAME_TAG = "file2";
  const QString GTest_CompareHmmFiles::FILE1_TMP_TAG = "tmp1";
  const QString GTest_CompareHmmFiles::FILE2_TMP_TAG = "tmp2";
  
  const int BUF_SZ = 2048;
  const char TERM_SYM = '\0';
  
  const QByteArray DATE_STR = "DATE";
  const QByteArray NAME_STR = "NAME";
  const QByteArray HEADER_STR = "HMMER3/";
  
  void GTest_CompareHmmFiles::init(XMLTestFormat *tf, const QDomElement &el) {
      Q_UNUSED(tf);
  
      filename1 = el.attribute(FILE1_NAME_TAG);
      filename2 = el.attribute(FILE2_NAME_TAG);
  
      QString file1TmpStr = el.attribute(FILE1_TMP_TAG);
      file1Tmp = !file1TmpStr.isEmpty() && file1TmpStr.toLower() != "no" && file1TmpStr.toLower() != "n";
  
      QString file2TmpStr = el.attribute(FILE2_TMP_TAG);
      file2Tmp = !file2TmpStr.isEmpty() && file2TmpStr.toLower() != "no" && file2TmpStr.toLower() != "n";
  }
  
  void GTest_CompareHmmFiles::setAndCheckArgs() {
      if (filename1.isEmpty()) {
          stateInfo.setError(tr("File #1 not set"));
          return;
      }
      filename1 = env->getVar(file1Tmp ? "TEMP_DATA_DIR" : "COMMON_DATA_DIR") + "/" + filename1;
  
      if (filename2.isEmpty()) {
          stateInfo.setError(tr("File #2 not set"));
          return;
      }
      filename2 = env->getVar(file2Tmp ? "TEMP_DATA_DIR" : "COMMON_DATA_DIR") + "/" + filename2;
  }
  
  static const float BUILD_COMPARE_FLOAT_EPS = (float)0.00002;
  
  static bool compareStr(const QString &s1, const QString &s2) {
      assert(s1.size() == s2.size());
  
      QStringList words1 = s1.split(QRegExp("\\s+"), QString::SkipEmptyParts);
      QStringList words2 = s2.split(QRegExp("\\s+"), QString::SkipEmptyParts);
  
      if (words1.size() != words2.size()) {
          return false;
      }
  
      int sz = words1.size();
      for (int i = 0; i < sz; ++i) {
          bool ok1 = false;
          bool ok2 = false;
          float num1 = 0;
          float num2 = 0;
  
          num1 = words1.at(i).toFloat(&ok1);
          num2 = words2.at(i).toFloat(&ok2);
  
          if (ok1 != ok2) {
              return false;
          } else {
              if (ok1) {
                  if ((isfin(num1) && !isfin(num2)) || (isfin(num2) && !isfin(num1))) {
                      return false;
                  }
                  if (qAbs(num1 - num2) > BUILD_COMPARE_FLOAT_EPS) {
                      return false;
                  }
                  continue;
              } else {
                  if (words1.at(i) != words2.at(i)) {
                      return false;
                  }
                  continue;
              }
          }
      }
      return true;
  }
  
  Task::ReportResult GTest_CompareHmmFiles::report() {
      assert(!hasError());
      setAndCheckArgs();
      if (hasError()) {
          return ReportResult_Finished;
      }
  
      IOAdapterFactory *iof1 = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(IOAdapterUtils::url2io(filename1));
      QScopedPointer<IOAdapter> io1(iof1->createIOAdapter());
      if (io1.isNull()) {
          stateInfo.setError(tr("Error creating ioadapter for first file"));
          return ReportResult_Finished;
      }
      if (!io1->open(filename1, IOAdapterMode_Read)) {
          stateInfo.setError(tr("Error opening 1 file"));
          return ReportResult_Finished;
      }
  
      IOAdapterFactory *iof2 = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(IOAdapterUtils::url2io(filename2));
      QScopedPointer<IOAdapter> io2(iof2->createIOAdapter());
      if (io2.isNull()) {
          stateInfo.setError(tr("Error creating ioadapter for second file"));
          return ReportResult_Finished;
      }
      if (!io2->open(filename2, IOAdapterMode_Read)) {
          stateInfo.setError(tr("Error opening second file"));
          return ReportResult_Finished;
      }
  
      QByteArray buf1(BUF_SZ, TERM_SYM);
      QByteArray buf2(BUF_SZ, TERM_SYM);
      int bytes1 = 0;
      int bytes2 = 0;
  
      do {
          bytes1 = io1->readUntil(buf1.data(), BUF_SZ, TextUtils::LINE_BREAKS, IOAdapter::Term_Include);
          bytes2 = io2->readUntil(buf2.data(), BUF_SZ, TextUtils::LINE_BREAKS, IOAdapter::Term_Include);
          if (buf1.startsWith(DATE_STR) && buf2.startsWith(DATE_STR)) {
              continue;
          }
          if (buf1.startsWith(HEADER_STR) && buf2.startsWith(HEADER_STR)) {
              continue;
          }
          if (buf1.startsWith(NAME_STR) && buf2.startsWith(NAME_STR)) {
              QString name1 = QByteArray(buf1.data(), bytes1).mid(NAME_STR.size()).trimmed();
              QString name2 = QByteArray(buf2.data(), bytes2).mid(NAME_STR.size()).trimmed();
  
              if (name1.startsWith(name2) || name2.startsWith(name1)) {
                  continue;
              }
              stateInfo.setError(tr("Names of aligments not matched"));
              return ReportResult_Finished;
          }
          if (bytes1 != bytes2) {
              stateInfo.setError(tr("Comparing files length not matched"));
              return ReportResult_Finished;
          }
  
          QString s1 = QString::fromLatin1(buf1.data(), bytes1);
          QString s2 = QString::fromLatin1(buf2.data(), bytes2);
          if (!compareStr(s1, s2)) {
              stateInfo.setError(tr("Files parts not equal:'%1' and '%2'").arg(s1).arg(s2));
              return ReportResult_Finished;
          }
      } while (0 < bytes1 && 0 < bytes2);
  
      return ReportResult_Finished;
  }
  
  }    // namespace U2
  