<commit_before>/*!
 * Copyright (c) 2017 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
#ifndef LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_
#define LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_

#include <LightGBM/meta.h>
#include <LightGBM/metric.h>
#include <LightGBM/utils/common.h>
#include <LightGBM/utils/log.h>

#include <string>
#include <algorithm>
#include <sstream>
#include <vector>

/*
 * Implements three related metrics:
 *
 * (1) standard cross-entropy that can be used for continuous labels in [0, 1]
 * (2) "intensity-weighted" cross-entropy, also for continuous labels in [0, 1]
 * (3) Kullback-Leibler divergence, also for continuous labels in [0, 1]
 *
 * (3) adds an offset term to (1); the entropy of the label
 *
 * See xentropy_objective.hpp for further details.
 *
 */

namespace LightGBM {

  // label should be in interval [0, 1];
  // prob should be in interval (0, 1); prob is clipped if needed
  inline static double XentLoss(label_t label, double prob) {
    const double log_arg_epsilon = 1.0e-12;
    double a = label;
    if (prob > log_arg_epsilon) {
      a *= std::log(prob);
    } else {
      a *= std::log(log_arg_epsilon);
    }
    double b = 1.0f - label;
    if (1.0f - prob > log_arg_epsilon) {
      b *= std::log(1.0f - prob);
    } else {
      b *= std::log(log_arg_epsilon);
    }
    return - (a + b);
  }

  // hhat >(=) 0 assumed; and weight > 0 required; but not checked here
  inline static double XentLambdaLoss(label_t label, label_t weight, double hhat) {
    return XentLoss(label, 1.0f - std::exp(-weight * hhat));
  }

  // Computes the (negative) entropy for label p; p should be in interval [0, 1];
  // This is used to presum the KL-divergence offset term (to be _added_ to the cross-entropy loss).
  // NOTE: x*log(x) = 0 for x=0,1; so only add when in (0, 1); avoid log(0)*0
  inline static double YentLoss(double p) {
    double hp = 0.0;
    if (p > 0) hp += p * std::log(p);
    double q = 1.0f - p;
    if (q > 0) hp += q * std::log(q);
    return hp;
  }

//
// CrossEntropyMetric : "xentropy" : (optional) weights are used linearly
//
class CrossEntropyMetric : public Metric {
 public:
  explicit CrossEntropyMetric(const Config&) {}
  virtual ~CrossEntropyMetric() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("cross_entropy");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);

    // ensure that labels are in interval [0, 1], interval ends included
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    // check that weights are non-negative and sum is positive
    if (weights_ == nullptr) {
      sum_weights_ = static_cast<double>(num_data_);
    } else {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), &sum_weights_);
      if (minw < 0.0f) {
        Log::Fatal("[%s:%s]: (metric) weights not allowed to be negative", GetName()[0].c_str(), __func__);
      }
    }

    // check weight sum (may fail to be zero)
    if (sum_weights_ <= 0.0f) {
      Log::Fatal("[%s:%s]: sum-of-weights = %f is non-positive", __func__, GetName()[0].c_str(), sum_weights_);
    }
    Log::Info("[%s:%s]: sum-of-weights = %f", GetName()[0].c_str(), __func__, sum_weights_);
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]);  // NOTE: does not work unless score is a probability
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]) * weights_[i];  // NOTE: does not work unless score is a probability
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p) * weights_[i];
        }
      }
    }
    double loss = sum_loss / sum_weights_;
    return std::vector<double>(1, loss);
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;  // negative means smaller loss is better, positive means larger loss is better
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Sum of weights */
  double sum_weights_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

//
// CrossEntropyLambdaMetric : "xentlambda" : (optional) weights have a different meaning than for "xentropy"
// ATTENTION: Supposed to be used when the objective also is "xentlambda"
//
class CrossEntropyLambdaMetric : public Metric {
 public:
  explicit CrossEntropyLambdaMetric(const Config&) {}
  virtual ~CrossEntropyLambdaMetric() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("cross_entropy_lambda");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    // check all weights are strictly positive; throw error if not
    if (weights_ != nullptr) {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), static_cast<label_t*>(nullptr));
      if (minw <= 0.0f) {
        Log::Fatal("[%s:%s]: (metric) all weights must be positive", GetName()[0].c_str(), __func__);
      }
    }
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = std::log(1.0f + std::exp(score[i]));  // auto-convert
          sum_loss += XentLambdaLoss(label_[i], 1.0f, hhat);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = std::log(1.0f + std::exp(score[i]));  // auto-convert
          sum_loss += XentLambdaLoss(label_[i], weights_[i], hhat);
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = 0;
          objective->ConvertOutput(&score[i], &hhat);  // NOTE: this only works if objective = "xentlambda"
          sum_loss += XentLambdaLoss(label_[i], 1.0f, hhat);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = 0;
          objective->ConvertOutput(&score[i], &hhat);  // NOTE: this only works if objective = "xentlambda"
          sum_loss += XentLambdaLoss(label_[i], weights_[i], hhat);
        }
      }
    }
    return std::vector<double>(1, sum_loss / static_cast<double>(num_data_));
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

//
// KullbackLeiblerDivergence : "kldiv" : (optional) weights are used linearly
//
class KullbackLeiblerDivergence : public Metric {
 public:
  explicit KullbackLeiblerDivergence(const Config&) {}
  virtual ~KullbackLeiblerDivergence() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("kullback_leibler");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    if (weights_ == nullptr) {
      sum_weights_ = static_cast<double>(num_data_);
    } else {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), &sum_weights_);
      if (minw < 0.0f) {
        Log::Fatal("[%s:%s]: (metric) at least one weight is negative", GetName()[0].c_str(), __func__);
      }
    }

    // check weight sum
    if (sum_weights_ <= 0.0f) {
      Log::Fatal("[%s:%s]: sum-of-weights = %f is non-positive", GetName()[0].c_str(), __func__, sum_weights_);
    }

    Log::Info("[%s:%s]: sum-of-weights = %f", GetName()[0].c_str(), __func__, sum_weights_);

    // evaluate offset term
    presum_label_entropy_ = 0.0f;
    if (weights_ == nullptr) {
    //  #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data; ++i) {
        presum_label_entropy_ += YentLoss(label_[i]);
      }
    } else {
    //  #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data; ++i) {
        presum_label_entropy_ += YentLoss(label_[i]) * weights_[i];
      }
    }
    presum_label_entropy_ /= sum_weights_;

    // communicate the value of the offset term to be added
    Log::Info("%s offset term = %f", GetName()[0].c_str(), presum_label_entropy_);
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]);  // NOTE: does not work unless score is a probability
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]) * weights_[i];  // NOTE: does not work unless score is a probability
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p) * weights_[i];
        }
      }
    }
    double loss = presum_label_entropy_ + sum_loss / sum_weights_;
    return std::vector<double>(1, loss);
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Sum of weights */
  double sum_weights_;
  /*! \brief Offset term to cross-entropy; precomputed during init */
  double presum_label_entropy_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

}  // end namespace LightGBM

#endif  // end #ifndef LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_
<commit_msg>remove commented-out code in cross-entropy metric source (#3999)<commit_after>/*!
 * Copyright (c) 2017 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
#ifndef LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_
#define LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_

#include <LightGBM/meta.h>
#include <LightGBM/metric.h>
#include <LightGBM/utils/common.h>
#include <LightGBM/utils/log.h>

#include <string>
#include <algorithm>
#include <sstream>
#include <vector>

/*
 * Implements three related metrics:
 *
 * (1) standard cross-entropy that can be used for continuous labels in [0, 1]
 * (2) "intensity-weighted" cross-entropy, also for continuous labels in [0, 1]
 * (3) Kullback-Leibler divergence, also for continuous labels in [0, 1]
 *
 * (3) adds an offset term to (1); the entropy of the label
 *
 * See xentropy_objective.hpp for further details.
 *
 */

namespace LightGBM {

  // label should be in interval [0, 1];
  // prob should be in interval (0, 1); prob is clipped if needed
  inline static double XentLoss(label_t label, double prob) {
    const double log_arg_epsilon = 1.0e-12;
    double a = label;
    if (prob > log_arg_epsilon) {
      a *= std::log(prob);
    } else {
      a *= std::log(log_arg_epsilon);
    }
    double b = 1.0f - label;
    if (1.0f - prob > log_arg_epsilon) {
      b *= std::log(1.0f - prob);
    } else {
      b *= std::log(log_arg_epsilon);
    }
    return - (a + b);
  }

  // hhat >(=) 0 assumed; and weight > 0 required; but not checked here
  inline static double XentLambdaLoss(label_t label, label_t weight, double hhat) {
    return XentLoss(label, 1.0f - std::exp(-weight * hhat));
  }

  // Computes the (negative) entropy for label p; p should be in interval [0, 1];
  // This is used to presum the KL-divergence offset term (to be _added_ to the cross-entropy loss).
  // NOTE: x*log(x) = 0 for x=0,1; so only add when in (0, 1); avoid log(0)*0
  inline static double YentLoss(double p) {
    double hp = 0.0;
    if (p > 0) hp += p * std::log(p);
    double q = 1.0f - p;
    if (q > 0) hp += q * std::log(q);
    return hp;
  }

//
// CrossEntropyMetric : "xentropy" : (optional) weights are used linearly
//
class CrossEntropyMetric : public Metric {
 public:
  explicit CrossEntropyMetric(const Config&) {}
  virtual ~CrossEntropyMetric() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("cross_entropy");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);

    // ensure that labels are in interval [0, 1], interval ends included
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    // check that weights are non-negative and sum is positive
    if (weights_ == nullptr) {
      sum_weights_ = static_cast<double>(num_data_);
    } else {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), &sum_weights_);
      if (minw < 0.0f) {
        Log::Fatal("[%s:%s]: (metric) weights not allowed to be negative", GetName()[0].c_str(), __func__);
      }
    }

    // check weight sum (may fail to be zero)
    if (sum_weights_ <= 0.0f) {
      Log::Fatal("[%s:%s]: sum-of-weights = %f is non-positive", __func__, GetName()[0].c_str(), sum_weights_);
    }
    Log::Info("[%s:%s]: sum-of-weights = %f", GetName()[0].c_str(), __func__, sum_weights_);
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]);  // NOTE: does not work unless score is a probability
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]) * weights_[i];  // NOTE: does not work unless score is a probability
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p) * weights_[i];
        }
      }
    }
    double loss = sum_loss / sum_weights_;
    return std::vector<double>(1, loss);
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;  // negative means smaller loss is better, positive means larger loss is better
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Sum of weights */
  double sum_weights_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

//
// CrossEntropyLambdaMetric : "xentlambda" : (optional) weights have a different meaning than for "xentropy"
// ATTENTION: Supposed to be used when the objective also is "xentlambda"
//
class CrossEntropyLambdaMetric : public Metric {
 public:
  explicit CrossEntropyLambdaMetric(const Config&) {}
  virtual ~CrossEntropyLambdaMetric() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("cross_entropy_lambda");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    // check all weights are strictly positive; throw error if not
    if (weights_ != nullptr) {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), static_cast<label_t*>(nullptr));
      if (minw <= 0.0f) {
        Log::Fatal("[%s:%s]: (metric) all weights must be positive", GetName()[0].c_str(), __func__);
      }
    }
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = std::log(1.0f + std::exp(score[i]));  // auto-convert
          sum_loss += XentLambdaLoss(label_[i], 1.0f, hhat);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = std::log(1.0f + std::exp(score[i]));  // auto-convert
          sum_loss += XentLambdaLoss(label_[i], weights_[i], hhat);
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = 0;
          objective->ConvertOutput(&score[i], &hhat);  // NOTE: this only works if objective = "xentlambda"
          sum_loss += XentLambdaLoss(label_[i], 1.0f, hhat);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double hhat = 0;
          objective->ConvertOutput(&score[i], &hhat);  // NOTE: this only works if objective = "xentlambda"
          sum_loss += XentLambdaLoss(label_[i], weights_[i], hhat);
        }
      }
    }
    return std::vector<double>(1, sum_loss / static_cast<double>(num_data_));
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

//
// KullbackLeiblerDivergence : "kldiv" : (optional) weights are used linearly
//
class KullbackLeiblerDivergence : public Metric {
 public:
  explicit KullbackLeiblerDivergence(const Config&) {}
  virtual ~KullbackLeiblerDivergence() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back("kullback_leibler");
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();

    CHECK_NOTNULL(label_);
    Common::CheckElementsIntervalClosed<label_t>(label_, 0.0f, 1.0f, num_data_, GetName()[0].c_str());
    Log::Info("[%s:%s]: (metric) labels passed interval [0, 1] check",  GetName()[0].c_str(), __func__);

    if (weights_ == nullptr) {
      sum_weights_ = static_cast<double>(num_data_);
    } else {
      label_t minw;
      Common::ObtainMinMaxSum(weights_, num_data_, &minw, static_cast<label_t*>(nullptr), &sum_weights_);
      if (minw < 0.0f) {
        Log::Fatal("[%s:%s]: (metric) at least one weight is negative", GetName()[0].c_str(), __func__);
      }
    }

    // check weight sum
    if (sum_weights_ <= 0.0f) {
      Log::Fatal("[%s:%s]: sum-of-weights = %f is non-positive", GetName()[0].c_str(), __func__, sum_weights_);
    }

    Log::Info("[%s:%s]: sum-of-weights = %f", GetName()[0].c_str(), __func__, sum_weights_);

    // evaluate offset term
    presum_label_entropy_ = 0.0f;
    if (weights_ == nullptr) {
      for (data_size_t i = 0; i < num_data; ++i) {
        presum_label_entropy_ += YentLoss(label_[i]);
      }
    } else {
      for (data_size_t i = 0; i < num_data; ++i) {
        presum_label_entropy_ += YentLoss(label_[i]) * weights_[i];
      }
    }
    presum_label_entropy_ /= sum_weights_;

    // communicate the value of the offset term to be added
    Log::Info("%s offset term = %f", GetName()[0].c_str(), presum_label_entropy_);
  }

  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
    double sum_loss = 0.0f;
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]);  // NOTE: does not work unless score is a probability
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          sum_loss += XentLoss(label_[i], score[i]) * weights_[i];  // NOTE: does not work unless score is a probability
        }
      }
    } else {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p);
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          double p = 0;
          objective->ConvertOutput(&score[i], &p);
          sum_loss += XentLoss(label_[i], p) * weights_[i];
        }
      }
    }
    double loss = presum_label_entropy_ + sum_loss / sum_weights_;
    return std::vector<double>(1, loss);
  }

  const std::vector<std::string>& GetName() const override {
    return name_;
  }

  double factor_to_bigger_better() const override {
    return -1.0f;
  }

 private:
  /*! \brief Number of data points */
  data_size_t num_data_;
  /*! \brief Pointer to label */
  const label_t* label_;
  /*! \brief Pointer to weights */
  const label_t* weights_;
  /*! \brief Sum of weights */
  double sum_weights_;
  /*! \brief Offset term to cross-entropy; precomputed during init */
  double presum_label_entropy_;
  /*! \brief Name of this metric */
  std::vector<std::string> name_;
};

}  // end namespace LightGBM

#endif  // end #ifndef LIGHTGBM_METRIC_XENTROPY_METRIC_HPP_
<|endoftext|>


<commit_before>#include "GPU.H"

#include "CAMERA.H"
#include "LOAD_LEV.H"
#include "PROFILE.H"

#include <assert.h>
#include <LIBGPU.H>

unsigned long GnFrameCounter = 19;
unsigned long GnLastFrameCount = 19;
struct PSXTEXTSTRUCT* psxtextinfo;
struct PSXSPRITESTRUCT* psxspriteinfo;
int rgbscaleme = 256;
int gfx_debugging_mode;
struct DB_STRUCT db;
struct MMTEXTURE* RoomTextInfo;
unsigned long GadwOrderingTables_V2[512];
static int LnFlipFrame;
unsigned long GadwOrderingTables[5128];
unsigned long GadwPolygonBuffers[52260];

void GPU_UseOrderingTables(unsigned long* pBuffers, int nOTSize)//5DF68(<), 5F1C8(<)
{
	db.order_table[0] = &pBuffers[0];
	db.order_table[1] = &pBuffers[nOTSize];
	db.nOTSize = nOTSize;
	db.pickup_order_table[0] = &db.disp[1];
	db.pickup_order_table[1] = &GadwOrderingTables_V2[256];
#if 0
	//Should be safe to use 32-bit ptrs tho
	db.order_table[0] = (unsigned long*)((unsigned long) pBuffers & 0xFFFFFF);
	db.order_table[1] = (unsigned long*)((unsigned long) &pBuffers[nOTSize] & 0xFFFFFF);
	db.nOTSize = nOTSize;
	db.pickup_order_table[0] = (unsigned long*)((unsigned long)&db.disp[1] & 0xFFFFFF);
	db.pickup_order_table[1] = (unsigned long*)((unsigned long)&GadwOrderingTables_V2[256] & 0xFFFFFF);
#endif
	return;
}

void GPU_UsePolygonBuffers(unsigned long* pBuffers, int nPBSize)//5DFB0(<),
{
#if 1
	db.nPBSize = nPBSize;
	db.poly_buffer[0] = &pBuffers[0];
	db.poly_buffer[1] = &pBuffers[nPBSize];
#else
	db.nPBSize = nPBSize;
	db.poly_buffer[0] = (unsigned long*)((unsigned long)pBuffers & 0xFFFFFF);
	db.poly_buffer[1] = (unsigned long*)((unsigned long)&pBuffers[nPBSize] & 0xFFFFFF);
#endif
	return;
}

void GPU_SyncBothScreens()//5F374(<), 60054(<)
{
	DrawSync(0);

	db.current_buffer ^= 1;
	if (db.current_buffer != 0)
	{
		MoveImage(&db.disp[1].disp, db.disp[0].disp.x, db.disp[0].disp.y);
		//TODO: Verify ra += 0x10;! prolly else skip loc_5F3A8 (implemented but should be checked).
	}
	else
	{
		//loc_5F3A8
		MoveImage(&db.disp[0].disp, db.disp[1].disp.x, db.disp[1].disp.y);
	}

	DrawSync(0);

	return;
}

void GPU_BeginScene()//5F0F0(<), 5FDD0(<)
{
	db.ot = db.order_table[db.current_buffer];
	db.polyptr = (char*)db.poly_buffer[db.current_buffer];
	db.curpolybuf = (char*)db.poly_buffer[db.current_buffer];
	db.polybuf_limit = (char*)(db.poly_buffer[db.current_buffer]) + 26000;
	db.pickup_ot = db.pickup_order_table[db.current_buffer];
	ClearOTagR(db.order_table[db.current_buffer], db.nOTSize);

	return;
}

int GPU_FlipNoIdle()//5E078(<), 5F264(<)
{
#if INTERNAL
	if (ProfileDraw)
	{
		ProfileRGB(255, 255, 255);
		ProfileAddOT(&db.ot[0]);
	}//loc_5E0B0
#endif

	DrawSync(0);//TODO confirm retail is sub_6B144 draw sync

#if _INTERNAL
	if (ProfileDraw)
	{
		ProfileAddDrawOT(&db.ot[0]);
	}//loc_5E0D8
#endif

	LnFlipFrame = GnLastFrameCount;

	if (GnLastFrameCount < 2)
	{
		//loc_5E0F4
		do
		{
			VSync(0);
			LnFlipFrame++;
		} while (LnFlipFrame < 2);
	}
	else
	{
		//loc_5E120
		VSync(0);
		LnFlipFrame++;
	}

	//loc_5E138
	//v0 = db.current_buffer;
	GnLastFrameCount = 0;
	PutDispEnv(&db.disp[db.current_buffer]);
	DrawOTagEnv(&db.ot[db.nOTSize - 1], &db.draw[db.current_buffer]);

#if INTERNAL
	ProfileStartCount();
#endif

	db.current_buffer ^= 1;

	return LnFlipFrame;
}

void GPU_GetScreenPosition(short* x, short* y)//5F34C, ?
{
	*x = db.disp[0].screen.x;
	*y = db.disp[0].screen.y;
	return;
}

/*   PSX VRAM   (H)
 *  ----------- 512px
 *  | TL | TR |  |
 *  -----------  v
 *  | BL | BR |
 *  -----------
 *(W)1024px-->
 *
 */
void GPU_ClearVRAM()//5F2D0(<), 5FFB0(<) (F)
{
	RECT r;

	DrawSync(0);
	VSync(0);

	//Clear TL
	r.x = 0;
	r.y = 0;
	r.w = 512;
	r.h = 256;
	clear_a_rect(&r);

	//Clear BL
	r.y = 256;
	clear_a_rect(&r);

	//Clear BR
	r.x = 512;
	clear_a_rect(&r);

	//Clear TR
	r.y = 0;
	clear_a_rect(&r);

	DrawSync(0);
	VSync(0);

	return;
}

void clear_a_rect(RECT* r)//5F334(<), 60014(<) (F)
{
	ClearImage(r, 0, 48, 0);

	return;
}

//@Gh0stblade - Not sure why this is so unoptimal, we can basically &disp[db.current_buffer]... double check code.
void GPU_FlipToBuffer(int buffer_index)//5F3C8(<), 600A8(<) (F)
{
	DrawSync(0);
	VSync(0);

	buffer_index &= 1;

	if (buffer_index)
	{
		PutDispEnv(&db.disp[1]);
		db.current_buffer = buffer_index ^ 1;
		PutDrawEnv(&db.draw[1]);
	}
	else
	{
		PutDispEnv(&db.disp[0]);
		db.current_buffer = buffer_index ^ 1;
		PutDrawEnv(&db.draw[0]);
	}

	return;
}

void GPU_EndScene()//5DFDC(<), 5F23C(!)
{
#if 0
	//int nPolys;
	//static int nWorstPolys;

	lui	$v0, 0x4EC4
	int a0 = &db.polyptr[0];
	int v1 = &db.curpolybuf[0];
	int v0 = 0x4EC4EC4F;
	a0 -= v1;
	v0 = a0 * v0;
	a0 >>= 31;
	v1 = psxtextinfo->u2v2pad;
	v0 >>= 4;
	v0 -= a0;

	if (v1 < v0)
	{
		u2v2pad = v0;
	}//loc_5E020

	//loc_5E020
#endif

	OptimiseOTagR(&db.ot[0], db.nOTSize);

#if 0
	nop

	ProfileRGB(-1, -1, -1);

	a0 = db.nOTSize;
	do_gfx_debug_mode(&db.ot[db.nOTSize - 1]);

	ProfileRGB(0, -1, -1);
#endif
	return;
}

long OptimiseOTagR(unsigned long* ot, int nOTSize)//86CC4(<), 88D08(<)
{
	unsigned long* a1;
	unsigned long* a3;
	unsigned long v0;
	unsigned long* v1;
	long at = 0;

	if (nOTSize < 8)
	{
		return 0;
	}

	a1 = &ot[nOTSize - 1];
	v1 = &ot[1];
	v0 = a1[0];

	//loc_86CE4
	do
	{
		a1--;

		if ((unsigned long*)v0 == a1)
		{
			a3 = a1 - 1;
			if (a1 == ot)
			{
				return at;
			}

			//loc_86CF4
			do
			{
				v0 = a1[0];
				a1--;
				if (a1 == v1)
				{
					break;
				}

				at++;
			} while ((unsigned long*) v0 == a1);

			//loc_86D08
			a3[0] = v0;
		}
		v0 = a1[0];
	} while (a1 != ot);

	//loc_86D0C
	return at;
}

void draw_rotate_sprite(long a0, long a1, long a2)//5F134, 5FE14
{
#if 1
	long t0;
	short* v0;
	long t6;
	long t5;
	long* a33;
	long t2;
	long at;
	long* t3;
	long v1;
	long t1;
	long t4;

	long v00;

	t0 = (DelRotAng - 52) & 0xFFF;
	a2 = -a2;
	v0 = &rcossin_tbl[t0 * 2];
	t6 = v0[0];
	a2 >>= 1;
	t6 = a2 * t6;//

	t3 = &db.ot[0];
	t5 = v0[1];
	a33 = db.polyptr;

	t2 = 0x2C808080;
	t5 = a2 * t5;

	at = 0x1303F00;
	DelRotAng = t0;
	*(long*) &db.polyptr[4] = t2;
	*(long*) &db.polyptr[12] = 0;
	*(long*) &db.polyptr[20] = at;

	t6 >>= 12;
	t5 >>= 12;

	t0 = t6 - t5;
	a2 = -t6;
	t4 = a2 - t5;
	a2 += t5;
	t1 = t6 + t5;

	v00 = t0 >> 1;
	v00 += a0;
	t0 += v00;

	v1 = t4 >> 1;
	v1 += a0;
	t4 += v1;

	v00 = a2 >> 1;
	v00 += a0;
	a2 += v00;

	v1 = t1 >> 1;
	v1 += a0;
	t1 += v1;

	v00 = t5 + t6;
	v00 += a1;
	v1 = -t5;

	*(short*) &db.polyptr[8] = t0;
	*(short*) &db.polyptr[10] = v00;

	v00 = v1 + t6;
	v00 += a1;

	*(short*) &db.polyptr[16] = t4;
	*(short*) &db.polyptr[18] = v00;

	v00 = t5 - t6;
	v00 += a1;
	v1 -= t6;

	*(short*) &db.polyptr[24] = t1;
	*(short*) &db.polyptr[26] = v00;

	t4 = 0x3F;//width/height?

	*(short*) &db.polyptr[28] = t4;

	t4 = 0x3F3F;
	*(short*) &db.polyptr[36] = t4;

	a1 += v1;

	*(short*) &db.polyptr[32] = a2;
	*(short*) &db.polyptr[34] = a1;//Verified

	v00 = db.ot[0];
	at = 0x09000000;
	v00 |= at;

	db.ot[0] = &db.polyptr[0];
	*(int*) &db.polyptr[0] = v00;

	db.polyptr += 0x28;//sizeof(POLY_F3); * 2

	v00 = 0x780100;
	v1 = 0x6800;
	a0 = 0x7801FF;

	*(long*) &db.polyptr[4] = t2;
	*(long*) &db.polyptr[8] = v00;
	*(long*) &db.polyptr[12] = v1;
	*(long*) &db.polyptr[16] = a0;

	at = 0x13468FF;
	v00 = 0xEF0100;
	v1 = 0xDF00;
	a0 = 0xEF01FF;

	*(long*) &db.polyptr[20] = at;
	*(long*) &db.polyptr[24] = v00;
	*(short*) &db.polyptr[28] = v1;
	*(long*) &db.polyptr[32] = a0;

	at = 0xDFFF;
	*(short*) &db.polyptr[36] = at;

	v00 = t3[0];
	v00 |= 0x9000000;

	t3[0] = db.polyptr;
	*(long*) &db.polyptr[0] = v00;
	db.polyptr += 0x28;
#endif
	return;
}

void GPU_SetScreenPosition(short x, short y)//5F360(<), 60040(<)
{
	db.disp[0].screen.x = x;
	db.disp[0].screen.y = y;
	db.disp[1].screen.x = x;
	db.disp[1].screen.y = y;

	return;
}<commit_msg>draw_rotate_sprite Clean<commit_after>#include "GPU.H"

#include "CAMERA.H"
#include "LOAD_LEV.H"
#include "PROFILE.H"

#include <assert.h>
#include <LIBGPU.H>

unsigned long GnFrameCounter = 19;
unsigned long GnLastFrameCount = 19;
struct PSXTEXTSTRUCT* psxtextinfo;
struct PSXSPRITESTRUCT* psxspriteinfo;
int rgbscaleme = 256;
int gfx_debugging_mode;
struct DB_STRUCT db;
struct MMTEXTURE* RoomTextInfo;
unsigned long GadwOrderingTables_V2[512];
static int LnFlipFrame;
unsigned long GadwOrderingTables[5128];
unsigned long GadwPolygonBuffers[52260];

void GPU_UseOrderingTables(unsigned long* pBuffers, int nOTSize)//5DF68(<), 5F1C8(<)
{
	db.order_table[0] = &pBuffers[0];
	db.order_table[1] = &pBuffers[nOTSize];
	db.nOTSize = nOTSize;
	db.pickup_order_table[0] = &db.disp[1];
	db.pickup_order_table[1] = &GadwOrderingTables_V2[256];
#if 0
	//Should be safe to use 32-bit ptrs tho
	db.order_table[0] = (unsigned long*)((unsigned long) pBuffers & 0xFFFFFF);
	db.order_table[1] = (unsigned long*)((unsigned long) &pBuffers[nOTSize] & 0xFFFFFF);
	db.nOTSize = nOTSize;
	db.pickup_order_table[0] = (unsigned long*)((unsigned long)&db.disp[1] & 0xFFFFFF);
	db.pickup_order_table[1] = (unsigned long*)((unsigned long)&GadwOrderingTables_V2[256] & 0xFFFFFF);
#endif
	return;
}

void GPU_UsePolygonBuffers(unsigned long* pBuffers, int nPBSize)//5DFB0(<),
{
#if 1
	db.nPBSize = nPBSize;
	db.poly_buffer[0] = &pBuffers[0];
	db.poly_buffer[1] = &pBuffers[nPBSize];
#else
	db.nPBSize = nPBSize;
	db.poly_buffer[0] = (unsigned long*)((unsigned long)pBuffers & 0xFFFFFF);
	db.poly_buffer[1] = (unsigned long*)((unsigned long)&pBuffers[nPBSize] & 0xFFFFFF);
#endif
	return;
}

void GPU_SyncBothScreens()//5F374(<), 60054(<)
{
	DrawSync(0);

	db.current_buffer ^= 1;
	if (db.current_buffer != 0)
	{
		MoveImage(&db.disp[1].disp, db.disp[0].disp.x, db.disp[0].disp.y);
		//TODO: Verify ra += 0x10;! prolly else skip loc_5F3A8 (implemented but should be checked).
	}
	else
	{
		//loc_5F3A8
		MoveImage(&db.disp[0].disp, db.disp[1].disp.x, db.disp[1].disp.y);
	}

	DrawSync(0);

	return;
}

void GPU_BeginScene()//5F0F0(<), 5FDD0(<)
{
	db.ot = db.order_table[db.current_buffer];
	db.polyptr = (char*)db.poly_buffer[db.current_buffer];
	db.curpolybuf = (char*)db.poly_buffer[db.current_buffer];
	db.polybuf_limit = (char*)(db.poly_buffer[db.current_buffer]) + 26000;
	db.pickup_ot = db.pickup_order_table[db.current_buffer];
	ClearOTagR(db.order_table[db.current_buffer], db.nOTSize);

	return;
}

int GPU_FlipNoIdle()//5E078(<), 5F264(<)
{
#if INTERNAL
	if (ProfileDraw)
	{
		ProfileRGB(255, 255, 255);
		ProfileAddOT(&db.ot[0]);
	}//loc_5E0B0
#endif

	DrawSync(0);//TODO confirm retail is sub_6B144 draw sync

#if _INTERNAL
	if (ProfileDraw)
	{
		ProfileAddDrawOT(&db.ot[0]);
	}//loc_5E0D8
#endif

	LnFlipFrame = GnLastFrameCount;

	if (GnLastFrameCount < 2)
	{
		//loc_5E0F4
		do
		{
			VSync(0);
			LnFlipFrame++;
		} while (LnFlipFrame < 2);
	}
	else
	{
		//loc_5E120
		VSync(0);
		LnFlipFrame++;
	}

	//loc_5E138
	//v0 = db.current_buffer;
	GnLastFrameCount = 0;
	PutDispEnv(&db.disp[db.current_buffer]);
	DrawOTagEnv(&db.ot[db.nOTSize - 1], &db.draw[db.current_buffer]);

#if INTERNAL
	ProfileStartCount();
#endif

	db.current_buffer ^= 1;

	return LnFlipFrame;
}

void GPU_GetScreenPosition(short* x, short* y)//5F34C, ?
{
	*x = db.disp[0].screen.x;
	*y = db.disp[0].screen.y;
	return;
}

/*   PSX VRAM   (H)
 *  ----------- 512px
 *  | TL | TR |  |
 *  -----------  v
 *  | BL | BR |
 *  -----------
 *(W)1024px-->
 *
 */
void GPU_ClearVRAM()//5F2D0(<), 5FFB0(<) (F)
{
	RECT r;

	DrawSync(0);
	VSync(0);

	//Clear TL
	r.x = 0;
	r.y = 0;
	r.w = 512;
	r.h = 256;
	clear_a_rect(&r);

	//Clear BL
	r.y = 256;
	clear_a_rect(&r);

	//Clear BR
	r.x = 512;
	clear_a_rect(&r);

	//Clear TR
	r.y = 0;
	clear_a_rect(&r);

	DrawSync(0);
	VSync(0);

	return;
}

void clear_a_rect(RECT* r)//5F334(<), 60014(<) (F)
{
	ClearImage(r, 0, 48, 0);

	return;
}

//@Gh0stblade - Not sure why this is so unoptimal, we can basically &disp[db.current_buffer]... double check code.
void GPU_FlipToBuffer(int buffer_index)//5F3C8(<), 600A8(<) (F)
{
	DrawSync(0);
	VSync(0);

	buffer_index &= 1;

	if (buffer_index)
	{
		PutDispEnv(&db.disp[1]);
		db.current_buffer = buffer_index ^ 1;
		PutDrawEnv(&db.draw[1]);
	}
	else
	{
		PutDispEnv(&db.disp[0]);
		db.current_buffer = buffer_index ^ 1;
		PutDrawEnv(&db.draw[0]);
	}

	return;
}

void GPU_EndScene()//5DFDC(<), 5F23C(!)
{
#if 0
	//int nPolys;
	//static int nWorstPolys;

	lui	$v0, 0x4EC4
	int a0 = &db.polyptr[0];
	int v1 = &db.curpolybuf[0];
	int v0 = 0x4EC4EC4F;
	a0 -= v1;
	v0 = a0 * v0;
	a0 >>= 31;
	v1 = psxtextinfo->u2v2pad;
	v0 >>= 4;
	v0 -= a0;

	if (v1 < v0)
	{
		u2v2pad = v0;
	}//loc_5E020

	//loc_5E020
#endif

	OptimiseOTagR(&db.ot[0], db.nOTSize);

#if 0
	nop

	ProfileRGB(-1, -1, -1);

	a0 = db.nOTSize;
	do_gfx_debug_mode(&db.ot[db.nOTSize - 1]);

	ProfileRGB(0, -1, -1);
#endif
	return;
}

long OptimiseOTagR(unsigned long* ot, int nOTSize)//86CC4(<), 88D08(<)
{
	unsigned long* a1;
	unsigned long* a3;
	unsigned long v0;
	unsigned long* v1;
	long at = 0;

	if (nOTSize < 8)
	{
		return 0;
	}

	a1 = &ot[nOTSize - 1];
	v1 = &ot[1];
	v0 = a1[0];

	//loc_86CE4
	do
	{
		a1--;

		if ((unsigned long*)v0 == a1)
		{
			a3 = a1 - 1;
			if (a1 == ot)
			{
				return at;
			}

			//loc_86CF4
			do
			{
				v0 = a1[0];
				a1--;
				if (a1 == v1)
				{
					break;
				}

				at++;
			} while ((unsigned long*) v0 == a1);

			//loc_86D08
			a3[0] = v0;
		}
		v0 = a1[0];
	} while (a1 != ot);

	//loc_86D0C
	return at;
}

void draw_rotate_sprite(long a0, long a1, long a2)//5F134, 5FE14
{
	long t0;
	short* r_cossinptr;
	long t6;
	long t5;
	long t1;
	long t4;

	DelRotAng = (DelRotAng - 52) & 0xFFF;
	r_cossinptr = &rcossin_tbl[DelRotAng * 2];

	t6 = ((-a2 / 2) * r_cossinptr[0]) / 4096;
	t5 = ((-a2 / 2) * r_cossinptr[1]) / 4096;

	*(long*) &db.polyptr[4] = 0x2C808080;
	*(long*) &db.polyptr[12] = 0;
	*(long*) &db.polyptr[20] = 0x1303F00;

	t0 = t6 - t5;
	a2 = -t6;
	t4 = a2 - t5;
	a2 += t5;
	t1 = t6 + t5;

	*(short*) &db.polyptr[8] = t0 + (t0 / 2) + a0;
	*(short*) &db.polyptr[10] = t5 + t6 + a1;

	*(short*) &db.polyptr[16] = t4 + (t4 / 2) + a0;
	*(short*) &db.polyptr[18] = -t5 + t6 + a1;

	*(short*) &db.polyptr[24] = t1 + (t1 / 2) + a0;
	*(short*) &db.polyptr[26] = (t5 - t6) + a1;

	*(short*) &db.polyptr[28] = 0x3F;//width/height of loading cd?
	*(short*) &db.polyptr[36] = 0x3F3F;

	*(short*) &db.polyptr[32] = a2 + (a2 / 2) + a0;
	*(short*) &db.polyptr[34] = a1 + (-t5 - t6);

	*(long*) &db.polyptr[0] = db.ot[0] | 0x09000000;
	db.ot[0] = &db.polyptr[0];

	db.polyptr += 0x28;//sizeof(POLY_F3); * 2?

	*(long*) &db.polyptr[4] = 0x2C808080;
	*(long*) &db.polyptr[8] = 0x780100;
	*(long*) &db.polyptr[12] = 0x6800;
	*(long*) &db.polyptr[16] = 0x7801FF;

	*(long*) &db.polyptr[20] = 0x13468FF;
	*(long*) &db.polyptr[24] = 0xEF0100;
	*(short*) &db.polyptr[28] = 0xDF00;
	*(long*) &db.polyptr[32] = 0xEF01FF;

	*(short*) &db.polyptr[36] = 0xDFFF;

	*(long*) &db.polyptr[0] = db.ot[0] | 0x9000000;
	db.ot[0] = db.polyptr;

	db.polyptr += 0x28;
	return;
}

void GPU_SetScreenPosition(short x, short y)//5F360(<), 60040(<)
{
	db.disp[0].screen.x = x;
	db.disp[0].screen.y = y;
	db.disp[1].screen.x = x;
	db.disp[1].screen.y = y;

	return;
}<|endoftext|>


<commit_before>/*!
    \mainpage The QtSparql library

    \brief unstable

    \section Introduction

    Description

    QtSparql is a client-side library for accessing RDF stores.

    The query language for RDF stores is SPARQL.

    QtSparql takes in SPARQL queries, forwards them to the selected backend, and
    gives back the results of the query.  It can return the results
    asynchronously if the backend supports asynchronous operations.

    QtSparql can connect to different backends.  Currently the following backends
    exist:

    - QTRACKER for accessing Tracker over D-Bus
    - QTRACKER_DIRECT for accessing Tracker via direct database
      access and D-Bus
    - QSPARQL_ENDPOINT for accessing online RDF stores, e.g., DBpedia
    - QVIRTUOSO backend for accessing Virtuoso

    List of classes QtSparql API provides:

Class	Description

QSparqlConnection
    Interface for accessing an RDF store.

QSparqlConnectionOptions
    Encapsulates options given to QSparqlConnection. Some options are used
    only by some drivers.

QSparqlError	SPARQL
    error information.

QSparqlBinding	Handles
    a binding between a SPARQL query variable name and the value of the RDF
    node.

QSparqlQuery	Means of
    executing and manipulating SPARQL statements.

QSparqlQueryOptions
    Encapsulates query execution options given to QSparqlConnection::exec(const  QSparqlQuery&, const QSparqlQueryOptions&)
    Some options are used only by some drivers.

QSparqlQueryModel	Read-only data model
    for SPARQL result sets.

QSparqlResultRow	Encapsulates a row in
    the results of a query.

QSparqlResult	Abstract
    interface for accessing the results of an executed QSparqlQuery.

    \attention The QtSparql library is not yet stable; we make no
    promises about API / ABI compatibility!

    \section gettingstarted Getting started

    The following code snippets demonstrate how to retrieve data from
    a RDF database using QtSparql.

    - Create a QSparqlConnection object specifiying the backend you want to use.
      If necessary, specify the parameters by using QSparqlConnectionOptions and
      passing it to QSparqlConnection.

    E.g. to use tracker:
    \dontinclude simple/main.cpp
    \skipline QSparqlConnection

    E.g. to use DBpedia:
    \dontinclude dbpedia/main.cpp
    \skip QSparqlConnectionOptions
    \until QSPARQL_ENDPOINT

    - Construct a QSparqlQuery with the SPARQL query string.  Specify the query
      type, if needed.

    E.g.
    \dontinclude simple/main.cpp
    \skipline QSparqlQuery

    or

    \dontinclude iteration/main.cpp
    \skip QSparqlQuery insert
    \until InsertStatement

    - Use QSparqlConnection::exec() to execute the query. It returns a
      pointer to QSparqlResult.

    E.g.
    \dontinclude simple/main.cpp
    \skipline QSparqlResult

    - You can then connect to the QSparqlResult::finished() and
      QSparqlResult::dataReady signals.

    - The QSparqlResult can be iterated over by using the following functions:
      QSparqlResult::first(), QSparqlResult::last(), QSparqlResult::next(),
      QSparqlResult::previous(), QSparqlResult::setPos(). The caller is
      responsible for deleting the QSparqlResult.

    E.g.
    \dontinclude simple/main.cpp
    \skip result->next
    \until toString

    - Data can be retrieved by using QSparqlResult::value().

    The following classes are the most relevant for getting started with QSparql:
    - QSparqlConnection
    - QSparqlQuery
    - QSparqlResult
    - QSparqlQueryModel

    \section querymodels Query models

    The QSparqlQueryModel class provides a convienient, read-only, data model for SPARQL results
    which can be used to provide data to view classes such as QTableView.

    After creating the model, use QSparqlQueryModel::setQuery() to set the query for the connection,
    header data for the model can also be set using QSparqlQueryModel::setHeaderData().

    E.g.
    \dontinclude querymodel/main.cpp
    \skip model;
    \until setHeaderData

    You can then use this in an a view class by using it's setModel() function.

    E.g.
    \dontinclude querymodel/main.cpp
    \skip *view
    \until model

    It is also easy to implement custom query models by reimplementing QSparqlQueryModel::data(), see
    the querymodel example for an example of this.

    \section connectionoptions Connection options supported by drivers

    QTRACKER_DIRECT driver supports the following connection options:
    - dataReadyInterval (int, default 1), controls the interval for
      emitting the dataReady signal.
    - maxThread (int), sets the maximum number of threads for the
      thread pool to use. If not set a default of number of cores * 2 will
      be used.
    - threadExpiry (int, default 2000), controls the expiry time
      (in milliseconds) of the threads created by the thread pool.

    QENDPOINT driver supports the following connection options:
    - hostName (QString)
    - path (QString)
    - port (int)
    - userName (QString)
    - password (QString)
    - networkAccessManager (QNetworkAccessManager*)
    - proxy (const QNetworkProxy&)
    - custom: "timeout" (int) (for virtuoso endpoints)
    - custom: "maxrows" (int) (for virtuoso endpoints)

    QVIRTUOSO driver supports the following connection options:
    - hostName (QString)
    - port (int)
    - userName (QString)
    - password (QString)
    - databaseName (QString)

    For setting custom options, use QSparqlConnectionOptions::setOption() and
    give the option name as a string, followed by the value.

    Other options can be set using QSparqlConnectionOptions::setOption(), however
    it is preferable to use the convinence functions in QSparqlConnectionOptions,
    as these provide additional error checking.

    \section connectionfeatures Connection features

    The following table describes the features supported by each driver. The
    features can be queried with QSparqlConnection::hasFeature().

    QuerySize
    DefaultGraph
    AskQueries
    ConstructQueries
    UpdateQueries
    SyncExec
    AsyncExec

    QTRACKER
    Yes
    Yes
    Yes
    No
    Yes
    No
    Yes

    QTRACKER_DIRECT
    Yes
    Yes
    Yes
    No
    Yes
    Yes
    No

    QSPARQL_ENDPOINT
    Yes
    Yes
    Yes
    Yes
    Yes
    No
    Yes

    QVIRTUOSO
    Yes
    No
    Yes
    Yes
    Yes
    No (*)
    No

    (*) The QVIRTUOSO driver is natively synchronous, but support for syncExec
    directly is not currently implemented.

    \section backendspecific Accessing backend-specific functionalities

    QtSparql doesn't offer backend-specific functionalities.  For that purpose,
    there are separate add-on libraries, e.g., libqtsparql-tracker-extensions.

*/
<commit_msg>Add QTRACKER_DIRECT specific information<commit_after>/*!
    \mainpage The QtSparql library

    \brief unstable

    \section Introduction

    Description

    QtSparql is a client-side library for accessing RDF stores.

    The query language for RDF stores is SPARQL.

    QtSparql takes in SPARQL queries, forwards them to the selected backend, and
    gives back the results of the query.  It can return the results
    asynchronously if the backend supports asynchronous operations.

    QtSparql can connect to different backends.  Currently the following backends
    exist:

    - QTRACKER for accessing Tracker over D-Bus
    - QTRACKER_DIRECT for accessing Tracker via direct database
      access and D-Bus. See the \ref trackerdirectspecific "specific useage section"
      for more information
    - QSPARQL_ENDPOINT for accessing online RDF stores, e.g., DBpedia
    - QVIRTUOSO backend for accessing Virtuoso

    List of classes QtSparql API provides:

Class	Description

QSparqlConnection
    Interface for accessing an RDF store.

QSparqlConnectionOptions
    Encapsulates options given to QSparqlConnection. Some options are used
    only by some drivers.

QSparqlError	SPARQL
    error information.

QSparqlBinding	Handles
    a binding between a SPARQL query variable name and the value of the RDF
    node.

QSparqlQuery	Means of
    executing and manipulating SPARQL statements.

QSparqlQueryOptions
    Encapsulates query execution options given to QSparqlConnection::exec(const  QSparqlQuery&, const QSparqlQueryOptions&)
    Some options are used only by some drivers.

QSparqlQueryModel	Read-only data model
    for SPARQL result sets.

QSparqlResultRow	Encapsulates a row in
    the results of a query.

QSparqlResult	Abstract
    interface for accessing the results of an executed QSparqlQuery.

    \attention The QtSparql library is not yet stable; we make no
    promises about API / ABI compatibility!

    \section gettingstarted Getting started

    The following code snippets demonstrate how to retrieve data from
    a RDF database using QtSparql.

    - Create a QSparqlConnection object specifiying the backend you want to use.
      If necessary, specify the parameters by using QSparqlConnectionOptions and
      passing it to QSparqlConnection.

    E.g. to use tracker:
    \dontinclude simple/main.cpp
    \skipline QSparqlConnection

    E.g. to use DBpedia:
    \dontinclude dbpedia/main.cpp
    \skip QSparqlConnectionOptions
    \until QSPARQL_ENDPOINT

    - Construct a QSparqlQuery with the SPARQL query string.  Specify the query
      type, if needed.

    E.g.
    \dontinclude simple/main.cpp
    \skipline QSparqlQuery

    or

    \dontinclude iteration/main.cpp
    \skip QSparqlQuery insert
    \until InsertStatement

    - Use QSparqlConnection::exec() to execute the query. It returns a
      pointer to QSparqlResult.

    E.g.
    \dontinclude simple/main.cpp
    \skipline QSparqlResult

    - You can then connect to the QSparqlResult::finished() and
      QSparqlResult::dataReady signals.

    - The QSparqlResult can be iterated over by using the following functions:
      QSparqlResult::first(), QSparqlResult::last(), QSparqlResult::next(),
      QSparqlResult::previous(), QSparqlResult::setPos(). The caller is
      responsible for deleting the QSparqlResult.

    E.g.
    \dontinclude simple/main.cpp
    \skip result->next
    \until toString

    - Data can be retrieved by using QSparqlResult::value().

    The following classes are the most relevant for getting started with QSparql:
    - QSparqlConnection
    - QSparqlQuery
    - QSparqlResult
    - QSparqlQueryModel

    \section querymodels Query models

    The QSparqlQueryModel class provides a convienient, read-only, data model for SPARQL results
    which can be used to provide data to view classes such as QTableView.

    After creating the model, use QSparqlQueryModel::setQuery() to set the query for the connection,
    header data for the model can also be set using QSparqlQueryModel::setHeaderData().

    E.g.
    \dontinclude querymodel/main.cpp
    \skip model;
    \until setHeaderData

    You can then use this in an a view class by using it's setModel() function.

    E.g.
    \dontinclude querymodel/main.cpp
    \skip *view
    \until model

    It is also easy to implement custom query models by reimplementing QSparqlQueryModel::data(), see
    the querymodel example for an example of this.

    \section connectionoptions Connection options supported by drivers

    QTRACKER_DIRECT driver supports the following connection options:
    - dataReadyInterval (int, default 1), controls the interval for
      emitting the dataReady signal.
    - maxThread (int), sets the maximum number of threads for the
      thread pool to use. If not set a default of number of cores * 2 will
      be used.
    - threadExpiry (int, default 2000), controls the expiry time
      (in milliseconds) of the threads created by the thread pool.

    QENDPOINT driver supports the following connection options:
    - hostName (QString)
    - path (QString)
    - port (int)
    - userName (QString)
    - password (QString)
    - networkAccessManager (QNetworkAccessManager*)
    - proxy (const QNetworkProxy&)
    - custom: "timeout" (int) (for virtuoso endpoints)
    - custom: "maxrows" (int) (for virtuoso endpoints)

    QVIRTUOSO driver supports the following connection options:
    - hostName (QString)
    - port (int)
    - userName (QString)
    - password (QString)
    - databaseName (QString)

    For setting custom options, use QSparqlConnectionOptions::setOption() and
    give the option name as a string, followed by the value.

    Other options can be set using QSparqlConnectionOptions::setOption(), however
    it is preferable to use the convinence functions in QSparqlConnectionOptions,
    as these provide additional error checking.

    \section connectionfeatures Connection features

    The following table describes the QSparclConnection::Feature support of each
    driver. The features can be queried with QSparqlConnection::hasFeature().

    QuerySize
    DefaultGraph
    AskQueries
    ConstructQueries
    UpdateQueries
    SyncExec
    AsyncExec

    QTRACKER
    Yes
    Yes
    Yes
    No
    Yes
    No
    Yes

    QTRACKER_DIRECT
    Yes
    Yes
    Yes
    No
    Yes
    Yes
    No

    QSPARQL_ENDPOINT
    Yes
    Yes
    Yes
    Yes
    Yes
    No
    Yes

    QVIRTUOSO
    Yes
    No
    Yes
    Yes
    Yes
    No (*)
    No

    (*) The QVIRTUOSO driver is natively synchronous, but support for syncExec
    directly is not currently implemented.

    \section trackerdirectspecific QTRACKER_DIRECT specific useage

    There are two ways to use the QTRACKER_DIRECT driver, synchronously using
    QSparqlConnection::syncExec(), and asynchronously using QSparqlConnection::exec().
    The result behaviour is different, and supports different features, depending on
    the method used.

    The following table describes the QSparqlResult::Feature support of each method.

    QuerySize
    ForwardOnly
    Sync

    exec()
    Yes
    No
    No

    syncExec()
    No
    Yes
    Yes

    When using synchronous execution, it is important to fully use the results returned
    before making another query, either synchronously or asynchronously, by using
    QSparqlResult::next until it returns false. If you fail to do this, any new results
    that may have been added after your original query will not be included in any subsequent
    queries you make.

    \section backendspecific Accessing backend-specific functionalities

    QtSparql doesn't offer backend-specific functionalities.  For that purpose,
    there are separate add-on libraries, e.g., libqtsparql-tracker-extensions.

*/
<|endoftext|>


<commit_before>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include "ssa.h"

namespace hcc {
namespace ssa {
namespace {

struct congruence_classes {
    std::map<argument, reg> classes;

    std::function<void(argument&)> replacer()
    {
        return [&](argument& arg) {
            if (!arg.is_reg()) {
                return;
            }

            auto it = classes.find(arg);
            if (it != classes.end()) {
                arg = it->second;
            }
        };
    }
};

// Implements "Method I" from paper by Sreedhar et al.
// "Translating Out of Static Single Assignment Form"
// FIXME copies are not parallel
void naive_copy_insertion(subroutine& s, congruence_classes& cc)
{
    // worklist of MOV instructions to be inserted at the end of basic block,
    // indexed by basic block
    std::map<label, instruction_list> worklist;

    // pass 1: fill worklist and insert primed copy
    s.for_each_bb([&](basic_block& bb) {
        for (auto i = bb.instructions.begin(), e = bb.instructions.end(); i != e; ++i) {
            if (i->type != instruction_type::PHI)
                continue;

            auto arg = i->arguments.begin();

            // invent a new primed name
            const auto base = s.create_reg();
            s.add_debug(base, "phi_dst_reg", *arg);

            // insert MOV after PHI
            bb.instructions.insert(++decltype(i)(i),
                                   instruction(instruction_type::MOV, {*arg, base}));
            cc.classes.emplace(base, base);
            cc.classes.emplace(*arg, base);

            // rename PHI's dest
            *arg++ = base;

            while (arg != i->arguments.end()) {
                auto label = *arg++;
                auto value = *arg;

                const auto name = s.create_reg();
                s.add_debug(name, "phi_src_reg", base);
                s.add_debug(name, "phi_src_label", label);

                // insert MOV into worklist
                worklist[label.get_label()].emplace_back(
                    instruction(instruction_type::MOV, {name, value}));
                cc.classes.emplace(name, base);

                // rename PHI's src
                *arg++ = name;
            }
        }
    });

    // pass 2: paste instructions from worklist at the end of respective basic block
    s.for_each_bb([&](basic_block& bb) {
        bb.instructions.splice(--bb.instructions.end(), worklist[bb.name]);
    });
}

// live(x) ... set of statements were x is live
// live(a) intersects live(b) ... def(a) in live(b) or def(b) in live(a)
// V(x) ... V(b) = V(a) if b <- a
//          V(b) = b    otherwise
// a interfere b ... live(a) intersects live(b) and V(a) != V(b)
bool interfere(const argument& a, const argument& b, subroutine& s)
{
    assert(a.is_reg() && b.is_reg());

    bool def_a_in_live_b = false;
    bool def_b_in_live_a = false;
    bool live_a = false;
    bool live_b = false;
    std::map<std::string, std::string> V; // value of variable

    // TODO investigate if domtree is really needed
    // TODO live ranges are probably overestimated at the end

    s.for_each_bb_in_domtree_preorder([&](basic_block& block) {
        for (auto& instr : block.instructions) {
            // live ranges
            instr.def_apply([&](argument& def) {
                if (def == a) {
                    live_a = true;
                    if (live_b)
                        def_a_in_live_b = true;
                } else if (def == b) {
                    live_b = true;
                    if (live_a)
                        def_b_in_live_a = true;
                }
            });

            // values
            if (instr.type == instruction_type::MOV) {
                auto s0 = instr.arguments[0].save_fast();
                auto s1 = instr.arguments[1].save_fast();
                V[std::move(s0)] = V[std::move(s1)];
            } else {
                instr.def_apply([&](argument& arg) {
                    auto s0 = arg.save_fast();
                    auto s1 = instr.save_fast();
                    V[std::move(s0)] = std::move(s1);
                });
            }
        }
    });

    const bool intersect = def_a_in_live_b || def_b_in_live_a;
    auto sa = a.save_fast();
    auto sb = b.save_fast();
    const bool differ_in_value = V.at(sa) != V.at(sb);
    return intersect && differ_in_value;
}

} // namespace {

// Inspired by paper
// "Revisiting Out-of-SSA Translation for Correctness, Code Quality, and Efficiency"
void subroutine::ssa_deconstruct()
{
    recompute_dominance();

    congruence_classes cc;
    naive_copy_insertion(*this, cc);

    // incidental classes, rising from the code
    for_each_bb([&](basic_block& bb) {
        for (auto& instr : bb.instructions) {
            if (instr.type == instruction_type::MOV) {
                auto& dest = instr.arguments[0];
                auto& src = instr.arguments[1];
                const bool has_src_class = cc.classes.count(src) > 0;
                const bool has_dest_class = cc.classes.count(dest) > 0;
                if (has_src_class && has_dest_class && cc.classes.at(src) == cc.classes.at(dest))
                    continue;

                if (src.is_reg() && dest.is_reg()) {
                    if (!interfere(src, dest, *this)) {
                        if (!has_src_class && !has_dest_class) {
                            const auto r = create_reg();
                            add_debug(r, "congruence_class");
                            cc.classes.emplace(src, r);
                            cc.classes.emplace(dest, r);
                        } else if (has_src_class && has_dest_class) {
                            auto keep_class = cc.classes.at(src);
                            auto remove_class = cc.classes.at(dest);
                            for (auto& kv : cc.classes) {
                                if (kv.second == remove_class)
                                    kv.second = keep_class;
                            }
                        }
                    }
                }
            }
        }
    });

    // pass 2: remove phis, replace names, remove nop moves
    // FIXME materialize parallel moves here, do not add them in the first place
    for_each_bb([&](basic_block& bb) {
        for (auto i = bb.instructions.begin(), e = bb.instructions.end(); i != e;) {
            if (i->type == instruction_type::PHI) {
                i = bb.instructions.erase(i);
            } else {
                i->def_apply(cc.replacer());
                i->use_apply(cc.replacer());
                if (i->type == instruction_type::MOV && i->arguments[0] == i->arguments[1]) {
                    i = bb.instructions.erase(i);
                } else {
                    ++i;
                }
            }
        }
    });
}

} // namespace ssa {
} // namespace hcc {
<commit_msg>Optimize interference computation<commit_after>// Copyright (c) 2012-2015 Dano Pernis
// See LICENSE for details

#include "ssa.h"

namespace hcc {
namespace ssa {
namespace {

struct congruence_classes {
    std::map<argument, reg> classes;

    std::function<void(argument&)> replacer()
    {
        return [&](argument& arg) {
            if (!arg.is_reg()) {
                return;
            }

            auto it = classes.find(arg);
            if (it != classes.end()) {
                arg = it->second;
            }
        };
    }
};

// Implements "Method I" from paper by Sreedhar et al.
// "Translating Out of Static Single Assignment Form"
// FIXME copies are not parallel
void naive_copy_insertion(subroutine& s, congruence_classes& cc)
{
    // worklist of MOV instructions to be inserted at the end of basic block,
    // indexed by basic block
    std::map<label, instruction_list> worklist;

    // pass 1: fill worklist and insert primed copy
    s.for_each_bb([&](basic_block& bb) {
        for (auto i = bb.instructions.begin(), e = bb.instructions.end(); i != e; ++i) {
            if (i->type != instruction_type::PHI)
                continue;

            auto arg = i->arguments.begin();

            // invent a new primed name
            const auto base = s.create_reg();
            s.add_debug(base, "phi_dst_reg", *arg);

            // insert MOV after PHI
            bb.instructions.insert(++decltype(i)(i),
                                   instruction(instruction_type::MOV, {*arg, base}));
            cc.classes.emplace(base, base);
            cc.classes.emplace(*arg, base);

            // rename PHI's dest
            *arg++ = base;

            while (arg != i->arguments.end()) {
                auto label = *arg++;
                auto value = *arg;

                const auto name = s.create_reg();
                s.add_debug(name, "phi_src_reg", base);
                s.add_debug(name, "phi_src_label", label);

                // insert MOV into worklist
                worklist[label.get_label()].emplace_back(
                    instruction(instruction_type::MOV, {name, value}));
                cc.classes.emplace(name, base);

                // rename PHI's src
                *arg++ = name;
            }
        }
    });

    // pass 2: paste instructions from worklist at the end of respective basic block
    s.for_each_bb([&](basic_block& bb) {
        bb.instructions.splice(--bb.instructions.end(), worklist[bb.name]);
    });
}

// Determine if two registers *a* and *b* interfere in subroutine *s*
//
// Interfering registers have intersecting live ranges and different values:
//
// live(x) ... set of statements where x is live
// live(a) intersects live(b) ... def(a) in live(b) or def(b) in live(a)
// V(x) ... V(b) = V(a) if b <- a
//          V(b) = b    otherwise
// a interfere b ... live(a) intersects live(b) and V(a) != V(b)
bool interfere(const reg& a, const reg& b, subroutine& s)
{
    bool def_a_in_live_b = false;
    bool def_b_in_live_a = false;
    bool live_a = false;
    bool live_b = false;

    // At this point, code is still in SSA, so value of each register
    // is determined solely by its defining instruction. What is more,
    // we can use pointer here because nothing gets reallocated.
    // TODO use const *s* to prove that pointer usage is indeed safe.
    std::map<reg, const instruction*> value;

    // TODO investigate if domtree is really needed
    // TODO live ranges are probably overestimated at the end

    s.for_each_bb_in_domtree_preorder([&](basic_block& block) {
        for (auto& instr : block.instructions) {
            // live ranges
            instr.def_apply([&](argument& def) {
                assert (def.is_reg());
                if (def.get_reg() == a) {
                    live_a = true;
                    if (live_b)
                        def_a_in_live_b = true;
                } else if (def == b) {
                    live_b = true;
                    if (live_a)
                        def_b_in_live_a = true;
                }
            });

            // values
            if (instr.type == instruction_type::MOV) {
                if (instr.arguments[1].is_reg()) {
                    value.emplace(instr.arguments[0].get_reg(),
                        value.at(instr.arguments[1].get_reg()));
                } else {
                    value.emplace(instr.arguments[0].get_reg(), &instr);
                }
            } else {
                instr.def_apply([&](argument& arg) {
                    assert (arg.is_reg());
                    value.emplace(arg.get_reg(), &instr);
                });
            }
        }
    });

    const bool intersect = def_a_in_live_b || def_b_in_live_a;
    const bool differ_in_value = value.at(a) != value.at(b);
    return intersect && differ_in_value;
}

} // namespace {

// Inspired by paper
// "Revisiting Out-of-SSA Translation for Correctness, Code Quality, and Efficiency"
void subroutine::ssa_deconstruct()
{
    recompute_dominance();

    congruence_classes cc;
    naive_copy_insertion(*this, cc);

    // incidental classes, rising from the code
    for_each_bb([&](basic_block& bb) {
        for (auto& instr : bb.instructions) {
            if (instr.type == instruction_type::MOV) {
                auto& dest = instr.arguments[0];
                auto& src = instr.arguments[1];
                const bool has_src_class = cc.classes.count(src) > 0;
                const bool has_dest_class = cc.classes.count(dest) > 0;
                if (has_src_class && has_dest_class && cc.classes.at(src) == cc.classes.at(dest))
                    continue;

                if (src.is_reg() && dest.is_reg()) {
                    if (!interfere(src.get_reg(), dest.get_reg(), *this)) {
                        if (!has_src_class && !has_dest_class) {
                            const auto r = create_reg();
                            add_debug(r, "congruence_class");
                            cc.classes.emplace(src, r);
                            cc.classes.emplace(dest, r);
                        } else if (has_src_class && has_dest_class) {
                            auto keep_class = cc.classes.at(src);
                            auto remove_class = cc.classes.at(dest);
                            for (auto& kv : cc.classes) {
                                if (kv.second == remove_class)
                                    kv.second = keep_class;
                            }
                        }
                    }
                }
            }
        }
    });

    // pass 2: remove phis, replace names, remove nop moves
    // FIXME materialize parallel moves here, do not add them in the first place
    for_each_bb([&](basic_block& bb) {
        for (auto i = bb.instructions.begin(), e = bb.instructions.end(); i != e;) {
            if (i->type == instruction_type::PHI) {
                i = bb.instructions.erase(i);
            } else {
                i->def_apply(cc.replacer());
                i->use_apply(cc.replacer());
                if (i->type == instruction_type::MOV && i->arguments[0] == i->arguments[1]) {
                    i = bb.instructions.erase(i);
                } else {
                    ++i;
                }
            }
        }
    });
}

} // namespace ssa {
} // namespace hcc {
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>

#include "class_names.h"
#include "common_action_mocks.h"
#include "i_dbg_object_factory_mock.h"
#include "i_eval_coordinator_mock.h"
#include "string_evaluator.h"
#include "type_signature.h"

using google_cloud_debugger::ConvertStringToWCharPtr;
using google_cloud_debugger::DbgObject;
using google_cloud_debugger::StringEvaluator;
using google_cloud_debugger::TypeSignature;
using std::string;
using ::testing::_;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;

namespace google_cloud_debugger_test {

// Test Fixture for DbgString.
class StringEvaluatorTest : public ::testing::Test {
 protected:
  virtual void SetUp() {}

  // Sets up IEvalCoordinator for mock call.
  virtual void SetUpEvalCoordinator() {
    EXPECT_CALL(eval_coordinator_mock_, CreateEval(_))
        .Times(1)
        .WillRepeatedly(
            DoAll(SetArgPointee<0>(&debug_eval_mock_), Return(S_OK)));

    EXPECT_CALL(eval_coordinator_mock_, WaitForEval(_, &debug_eval_mock_, _))
        .Times(1)
        .WillRepeatedly(
            DoAll(SetArgPointee<2>(&debug_string_mock_), Return(S_OK)));

    EXPECT_CALL(debug_eval_mock_, NewString(_))
        .Times(1)
        .WillRepeatedly(Return(S_OK));
  }

  // Sets up object factory for object creation.
  virtual void SetUpObjFactory() {
    EXPECT_CALL(object_factory_mock_,
                CreateDbgObject(&debug_string_mock_, _, _, _))
        .Times(1)
        .WillRepeatedly(Return(S_OK));
  }

  // Mock of IEvalCoordinator used for evaluate.
  IEvalCoordinatorMock eval_coordinator_mock_;

  // Mock used for object creation.
  IDbgObjectFactoryMock object_factory_mock_;

  // Mock eval returned by IEvalCoordinatorMock.
  ICorDebugEvalMock debug_eval_mock_;

  // Debug String returned by IEvalCoordinatorMock.
  ICorDebugStringValueMock debug_string_mock_;

  // Content of the string.
  string string_content_ = "String Content";
};

// Tests Compile function of DbgString.
// This function returns S_OK without anywork.
TEST_F(StringEvaluatorTest, Compile) {
  StringEvaluator evaluator(string_content_);
  EXPECT_EQ(evaluator.Compile(nullptr, nullptr, nullptr), S_OK);
}

// Tests that GetStaticType returns string type.
TEST_F(StringEvaluatorTest, GetStaticType) {
  StringEvaluator evaluator(string_content_);
  TypeSignature type_sig = evaluator.GetStaticType();

  EXPECT_EQ(type_sig.cor_type, CorElementType::ELEMENT_TYPE_STRING);
  EXPECT_EQ(type_sig.type_name, google_cloud_debugger::kStringClassName);
}

// Tests that Evaluate function creates a string.
TEST_F(StringEvaluatorTest, Evaluate) {
  StringEvaluator evaluator(string_content_);

  SetUpEvalCoordinator();
  SetUpObjFactory();

  std::shared_ptr<DbgObject> result;
  std::ostringstream err_stream;
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                               &object_factory_mock_, &err_stream),
            S_OK);
}

// Tests that error cases for Evaluate function.
TEST_F(StringEvaluatorTest, EvaluateError) {
  StringEvaluator evaluator(string_content_);

  std::shared_ptr<DbgObject> result;
  std::ostringstream err_stream;
  // Null tests.
  EXPECT_EQ(evaluator.Evaluate(nullptr, &eval_coordinator_mock_,
                               &object_factory_mock_, &err_stream),
            E_INVALIDARG);
  EXPECT_EQ(
      evaluator.Evaluate(&result, nullptr, &object_factory_mock_, &err_stream),
      E_INVALIDARG);
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_, nullptr,
                               &err_stream),
            E_INVALIDARG);
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                               &object_factory_mock_, nullptr),
            E_INVALIDARG);

  {
    // Tests that Evaluate fails if we cannot create ICorDebugEval.
    EXPECT_CALL(eval_coordinator_mock_, CreateEval(_))
        .Times(1)
        .WillRepeatedly(Return(E_ACCESSDENIED));
    EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                                 &object_factory_mock_, &err_stream),
              E_ACCESSDENIED);
  }

  {
    // Tests that Evaluate fails if we cannot Object Factory fails.
    SetUpEvalCoordinator();
    EXPECT_CALL(object_factory_mock_,
                CreateDbgObject(&debug_string_mock_, _, _, _))
        .Times(1)
        .WillRepeatedly(Return(CORDBG_E_PROCESS_TERMINATED));

    EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                                 &object_factory_mock_, &err_stream),
              CORDBG_E_PROCESS_TERMINATED);
  }
}

}  // namespace google_cloud_debugger_test
<commit_msg>Address PR comments<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>

#include "class_names.h"
#include "common_action_mocks.h"
#include "dbg_string.h"
#include "i_dbg_object_factory_mock.h"
#include "i_eval_coordinator_mock.h"
#include "string_evaluator.h"
#include "type_signature.h"
#include "error_messages.h"

using google_cloud_debugger::ConvertStringToWCharPtr;
using google_cloud_debugger::DbgObject;
using google_cloud_debugger::DbgString;
using google_cloud_debugger::StringEvaluator;
using google_cloud_debugger::TypeSignature;
using std::string;
using ::testing::_;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;

namespace google_cloud_debugger_test {

// Test Fixture for StringEvaluator.
class StringEvaluatorTest : public ::testing::Test {
 protected:
  virtual void SetUp() {}

  // Sets up IEvalCoordinator for mock call.
  virtual void SetUpEvalCoordinator() {
    EXPECT_CALL(eval_coordinator_mock_, CreateEval(_))
        .Times(1)
        .WillRepeatedly(
            DoAll(SetArgPointee<0>(&debug_eval_mock_), Return(S_OK)));

    EXPECT_CALL(eval_coordinator_mock_, WaitForEval(_, &debug_eval_mock_, _))
        .Times(1)
        .WillRepeatedly(
            DoAll(SetArgPointee<2>(&debug_string_mock_), Return(S_OK)));

    EXPECT_CALL(debug_eval_mock_, NewString(_))
        .Times(1)
        .WillRepeatedly(Return(S_OK));
  }

  // Sets up object factory for object creation.
  virtual void SetUpObjFactory() {
    EXPECT_CALL(object_factory_mock_,
                CreateDbgObject(&debug_string_mock_, _, _, _))
        .Times(1)
        .WillRepeatedly(Return(S_OK));
  }

  // Mock of IEvalCoordinator used for evaluate.
  IEvalCoordinatorMock eval_coordinator_mock_;

  // Mock used for object creation.
  IDbgObjectFactoryMock object_factory_mock_;

  // Mock eval returned by IEvalCoordinatorMock.
  ICorDebugEvalMock debug_eval_mock_;

  // Debug String returned by IEvalCoordinatorMock.
  ICorDebugStringValueMock debug_string_mock_;

  // Content of the string.
  string string_content_ = "String Content";

  // Error stream.
  std::ostringstream err_stream_;
};

// Tests Compile function of DbgString.
// This function returns S_OK without anywork.
TEST_F(StringEvaluatorTest, Compile) {
  StringEvaluator evaluator(string_content_);
  EXPECT_EQ(evaluator.Compile(nullptr, nullptr, nullptr), S_OK);
}

// Tests that GetStaticType returns string type.
TEST_F(StringEvaluatorTest, GetStaticType) {
  StringEvaluator evaluator(string_content_);
  TypeSignature type_sig = evaluator.GetStaticType();

  EXPECT_EQ(type_sig.cor_type, CorElementType::ELEMENT_TYPE_STRING);
  EXPECT_EQ(type_sig.type_name, google_cloud_debugger::kStringClassName);
}

// Tests that Evaluate function creates a string.
TEST_F(StringEvaluatorTest, Evaluate) {
  StringEvaluator evaluator(string_content_);

  SetUpEvalCoordinator();
  SetUpObjFactory();

  std::shared_ptr<DbgObject> result;
  std::ostringstream err_stream;
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                               &object_factory_mock_, &err_stream),
            S_OK);
}

// Tests null error cases for Evaluate function.
TEST_F(StringEvaluatorTest, EvaluateError) {
  StringEvaluator evaluator(string_content_);
  std::shared_ptr<DbgObject> result;

  EXPECT_EQ(evaluator.Evaluate(nullptr, &eval_coordinator_mock_,
    &object_factory_mock_, &err_stream_),
    E_INVALIDARG);
  EXPECT_EQ(
    evaluator.Evaluate(&result, nullptr, &object_factory_mock_, &err_stream_),
    E_INVALIDARG);
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_, nullptr,
    &err_stream_),
    E_INVALIDARG);
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
    &object_factory_mock_, nullptr),
    E_INVALIDARG);
}

// Tests that Evaluate fails if we cannot create ICorDebugEval.
TEST_F(StringEvaluatorTest, EvaluateErrorEvalCoordinator) {
  StringEvaluator evaluator(string_content_);
  std::shared_ptr<DbgObject> result;

  EXPECT_CALL(eval_coordinator_mock_, CreateEval(_))
      .Times(1)
      .WillRepeatedly(Return(E_ACCESSDENIED));
  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                                &object_factory_mock_, &err_stream_),
            E_ACCESSDENIED);
  EXPECT_EQ(err_stream_.str(), google_cloud_debugger::kFailedEvalCreation.c_str());
}

// Tests that Evaluate fails if the IDbgObjectFactory cannot create
// a new object.
TEST_F(StringEvaluatorTest, EvaluateErrorObjCreation) {
  StringEvaluator evaluator(string_content_);
  std::shared_ptr<DbgObject> result;

  SetUpEvalCoordinator();
  EXPECT_CALL(object_factory_mock_,
              CreateDbgObject(&debug_string_mock_, _, _, _))
      .Times(1)
      .WillRepeatedly(Return(CORDBG_E_PROCESS_TERMINATED));

  EXPECT_EQ(evaluator.Evaluate(&result, &eval_coordinator_mock_,
                                &object_factory_mock_, &err_stream_),
            CORDBG_E_PROCESS_TERMINATED);
  EXPECT_EQ(err_stream_.str(), google_cloud_debugger::kFailedToCreateDbgObject.c_str());
}

}  // namespace google_cloud_debugger_test
<|endoftext|>


<commit_before>/*
Resembla: Word-based Japanese similar sentence search library
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef RESEMBLA_ELIMINATOR_HPP
#define RESEMBLA_ELIMINATOR_HPP

#include <string>
#include <vector>
#include <map>

#include "string_util.hpp"

namespace resembla {

template<typename string_type, typename bitvector_type = uint64_t>
struct Eliminator
{
    using size_type = typename string_type::size_type;
    using symbol_type = typename string_type::value_type;
    using distance_type = int;

    Eliminator(string_type const& pattern = string_type())
    {
        init(pattern);
    }

    void init(string_type const& pattern)
    {
        this->pattern = pattern;
        if(pattern.empty()){
            return;
        }

        pattern_length = pattern.size();
        block_size = ((pattern_length - 1) >> bit_offset<bitvector_type>()) + 1;
        rest_bits = pattern_length - (block_size - 1) * bit_width<bitvector_type>();
        sink = bitvector_type{1} << (rest_bits - 1);

        constructPM();
        zeroes.resize(block_size, 0);
        work.resize(block_size);
    }

    void operator()(std::vector<string_type>& candidates, size_type k)
    {
        using index_distance = std::pair<size_type, distance_type>;

        // calculate scores
        std::vector<index_distance> work(candidates.size());
        for(size_type i = 0; i < work.size(); ++i){
            work[i].first = i;
            work[i].second = -distance(candidates[i]);
        }

        // sort partially to obtain top-k elements
        std::nth_element(std::begin(work), std::begin(work) + k, std::end(work),
            [](const index_distance& a, const index_distance& b) -> bool{
                return a.second > b.second;
            });

        // ensure that work[i].first < work[j].first if i < j < k
        std::partial_sort(std::begin(work), std::begin(work) + k, std::begin(work) + k,
            [](const index_distance& a, const index_distance& b) -> bool{
                return a.first < b.first;
            });
#ifdef DEBUG
        std::cerr << "narrow " << work.size() << " strings" << std::endl;
        for(size_type i = 0; i < k; ++i){
            std::cerr << cast_string<std::string>(candidates[work[i].first]) << ": " << work[i].second << std::endl;
        }
#endif

        // sort original list
        for(size_type i = 0; i < k; ++i){
            std::swap(candidates[i], candidates[work[i].first]);
        }
        candidates.erase(std::begin(candidates) + k, std::end(candidates));
    }

protected:
    string_type pattern;
    size_type pattern_length;
    size_type block_size;
    size_type rest_bits;
    bitvector_type sink;

    std::vector<std::pair<symbol_type, std::vector<bitvector_type>>> PM;
    std::vector<bitvector_type> zeroes;

    struct WorkData
    {
        bitvector_type D0;
        bitvector_type HP;
        bitvector_type HN;
        bitvector_type VP;
        bitvector_type VN;

        void reset()
        {
            D0 = HP = HN = VN = 0;
            VP = ~(bitvector_type{0});
        }
    };
    mutable std::vector<WorkData> work;

    template<typename Integer> static constexpr int bit_width()
    {
        return 8 * sizeof(Integer);
    }

    static constexpr int bit_offset(int w)
    {
        return w < 2 ? 0 : (bit_offset(w >> 1) + 1);
    }

    template<typename Integer> static constexpr int bit_offset()
    {
        return bit_offset(bit_width<Integer>());
    }

    template<typename key_type, typename value_type>
    const value_type& find_value(const std::vector<std::pair<key_type, value_type>>& data,
            const key_type c, const value_type& default_value) const
    {
        size_type l = 0, r = data.size();
        while(r - l > 8){
            auto i = (l + r) / 2;
            if(data[i].first < c){
                l = i + 1;
            }
            else if(data[i].first > c){
                r = i;
            }
            else{
                return data[i].second;
            }
        }
        for(size_type i = l; i < r; ++i){
            if(data[i].first == c){
                return data[i].second;
            }
        }
        return default_value;
    }

    void constructPM()
    {
        std::map<symbol_type, std::vector<bitvector_type>> PM_work;
        for(size_type i = 0; i < block_size - 1; ++i){
            for(size_type j = 0; j < bit_width<bitvector_type>(); ++j){
                if(PM_work[pattern[i * bit_width<bitvector_type>() + j]].empty()){
                    PM_work[pattern[i * bit_width<bitvector_type>() + j]].resize(block_size, 0);
                }
                PM_work[pattern[i * bit_width<bitvector_type>() + j]][i] |= bitvector_type{1} << j;
            }
        }
        for(size_type i = 0; i < rest_bits; ++i){
            if(PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].empty()){
                PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].resize(block_size, 0);
            }
            PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].back() |= bitvector_type{1} << i;
        }

        PM.clear();
        for(const auto& p: PM_work){
            PM.push_back(p);
        }
    }

    distance_type distance_sp(string_type const &text) const
    {
        auto& w = work.front();
        w.reset();
        for(size_type i = 0; i < pattern_length; ++i){
            w.VP |= bitvector_type{1} << i;
        }

        distance_type D = pattern_length;
        for(auto c: text){
            auto X = find_value(PM, c, zeroes).front() | w.VN;

            w.D0 = ((w.VP + (X & w.VP)) ^ w.VP) | X;
            w.HP = w.VN | ~(w.VP | w.D0);
            w.HN = w.VP & w.D0;

            X = (w.HP << 1) | 1;
            w.VP = (w.HN << 1) | ~(X | w.D0);
            w.VN = X & w.D0;

            if(w.HP & sink){
                ++D;
            }
            else if(w.HN & sink){
                --D;
            }
        }
        return D;
    }

    distance_type distance_lp(string_type const &text) const
    {
        constexpr bitvector_type msb = bitvector_type{1} << (bit_width<bitvector_type>() - 1);

        for(auto& w: work){
            w.reset();
        }
        for(size_type i = 0; i < rest_bits; ++i){
            work.back().VP |= bitvector_type{1} << i;
        }

        distance_type D = pattern_length;
        for(auto c: text){
            const auto& PMc = find_value(PM, c, zeroes);
            for(size_type r = 0; r < block_size; ++r){
                auto& w = work[r];
                auto X = PMc[r];
                if(r > 0 && (work[r - 1].HN & msb)){
                    X |= 1;
                }

                w.D0 = ((w.VP + (X & w.VP)) ^ w.VP) | X | w.VN;
                w.HP = w.VN | ~(w.VP | w.D0);
                w.HN = w.VP & w.D0;

                X = w.HP << 1;
                if(r == 0 || work[r - 1].HP & msb){
                    X |= 1;
                }
                w.VP = (w.HN << 1) | ~(X | w.D0);
                if(r > 0 && (work[r - 1].HN & msb)){
                    w.VP |= 1;
                }
                w.VN = X & w.D0;
            }

            if(work.back().HP & sink){
                ++D;
            }
            else if(work.back().HN & sink){
                --D;
            }
        }
        return D;
    }

    distance_type distance(string_type const &text) const
    {
        if(text.empty()){
            return pattern_length;
        }
        else if(pattern_length == 0){
            return text.size();
        }

        if(block_size == 1){
            return distance_sp(text);
        }
        else{
            return distance_lp(text);
        }
    }
};

}
#endif
<commit_msg>add heuristics<commit_after>/*
Resembla: Word-based Japanese similar sentence search library
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef RESEMBLA_ELIMINATOR_HPP
#define RESEMBLA_ELIMINATOR_HPP

#include <string>
#include <vector>
#include <map>

#include "string_util.hpp"

namespace resembla {

template<typename string_type, typename bitvector_type = uint64_t>
struct Eliminator
{
    using size_type = typename string_type::size_type;
    using symbol_type = typename string_type::value_type;
    using distance_type = int;

    Eliminator(string_type const& pattern = string_type())
    {
        init(pattern);
    }

    void init(string_type const& pattern)
    {
        this->pattern = pattern;
        if(pattern.empty()){
            return;
        }

        pattern_length = pattern.size();
        block_size = ((pattern_length - 1) >> bit_offset<bitvector_type>()) + 1;
        rest_bits = pattern_length - (block_size - 1) * bit_width<bitvector_type>();
        sink = bitvector_type{1} << (rest_bits - 1);

        constructPM();
        zeroes.resize(block_size, 0);
        work.resize(block_size);
    }

    void operator()(std::vector<string_type>& candidates, size_type k)
    {
        using index_distance = std::pair<size_type, distance_type>;

        // calculate scores
        std::vector<index_distance> work(candidates.size());
        for(size_type i = 0; i < work.size(); ++i){
            work[i].first = i;
            work[i].second = -distance(candidates[i]);
        }

        // sort partially to obtain top-k elements
        std::nth_element(std::begin(work), std::begin(work) + k, std::end(work),
            [](const index_distance& a, const index_distance& b) -> bool{
                return a.second > b.second;
            });

        // ensure that work[i].first < work[j].first if i < j < k
        std::partial_sort(std::begin(work), std::begin(work) + k, std::begin(work) + k,
            [](const index_distance& a, const index_distance& b) -> bool{
                return a.first < b.first;
            });
#ifdef DEBUG
        std::cerr << "narrow " << work.size() << " strings" << std::endl;
        for(size_type i = 0; i < k; ++i){
            std::cerr << cast_string<std::string>(candidates[work[i].first]) << ": " << work[i].second << std::endl;
        }
#endif

        // sort original list
        for(size_type i = 0; i < k; ++i){
            std::swap(candidates[i], candidates[work[i].first]);
        }
        candidates.erase(std::begin(candidates) + k, std::end(candidates));
    }

protected:
    string_type pattern;
    size_type pattern_length;
    symbol_type c_min, c_max;
    size_type block_size;
    size_type rest_bits;
    bitvector_type sink;

    std::vector<std::pair<symbol_type, std::vector<bitvector_type>>> PM;
    std::vector<bitvector_type> zeroes;

    struct WorkData
    {
        bitvector_type D0;
        bitvector_type HP;
        bitvector_type HN;
        bitvector_type VP;
        bitvector_type VN;

        void reset()
        {
            D0 = HP = HN = VN = 0;
            VP = ~(bitvector_type{0});
        }
    };
    mutable std::vector<WorkData> work;

    template<typename Integer> static constexpr int bit_width()
    {
        return 8 * sizeof(Integer);
    }

    static constexpr int bit_offset(int w)
    {
        return w < 2 ? 0 : (bit_offset(w >> 1) + 1);
    }

    template<typename Integer> static constexpr int bit_offset()
    {
        return bit_offset(bit_width<Integer>());
    }

    template<typename key_type, typename value_type>
    const value_type& find_value(const std::vector<std::pair<key_type, value_type>>& data,
            const key_type c, const value_type& default_value) const
    {
        if(c < c_min || c_max < c){
            return default_value;
        }
        else if(c == c_min){
            return PM.front().second;
        }
        else if(c == c_max){
            return PM.back().second;
        }

        size_type l = 1, r = data.size() - 1;
        while(r - l > 8){
            auto i = (l + r) / 2;
            if(data[i].first < c){
                l = i + 1;
            }
            else if(data[i].first > c){
                r = i;
            }
            else{
                return data[i].second;
            }
        }

        for(size_type i = l; i < r; ++i){
            if(data[i].first == c){
                return data[i].second;
            }
        }

        return default_value;
    }

    void constructPM()
    {
        std::map<symbol_type, std::vector<bitvector_type>> PM_work;
        for(size_type i = 0; i < block_size - 1; ++i){
            for(size_type j = 0; j < bit_width<bitvector_type>(); ++j){
                if(PM_work[pattern[i * bit_width<bitvector_type>() + j]].empty()){
                    PM_work[pattern[i * bit_width<bitvector_type>() + j]].resize(block_size, 0);
                }
                PM_work[pattern[i * bit_width<bitvector_type>() + j]][i] |= bitvector_type{1} << j;
            }
        }
        for(size_type i = 0; i < rest_bits; ++i){
            if(PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].empty()){
                PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].resize(block_size, 0);
            }
            PM_work[pattern[(block_size - 1) * bit_width<bitvector_type>() + i]].back() |= bitvector_type{1} << i;
        }

        PM.clear();
        for(const auto& p: PM_work){
            PM.push_back(p);
        }
        c_min = PM.front().first;
        c_max = PM.back().first;
    }

    distance_type distance_sp(string_type const &text) const
    {
        auto& w = work.front();
        w.reset();
        for(size_type i = 0; i < pattern_length; ++i){
            w.VP |= bitvector_type{1} << i;
        }

        distance_type D = pattern_length;
        for(auto c: text){
            auto X = find_value(PM, c, zeroes).front() | w.VN;

            w.D0 = ((w.VP + (X & w.VP)) ^ w.VP) | X;
            w.HP = w.VN | ~(w.VP | w.D0);
            w.HN = w.VP & w.D0;

            X = (w.HP << 1) | 1;
            w.VP = (w.HN << 1) | ~(X | w.D0);
            w.VN = X & w.D0;

            if(w.HP & sink){
                ++D;
            }
            else if(w.HN & sink){
                --D;
            }
        }
        return D;
    }

    distance_type distance_lp(string_type const &text) const
    {
        constexpr bitvector_type msb = bitvector_type{1} << (bit_width<bitvector_type>() - 1);

        for(auto& w: work){
            w.reset();
        }
        for(size_type i = 0; i < rest_bits; ++i){
            work.back().VP |= bitvector_type{1} << i;
        }

        distance_type D = pattern_length;
        for(auto c: text){
            const auto& PMc = find_value(PM, c, zeroes);
            for(size_type r = 0; r < block_size; ++r){
                auto& w = work[r];
                auto X = PMc[r];
                if(r > 0 && (work[r - 1].HN & msb)){
                    X |= 1;
                }

                w.D0 = ((w.VP + (X & w.VP)) ^ w.VP) | X | w.VN;
                w.HP = w.VN | ~(w.VP | w.D0);
                w.HN = w.VP & w.D0;

                X = w.HP << 1;
                if(r == 0 || work[r - 1].HP & msb){
                    X |= 1;
                }
                w.VP = (w.HN << 1) | ~(X | w.D0);
                if(r > 0 && (work[r - 1].HN & msb)){
                    w.VP |= 1;
                }
                w.VN = X & w.D0;
            }

            if(work.back().HP & sink){
                ++D;
            }
            else if(work.back().HN & sink){
                --D;
            }
        }
        return D;
    }

    distance_type distance(string_type const &text) const
    {
        if(text.empty()){
            return pattern_length;
        }
        else if(pattern_length == 0){
            return text.size();
        }

        if(block_size == 1){
            return distance_sp(text);
        }
        else{
            return distance_lp(text);
        }
    }
};

}
#endif
<|endoftext|>