<commit_before>#pragma once

#include <memory>

#include <anyode/anyode.hpp>
#include <anyode/anyode_blas_lapack.hpp>  // dgemv, dgesvd
#include <anyode/anyode_matrix.hpp> // DenseMatrix
#include <anyode/anyode_buffer.hpp>  // make_unique
#include <anyode/anyode_decomposition.hpp>  // SVD

namespace AnyODE {

    template <typename Real_t=double, typename JacMat_t=DenseMatrix<Real_t>, typename Decomp_t=SVD<Real_t>>
    struct OdeSysIterativeBase : public OdeSysBase<Real_t> {
        int m_njacvec_dot=0, m_nprec_setup=0, m_nprec_solve=0;
        std::unique_ptr<JacMat_t> m_jac_cache {nullptr};
        std::unique_ptr<JacMat_t> m_prec_cache {nullptr};
        bool m_update_prec_cache = false;
        Real_t m_old_gamma;

        virtual Status jac_times_vec(const Real_t * const __restrict__ vec,
                                     Real_t * const __restrict__ out,
                                     Real_t t,
                                     const Real_t * const __restrict__ y,
                                     const Real_t * const __restrict__ fy
                                     ) override
        {
            // See "Jacobian information (matrix-vector product)"
            //     (4.6.8 in cvs_guide.pdf for sundials 2.7.0)
            auto status = AnyODE::Status::success;
            const int ny = this->get_ny();
            if (m_jac_cache == nullptr){
                m_jac_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);
                status = this->dense_jac_cmaj(t, y, fy, m_jac_cache->m_data, m_jac_cache->m_ld);
            }
            m_jac_cache->dot_vec(vec, out);
            m_njacvec_dot++;
            return status;
        }

        virtual Status prec_setup(Real_t t,
                                  const Real_t * const __restrict__ y,
                                  const Real_t * const __restrict__ fy,
                                  bool jac_ok,
                                  bool& jac_recomputed,
                                  Real_t gamma) override
        {
            const int ny = this->get_ny();
            auto status = AnyODE::Status::success;
            ignore(gamma);
            // See "Preconditioning (Jacobian data)" in cvs_guide.pdf (4.6.10 for 2.7.0)
            if (m_jac_cache == nullptr)
                m_jac_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);

            if (jac_ok){
                jac_recomputed = false;
            } else {
                status = this->dense_jac_cmaj(t, y, fy, m_jac_cache->m_data, m_jac_cache->m_ld);
                m_update_prec_cache = true;
                jac_recomputed = true;
            }
            m_nprec_setup++;
            return status;
        }

        virtual Status prec_solve_left(const Real_t t,
                                       const Real_t * const __restrict__ y,
                                       const Real_t * const __restrict__ fy,
                                       const Real_t * const __restrict__ r,
                                       Real_t * const __restrict__ z,
                                       Real_t gamma,
                                       Real_t delta,
                                       const Real_t * const __restrict__ ewt
                                       ) override
        {
            // See 4.6.9 on page 75 in cvs_guide.pdf (Sundials 2.6.2)
            // Solves P*z = r, where P ~= I - gamma*J
            ignore(delta);
            const int ny = this->get_ny();
            if (ewt)
                throw std::runtime_error("Not implemented.");
            m_nprec_solve++;

            ignore(t); ignore(fy); ignore(y);
            bool recompute = false;
            if (m_prec_cache == nullptr){
                m_prec_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);
                recompute = true;
            } else {
                if (m_update_prec_cache or (m_old_gamma != gamma))
                    recompute = true;
            }
            if (recompute){
                m_old_gamma = gamma;
                m_prec_cache->set_to_eye_plus_scaled_mtx(-gamma, *m_jac_cache);
            }
            int info;
            auto decomp = Decomp_t((JacMat_t*)(m_prec_cache.get()));
            info = decomp.solve(r, z);
            if (info == 0)
                return AnyODE::Status::success;
            return AnyODE::Status::recoverable_error;
        }

    };
}
<commit_msg>Recompute jacobian for CVSpilsJacTimesVecFn<commit_after>#pragma once

#include <memory>

#include <anyode/anyode.hpp>
#include <anyode/anyode_blas_lapack.hpp>  // dgemv, dgesvd
#include <anyode/anyode_matrix.hpp> // DenseMatrix
#include <anyode/anyode_buffer.hpp>  // make_unique
#include <anyode/anyode_decomposition.hpp>  // SVD

namespace AnyODE {

    template <typename Real_t=double, typename JacMat_t=DenseMatrix<Real_t>, typename Decomp_t=SVD<Real_t>>
    struct OdeSysIterativeBase : public OdeSysBase<Real_t> {
        int m_njacvec_dot=0, m_nprec_setup=0, m_nprec_solve=0;
        std::unique_ptr<JacMat_t> m_jac_cache {nullptr};
        std::unique_ptr<JacMat_t> m_prec_cache {nullptr};
        bool m_update_prec_cache = false;
        Real_t m_old_gamma;

        virtual Status jac_times_vec(const Real_t * const __restrict__ vec,
                                     Real_t * const __restrict__ out,
                                     Real_t t,
                                     const Real_t * const __restrict__ y,
                                     const Real_t * const __restrict__ fy
                                     ) override
        {
            // See "Jacobian information (matrix-vector product)"
            //     (4.6.8 in cvs_guide.pdf for sundials 2.7.0)
            auto status = AnyODE::Status::success;
            const int ny = this->get_ny();
            if (m_jac_cache == nullptr){
                m_jac_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);
            }
            status = this->dense_jac_cmaj(t, y, fy, m_jac_cache->m_data, m_jac_cache->m_ld);
            m_jac_cache->dot_vec(vec, out);
            m_njacvec_dot++;
            return status;
        }

        virtual Status prec_setup(Real_t t,
                                  const Real_t * const __restrict__ y,
                                  const Real_t * const __restrict__ fy,
                                  bool jac_ok,
                                  bool& jac_recomputed,
                                  Real_t gamma) override
        {
            const int ny = this->get_ny();
            auto status = AnyODE::Status::success;
            ignore(gamma);
            // See "Preconditioning (Jacobian data)" in cvs_guide.pdf (4.6.10 for 2.7.0)
            if (m_jac_cache == nullptr)
                m_jac_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);

            if (jac_ok){
                jac_recomputed = false;
            } else {
                status = this->dense_jac_cmaj(t, y, fy, m_jac_cache->m_data, m_jac_cache->m_ld);
                m_update_prec_cache = true;
                jac_recomputed = true;
            }
            m_nprec_setup++;
            return status;
        }

        virtual Status prec_solve_left(const Real_t t,
                                       const Real_t * const __restrict__ y,
                                       const Real_t * const __restrict__ fy,
                                       const Real_t * const __restrict__ r,
                                       Real_t * const __restrict__ z,
                                       Real_t gamma,
                                       Real_t delta,
                                       const Real_t * const __restrict__ ewt
                                       ) override
        {
            // See 4.6.9 on page 75 in cvs_guide.pdf (Sundials 2.6.2)
            // Solves P*z = r, where P ~= I - gamma*J
            ignore(delta);
            const int ny = this->get_ny();
            if (ewt)
                throw std::runtime_error("Not implemented.");
            m_nprec_solve++;

            ignore(t); ignore(fy); ignore(y);
            bool recompute = false;
            if (m_prec_cache == nullptr){
                m_prec_cache = make_unique<JacMat_t>(nullptr, ny, ny, ny, true);
                recompute = true;
            } else {
                if (m_update_prec_cache or (m_old_gamma != gamma))
                    recompute = true;
            }
            if (recompute){
                m_old_gamma = gamma;
                m_prec_cache->set_to_eye_plus_scaled_mtx(-gamma, *m_jac_cache);
            }
            int info;
            auto decomp = Decomp_t((JacMat_t*)(m_prec_cache.get()));
            info = decomp.solve(r, z);
            if (info == 0)
                return AnyODE::Status::success;
            return AnyODE::Status::recoverable_error;
        }

    };
}
<|endoftext|>


<commit_before>namespace gen {

/** \brief The Population template.
 *
 * Requires a `Candidate` class derived from ICandidate and implementing a
 * default constructor. */
template<class Candidate>
class Population : private std::vector<Candidate> {
  bool sorted = false;
  std::mutex mtx{};

  static_assert(std::is_default_constructible<Candidate>::value,
      "The Candidate type needs to provide a default constructor.");

  typedef decltype(internal::detectFT<Candidate>(nullptr)) _FitnessType;

  static_assert(internal::hasFT<Candidate>(nullptr) &&
      std::is_base_of<ICandidate<_FitnessType>, Candidate>::value,
      "The Candidate type needs to be derived from ICandidate.");

  public:
  /** \brief Creates an empty population. */
  Population() = default;

  /** \brief Creates an empty population but preallocate space for count
   * candidates. */
  Population(size_t count) {
    this->reserve(count);
  }

  /** \brief Creates a population of size `count` whose candidates are results
   * of calls to the source function `src`.
   *
   * For discussion about the latter parameter see add(size_t, Source).
   *
   * \see add(size_t, Source) */
  template<class Source>
  Population(size_t count, Source src) {
    add(count, src);
  }

  /* The Big Four: trivial but we need them because the mutex can't be
   * default copied or moved */

  /** \brief The copy constructor. */
  Population(const Population& _p): std::vector<Candidate>(_p), sorted(_p.sorted) { }

  /** \brief The move constructor. */
  Population(Population&& _p): std::vector<Candidate>(std::move(_p)), sorted(_p.sorted) { }

  /** \brief Copy assignment operator. */
  Population& operator=(const Population& _p) {
    std::lock_guard<std::mutex> lock(mtx);
    std::vector<Candidate>::operator=(_p);
    sorted = _p.sorted;
    return *this;
  }

  /** \brief Move assignment operator. */
  Population& operator=(Population&& _p) {
    std::lock_guard<std::mutex> lock(mtx);
    std::vector<Candidate>::operator=(std::move(_p));
    sorted = _p.sorted;
    return *this;
  }

  /** \brief Adds a new candidate. */
  void add(const Candidate& c) {
    std::lock_guard<std::mutex> lock(mtx);
    this->push_back(c);
    sorted = false;
  }

  /** \brief Pushes back a new candidate using the move semantics. */
  void add(Candidate&& c) {
    std::lock_guard<std::mutex> lock(mtx);
    this->push_back(std::forward<Candidate>(c));
    sorted = false;
  }

  /** \brief Draws `count` candidates from a source function `src`.
   *
   * Source can be:
   * - `std::function<(const) Candidate>`: returning by copy,
   * - `std::function<(const) Candidate&>`: returning by reference,
   * - a pointer to function returning `Candidate` or `Candidate&`,
   * - a lambda function returning either.
   *
   * The template allows for optimizations (inlining) in the latter case. */
  template<class Source>
  void NOINLINE add(size_t count, Source src) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + count);
    for(size_t j = 0; j < count; j++)
      this->push_back(src());
    sorted = false;
  }

  /** \brief Copies all candidates from a vector of `Candidate`s. */
  void NOINLINE add(const std::vector<Candidate>& vec) {
    add(vec.begin(), vec.end());
  }

  /** \brief Copies an iterator range from a container of `Candidate`s. */
  template<class InputIt>
  void add(InputIt first, InputIt last) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + std::distance(first, last));
    this->insert(end(), first, last);
    sorted = false;
  }

  /** \brief Moves all candidates from a vector of `Candidate`s. */
  void NOINLINE add(std::vector<Candidate>&& vec) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + vec.size());
    this->insert(end(), std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()));
    vec.clear();
    sorted = false;
  }

  /** \brief Copies all candidates from another Population. */
  void add(const Population<Candidate>& pop) {
    return merge(static_cast<const std::vector<Candidate>&>(pop));
  }

  /** \brief Moves all candidates from another Population. */
  void add(Population<Candidate>&& pop) {
    return add(static_cast<std::vector<Candidate>&&>(pop));
  }

  using std::vector<Candidate>::begin;
  using std::vector<Candidate>::end;
  using std::vector<Candidate>::size;
  using std::vector<Candidate>::clear;
  using std::vector<Candidate>::operator[];

  /** \brief Retrieves a candidate randomly chosen by rank-based selection.
   *
   * This method accepts as a template parameter a name of a function
   * `double(double)` that will receive arguments linearly spaced between
   * `1/size * bias` and `bias` for candidates ranked `1` through `size` and
   * its return value will be interpreted as inverse probability, and as such,
   * is expected to be positive and strictly increasing in its argument. This
   * function will be built in at compile time, eliminating a function pointer
   * lookup.  The default value is `std::exp`, for which an equivalent fast
   * replacement algorithm is provided.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied.
   *
   * \param bias > 0 determines how much low-fitness solutions are preferred.
   * Zero would mean no account on fitness in the selection process
   * whatsoever. The bigger the value the more candidates with low fitness are
   * likely to be selected.
   * \param rng the random number generator, or gen::rng by default.
   *
   * \returns a constant reference to a randomly chosen `Candidate`. */
  template<double (*fun)(double) = std::exp, class Rng = decltype(rng)>
  const Candidate& NOINLINE rankSelect(float bias, Rng& rng = rng) {
    if(internal::is_exp<fun>::value)
      return rankSelect_exp(bias, rng);
    else
      return rankSelect<
        static_cast<double(*)(double, double)>(&internal::eval_in_product<fun>)
        > (bias);
  }

  /** \brief Retrieves a candidate randomly chosen by rank-based selection.
   *
   * This method accepts as a template parameter a name of a function
   * `double(double, double)` that will receive its first argument linearly
   * spaced between `1/size` and `1` for candidates ranked `1` through `size`
   * and second argument equal to `bias` and its return value will be
   * interpreted as inverse probability. As such, is expected to be positive
   * and strictly increasing in its argument. This function will be built in
   * at compile time, eliminating a function pointer lookup. A usual choice
   * for `fun` is `std::pow`.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied.
   *
   * \param bias > 0 determines how much low-fitness solutions are preferred.
   * Zero would mean no account on fitness in the selection process
   * whatsoever. The bigger the value the more candidates with low fitness are
   * likely to be selected.
   * \param rng the random number generator, or gen::rng by default.
   *
   * \returns a constant reference to a randomly chosen `Candidate`. */
  template<double (*fun)(double, double), class Rng = decltype(rng)>
  const Candidate& NOINLINE rankSelect(double bias, Rng& rng = rng) {
    static thread_local std::discrete_distribution<size_t> iDist{};
    static thread_local size_t last_sz = 0;
    static thread_local std::vector<double> probs{};
    size_t sz = size();
    if(sz != last_sz) {
      probs.clear();
      probs.reserve(sz);
      for(size_t i = 0; i < sz; i++)
        probs.push_back(1 / fun((double)(i+1) / sz, bias));
      iDist = std::discrete_distribution<size_t>(probs.begin(), probs.end());
      last_sz = sz;
    }
    ensureSorted();
    return (*this)[iDist(rng)];
  }

  private:
  template<class Rng>
  const Candidate& rankSelect_exp(double bias, Rng& rng = rng) {
    static thread_local std::uniform_real_distribution<double> rDist(0, 1);
    ensureSorted();
    double x = rDist(rng);
    if(x == 1)
      return this->back();
    else
      return (*this)[(int)(-log(1 - x + x*exp(-bias))/bias*size())];
  }

  public:

  /** \brief Returns the best candidate of population.
   *
   * If more candidates have equal best fitness the returned reference may be
   * any of them.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied. */
  const Candidate& best() {
    ensureSorted();
    return this->front();
  }

  /** \brief Reduces the population to a maximum size given by the argument,
   * dropping the worst part of the sample.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied. */
  Population<Candidate>& trim(size_t newSize) {
    ensureSorted();
    std::lock_guard<std::mutex> lock(mtx);
    if(size() > newSize)
      this->resize(newSize);
    return *this;
  }

  /** \brief The return type of stat(). */
  struct Stat {
    double mean;  ///< The mean fitness of the Population.
    double stdev; ///< The standard deviation of fitness in the Population.
  };

  /** \brief Returns the mean fitness of the population and the standard
   * deviation.
   *
   * Applicable only to candidate classes whose fitness is a simple floating
   * point type or allows an implicit convertion to one. This method
   * generates an error at compile time in specializations for which this
   * condition is not satisfied. */
  Stat stat() {
    static_assert(std::is_convertible<_FitnessType, double>::value,
        "This method requires the fitness type to be convertible to double.");
    double f, sf = 0, sf2 = 0;
    for(Candidate &c : *this) {
      f = c.fitness();
      sf += f;
      sf2 += f*f;
    }
    size_t sz = size();
    double dev2 = sf2/sz - sf/sz*sf/sz;
    return Stat{sf/sz, dev2 >= 0 ? sqrt(dev2) : 0};
  }

  private:
  void ensureSorted() {
    static_assert(internal::comparable<_FitnessType>(0),
        "This method requires the fitness type to implement an operator<.");
    std::lock_guard<std::mutex> lock(mtx);
    if(!sorted) {
      std::sort(begin(), end());
      sorted = true;
    }
  }
}; // class Population

} // namespace gen
<commit_msg>Fix old call to merge()<commit_after>namespace gen {

/** \brief The Population template.
 *
 * Requires a `Candidate` class derived from ICandidate and implementing a
 * default constructor. */
template<class Candidate>
class Population : private std::vector<Candidate> {
  bool sorted = false;
  std::mutex mtx{};

  static_assert(std::is_default_constructible<Candidate>::value,
      "The Candidate type needs to provide a default constructor.");

  typedef decltype(internal::detectFT<Candidate>(nullptr)) _FitnessType;

  static_assert(internal::hasFT<Candidate>(nullptr) &&
      std::is_base_of<ICandidate<_FitnessType>, Candidate>::value,
      "The Candidate type needs to be derived from ICandidate.");

  public:
  /** \brief Creates an empty population. */
  Population() = default;

  /** \brief Creates an empty population but preallocate space for count
   * candidates. */
  Population(size_t count) {
    this->reserve(count);
  }

  /** \brief Creates a population of size `count` whose candidates are results
   * of calls to the source function `src`.
   *
   * For discussion about the latter parameter see add(size_t, Source).
   *
   * \see add(size_t, Source) */
  template<class Source>
  Population(size_t count, Source src) {
    add(count, src);
  }

  /* The Big Four: trivial but we need them because the mutex can't be
   * default copied or moved */

  /** \brief The copy constructor. */
  Population(const Population& _p): std::vector<Candidate>(_p), sorted(_p.sorted) { }

  /** \brief The move constructor. */
  Population(Population&& _p): std::vector<Candidate>(std::move(_p)), sorted(_p.sorted) { }

  /** \brief Copy assignment operator. */
  Population& operator=(const Population& _p) {
    std::lock_guard<std::mutex> lock(mtx);
    std::vector<Candidate>::operator=(_p);
    sorted = _p.sorted;
    return *this;
  }

  /** \brief Move assignment operator. */
  Population& operator=(Population&& _p) {
    std::lock_guard<std::mutex> lock(mtx);
    std::vector<Candidate>::operator=(std::move(_p));
    sorted = _p.sorted;
    return *this;
  }

  /** \brief Adds a new candidate. */
  void add(const Candidate& c) {
    std::lock_guard<std::mutex> lock(mtx);
    this->push_back(c);
    sorted = false;
  }

  /** \brief Pushes back a new candidate using the move semantics. */
  void add(Candidate&& c) {
    std::lock_guard<std::mutex> lock(mtx);
    this->push_back(std::forward<Candidate>(c));
    sorted = false;
  }

  /** \brief Draws `count` candidates from a source function `src`.
   *
   * Source can be:
   * - `std::function<(const) Candidate>`: returning by copy,
   * - `std::function<(const) Candidate&>`: returning by reference,
   * - a pointer to function returning `Candidate` or `Candidate&`,
   * - a lambda function returning either.
   *
   * The template allows for optimizations (inlining) in the latter case. */
  template<class Source>
  void NOINLINE add(size_t count, Source src) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + count);
    for(size_t j = 0; j < count; j++)
      this->push_back(src());
    sorted = false;
  }

  /** \brief Copies all candidates from a vector of `Candidate`s. */
  void NOINLINE add(const std::vector<Candidate>& vec) {
    add(vec.begin(), vec.end());
  }

  /** \brief Copies an iterator range from a container of `Candidate`s. */
  template<class InputIt>
  void add(InputIt first, InputIt last) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + std::distance(first, last));
    this->insert(end(), first, last);
    sorted = false;
  }

  /** \brief Moves all candidates from a vector of `Candidate`s. */
  void NOINLINE add(std::vector<Candidate>&& vec) {
    std::lock_guard<std::mutex> lock(mtx);
    this->reserve(size() + vec.size());
    this->insert(end(), std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()));
    vec.clear();
    sorted = false;
  }

  /** \brief Copies all candidates from another Population. */
  void add(const Population<Candidate>& pop) {
    return add(static_cast<const std::vector<Candidate>&>(pop));
  }

  /** \brief Moves all candidates from another Population. */
  void add(Population<Candidate>&& pop) {
    return add(static_cast<std::vector<Candidate>&&>(pop));
  }

  using std::vector<Candidate>::begin;
  using std::vector<Candidate>::end;
  using std::vector<Candidate>::size;
  using std::vector<Candidate>::clear;
  using std::vector<Candidate>::operator[];

  /** \brief Retrieves a candidate randomly chosen by rank-based selection.
   *
   * This method accepts as a template parameter a name of a function
   * `double(double)` that will receive arguments linearly spaced between
   * `1/size * bias` and `bias` for candidates ranked `1` through `size` and
   * its return value will be interpreted as inverse probability, and as such,
   * is expected to be positive and strictly increasing in its argument. This
   * function will be built in at compile time, eliminating a function pointer
   * lookup.  The default value is `std::exp`, for which an equivalent fast
   * replacement algorithm is provided.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied.
   *
   * \param bias > 0 determines how much low-fitness solutions are preferred.
   * Zero would mean no account on fitness in the selection process
   * whatsoever. The bigger the value the more candidates with low fitness are
   * likely to be selected.
   * \param rng the random number generator, or gen::rng by default.
   *
   * \returns a constant reference to a randomly chosen `Candidate`. */
  template<double (*fun)(double) = std::exp, class Rng = decltype(rng)>
  const Candidate& NOINLINE rankSelect(float bias, Rng& rng = rng) {
    if(internal::is_exp<fun>::value)
      return rankSelect_exp(bias, rng);
    else
      return rankSelect<
        static_cast<double(*)(double, double)>(&internal::eval_in_product<fun>)
        > (bias);
  }

  /** \brief Retrieves a candidate randomly chosen by rank-based selection.
   *
   * This method accepts as a template parameter a name of a function
   * `double(double, double)` that will receive its first argument linearly
   * spaced between `1/size` and `1` for candidates ranked `1` through `size`
   * and second argument equal to `bias` and its return value will be
   * interpreted as inverse probability. As such, is expected to be positive
   * and strictly increasing in its argument. This function will be built in
   * at compile time, eliminating a function pointer lookup. A usual choice
   * for `fun` is `std::pow`.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied.
   *
   * \param bias > 0 determines how much low-fitness solutions are preferred.
   * Zero would mean no account on fitness in the selection process
   * whatsoever. The bigger the value the more candidates with low fitness are
   * likely to be selected.
   * \param rng the random number generator, or gen::rng by default.
   *
   * \returns a constant reference to a randomly chosen `Candidate`. */
  template<double (*fun)(double, double), class Rng = decltype(rng)>
  const Candidate& NOINLINE rankSelect(double bias, Rng& rng = rng) {
    static thread_local std::discrete_distribution<size_t> iDist{};
    static thread_local size_t last_sz = 0;
    static thread_local std::vector<double> probs{};
    size_t sz = size();
    if(sz != last_sz) {
      probs.clear();
      probs.reserve(sz);
      for(size_t i = 0; i < sz; i++)
        probs.push_back(1 / fun((double)(i+1) / sz, bias));
      iDist = std::discrete_distribution<size_t>(probs.begin(), probs.end());
      last_sz = sz;
    }
    ensureSorted();
    return (*this)[iDist(rng)];
  }

  private:
  template<class Rng>
  const Candidate& rankSelect_exp(double bias, Rng& rng = rng) {
    static thread_local std::uniform_real_distribution<double> rDist(0, 1);
    ensureSorted();
    double x = rDist(rng);
    if(x == 1)
      return this->back();
    else
      return (*this)[(int)(-log(1 - x + x*exp(-bias))/bias*size())];
  }

  public:

  /** \brief Returns the best candidate of population.
   *
   * If more candidates have equal best fitness the returned reference may be
   * any of them.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied. */
  const Candidate& best() {
    ensureSorted();
    return this->front();
  }

  /** \brief Reduces the population to a maximum size given by the argument,
   * dropping the worst part of the sample.
   *
   * Applicable only if the fitness type of `Candidate` allows total ordering
   * using `operator<`. This method generates an error at compile time in
   * specializations for which this condition is not satisfied. */
  Population<Candidate>& trim(size_t newSize) {
    ensureSorted();
    std::lock_guard<std::mutex> lock(mtx);
    if(size() > newSize)
      this->resize(newSize);
    return *this;
  }

  /** \brief The return type of stat(). */
  struct Stat {
    double mean;  ///< The mean fitness of the Population.
    double stdev; ///< The standard deviation of fitness in the Population.
  };

  /** \brief Returns the mean fitness of the population and the standard
   * deviation.
   *
   * Applicable only to candidate classes whose fitness is a simple floating
   * point type or allows an implicit convertion to one. This method
   * generates an error at compile time in specializations for which this
   * condition is not satisfied. */
  Stat stat() {
    static_assert(std::is_convertible<_FitnessType, double>::value,
        "This method requires the fitness type to be convertible to double.");
    double f, sf = 0, sf2 = 0;
    for(Candidate &c : *this) {
      f = c.fitness();
      sf += f;
      sf2 += f*f;
    }
    size_t sz = size();
    double dev2 = sf2/sz - sf/sz*sf/sz;
    return Stat{sf/sz, dev2 >= 0 ? sqrt(dev2) : 0};
  }

  private:
  void ensureSorted() {
    static_assert(internal::comparable<_FitnessType>(0),
        "This method requires the fitness type to implement an operator<.");
    std::lock_guard<std::mutex> lock(mtx);
    if(!sorted) {
      std::sort(begin(), end());
      sorted = true;
    }
  }
}; // class Population

} // namespace gen
<|endoftext|>


<commit_before>/*
 * StoichiometryView.hpp
 *
 *  Copyright (C) 2012 Marc Kirchner
 *
 */
#ifndef __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__
#define __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__

namespace ipaca {

template<typename T>
struct StoichiometryTraits
{
    typedef typename T::iterator iterator_type;
    typedef typename T::const_iterator const_iterator_type;
    typedef typename T::value_type value_type;
};

template<typename T>
struct StoichiometryValueTraits
{
    // must be specified by the user
};

/** A generic view on stoichiometries.
 * This class declares the interface that \c libipaca expects from a
 * stoichiometry. The client is required to specify stoichiometry
 * propertie (iterator types, value types and accessor functors in the
 * respective trait classes \c StoichiometryTraits and
 * \c StoichiometryValueTraits.
 */
template<typename T>
class StoichiometryView
{
public:
    typedef typename StoichiometryTraits<T>::iterator_type iterator;
    typedef typename StoichiometryTraits<T>::const_iterator_type
            const_iterator;

    StoichiometryView(T&);
    StoichiometryView(const StoichiometryView<T>& rhs);

    const_iterator begin() const;
    const_iterator end() const;

    // TODO: figure out if we REALLY need the non-const iterator. Eliminate if possible.
    iterator begin();
    iterator end();

    const T& ref() const;
    bool isNonNegative() const;

private:
    T& stoi_;
};

}

//
// template implementation
//

template<typename T>
ipaca::StoichiometryView<T>::StoichiometryView(T& stoi) :
    stoi_(stoi)
{
}

template<typename T>
ipaca::StoichiometryView<T>::StoichiometryView(const StoichiometryView<T>& rhs) :
    stoi_(rhs.stoi_)
{
}

template<typename T>
typename ipaca::StoichiometryView<T>::const_iterator ipaca::StoichiometryView<
        T>::begin() const
{
    return stoi_.begin();
}

template<typename T>
typename ipaca::StoichiometryView<T>::const_iterator ipaca::StoichiometryView<
        T>::end() const
{
    return stoi_.end();
}

template<typename T>
typename ipaca::StoichiometryView<T>::iterator ipaca::StoichiometryView<T>::begin()
{
    return stoi_.begin();
}

template<typename T>
typename ipaca::StoichiometryView<T>::iterator ipaca::StoichiometryView<T>::end()
{
    return stoi_.end();
}

template<typename T>
const T& ipaca::StoichiometryView<T>::ref() const
{
    return stoi_;
}

#endif /* __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__ */
<commit_msg>adding typeinfo for the viewed type<commit_after>/*
 * StoichiometryView.hpp
 *
 *  Copyright (C) 2012 Marc Kirchner
 *
 */
#ifndef __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__
#define __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__

namespace ipaca {

template<typename T>
struct StoichiometryTraits
{
    typedef typename T::iterator iterator_type;
    typedef typename T::const_iterator const_iterator_type;
    typedef typename T::value_type value_type;
};

template<typename T>
struct StoichiometryValueTraits
{
    // must be specified by the user
};

/** A generic view on stoichiometries.
 * This class declares the interface that \c libipaca expects from a
 * stoichiometry. The client is required to specify stoichiometry
 * propertie (iterator types, value types and accessor functors in the
 * respective trait classes \c StoichiometryTraits and
 * \c StoichiometryValueTraits.
 */
template<typename T>
class StoichiometryView
{
public:
    typedef T viewed_type;
    typedef typename StoichiometryTraits<T>::iterator_type iterator;
    typedef typename StoichiometryTraits<T>::const_iterator_type
            const_iterator;

    StoichiometryView(T&);
    StoichiometryView(const StoichiometryView<T>& rhs);

    const_iterator begin() const;
    const_iterator end() const;

    // TODO: figure out if we REALLY need the non-const iterator. Eliminate if possible.
    iterator begin();
    iterator end();

    const T& ref() const;
    bool isNonNegative() const;

private:
    T& stoi_;
};

}

//
// template implementation
//

template<typename T>
ipaca::StoichiometryView<T>::StoichiometryView(T& stoi) :
    stoi_(stoi)
{
}

template<typename T>
ipaca::StoichiometryView<T>::StoichiometryView(const StoichiometryView<T>& rhs) :
    stoi_(rhs.stoi_)
{
}

template<typename T>
typename ipaca::StoichiometryView<T>::const_iterator ipaca::StoichiometryView<
        T>::begin() const
{
    return stoi_.begin();
}

template<typename T>
typename ipaca::StoichiometryView<T>::const_iterator ipaca::StoichiometryView<
        T>::end() const
{
    return stoi_.end();
}

template<typename T>
typename ipaca::StoichiometryView<T>::iterator ipaca::StoichiometryView<T>::begin()
{
    return stoi_.begin();
}

template<typename T>
typename ipaca::StoichiometryView<T>::iterator ipaca::StoichiometryView<T>::end()
{
    return stoi_.end();
}

template<typename T>
const T& ipaca::StoichiometryView<T>::ref() const
{
    return stoi_;
}

#endif /* __LIBIPACA_INCLUDE_IPACA_STOICHIOMETRYVIEW_HPP__ */
<|endoftext|>


<commit_before>#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/bind/bind_member_function.hpp>

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

/**
	Parser and executer of my primitive (not Turing complete) assembly-like language

	Example:

	create function_name(param1, param2,...) {
		create x
		create y
		setval x 5
		setval y 10
		add y y
		mul x y
		sub y x
		div x y
		setvar param1 x
		setvar param2 y
		print x
	}
*/

#define BIND_VALUE(member_method, bind_point) phoenix::bind(&member_method, &bind_point, qi::_1)

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

namespace L
{
	class Function {
	private:

		struct Command {
			std::string commandName;
			std::string param1;
			std::string param2;

			Command(const std::string& cmdName)
				: commandName(cmdName) {}
		};

		bool isValid = true;
		std::string name;
		std::vector<std::string> param;
		std::vector<Command> commands;

	public:

		Function() = default;

		void SetName(const std::string& n) {
			//std::cout << "setting name " << n << std::endl;
			name = n;
		}

		void AddFunctionParameter(const std::string& p) {
			//std::cout << "adding function parameter " << p << std::endl;
			param.push_back(p);
		}

		void AddCommand(const std::string& c) {
			//std::cout << "adding command " << c << std::endl;
			commands.emplace_back(c);
		}

		void AddCommandParameter(const std::string& p) {
			//std::cout << "adding command parameter " << p << std::endl;
			if (!commands.empty()) {
				auto& b = commands.back();
				if (b.param1.empty()) {
					b.param1 = p;
				}
				else if (b.param2.empty()) {
					b.param2 = p;
				}
				else {
					isValid = false;
				}
			}
		}

		bool CheckValidity() {
			if (name.empty()) {
				isValid = false;
			}
			return isValid;
		}
	};

	template<typename It>
	class FunctionParser : public qi::grammar<It, std::string(), ascii::space_type>
	{
	private:

		qi::rule<It, std::string(), ascii::space_type> startRule;
		qi::rule<It, std::string(), ascii::space_type> paramRule;
		qi::rule<It, std::string(), ascii::space_type> bodyRule;

		Function func;

	public:

		FunctionParser() : FunctionParser::base_type(startRule)
		{
			startRule =
				qi::lit("create")
				>> qi::as_string[+(qi::alnum - qi::char_('('))][BIND_VALUE(Function::SetName, func)]
				>> '('
				>> *paramRule
				>> qi::char_(')')
				>> qi::char_('{')
				>> -bodyRule
				>> qi::char_('}');

			paramRule =
				qi::as_string[+(qi::alnum)][BIND_VALUE(Function::AddFunctionParameter, func)]
				>> -qi::char_(',');

			bodyRule =
				qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommand, func)]
				>> -qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommandParameter, func)]
				>> -qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommandParameter, func)]
				>> -(qi::char_(',') >> bodyRule);
		}

		Function& GetParsedFunction() {
			return func;
		}
	};
}

template<typename It>
void ParseAndRun(It first, It end)
{
	L::FunctionParser<It> parser;
	bool succ = qi::phrase_parse(first, end, parser, ascii::space, std::string());
	auto& func = parser.GetParsedFunction();

	// TODO check first iterator
	// TODO replace first iterator with begin
	if (succ && func.CheckValidity()) {
		std::cout << "Parsing successful" << std::endl;
	}
	else {
		std::cout << "Parsing failed" << std::endl;
	}
}

int main() {
	std::string line;

	do {
		std::getline(std::cin, line);
		ParseAndRun(line.cbegin(), line.cend());
	} while (!line.empty());

	return 0;
}
<commit_msg>qi::char_ update<commit_after>#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/bind/bind_member_function.hpp>

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

/**
	Parser and executer of my primitive (not Turing complete) assembly-like language

	Example:

	create function_name(param1, param2,...) {
		create x
		create y
		setval x 5
		setval y 10
		add y y
		mul x y
		sub y x
		div x y
		setvar param1 x
		setvar param2 y
		print x
	}
*/

#define BIND_VALUE(member_method, bind_point) phoenix::bind(&member_method, &bind_point, qi::_1)

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

namespace L
{
	class Function {
	private:

		struct Command {
			std::string commandName;
			std::string param1;
			std::string param2;

			Command(const std::string& cmdName)
				: commandName(cmdName) {}
		};

		bool isValid = true;
		std::string name;
		std::vector<std::string> param;
		std::vector<Command> commands;

	public:

		Function() = default;

		void SetName(const std::string& n) {
			//std::cout << "setting name " << n << std::endl;
			name = n;
		}

		void AddFunctionParameter(const std::string& p) {
			//std::cout << "adding function parameter " << p << std::endl;
			param.push_back(p);
		}

		void AddCommand(const std::string& c) {
			//std::cout << "adding command " << c << std::endl;
			commands.emplace_back(c);
		}

		void AddCommandParameter(const std::string& p) {
			//std::cout << "adding command parameter " << p << std::endl;
			if (!commands.empty()) {
				auto& b = commands.back();
				if (b.param1.empty()) {
					b.param1 = p;
				}
				else if (b.param2.empty()) {
					b.param2 = p;
				}
				else {
					isValid = false;
				}
			}
		}

		bool CheckValidity() {
			if (name.empty()) {
				isValid = false;
			}
			return isValid;
		}
	};

	template<typename It>
	class FunctionParser : public qi::grammar<It, std::string(), ascii::space_type>
	{
	private:

		qi::rule<It, std::string(), ascii::space_type> startRule;
		qi::rule<It, std::string(), ascii::space_type> paramRule;
		qi::rule<It, std::string(), ascii::space_type> bodyRule;

		Function func;

	public:

		FunctionParser() : FunctionParser::base_type(startRule)
		{
			startRule =
				qi::lit("create")
				>> qi::as_string[+(qi::alnum - qi::char_('('))][BIND_VALUE(Function::SetName, func)]
				>> qi::char_('(')
				>> *paramRule
				>> qi::char_(')')
				>> qi::char_('{')
				>> -bodyRule
				>> qi::char_('}');

			paramRule =
				qi::as_string[+(qi::alnum)][BIND_VALUE(Function::AddFunctionParameter, func)]
				>> -qi::char_(',');

			bodyRule =
				qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommand, func)]
				>> -qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommandParameter, func)]
				>> -qi::as_string[qi::lexeme[+qi::alnum - qi::char_('}')]][BIND_VALUE(Function::AddCommandParameter, func)]
				>> -(qi::char_(',') >> bodyRule);
		}

		Function& GetParsedFunction() {
			return func;
		}
	};
}

template<typename It>
void ParseAndRun(It first, It end)
{
	L::FunctionParser<It> parser;
	bool succ = qi::phrase_parse(first, end, parser, ascii::space, std::string());
	auto& func = parser.GetParsedFunction();

	// TODO check first iterator
	// TODO replace first iterator with begin
	if (succ && func.CheckValidity()) {
		std::cout << "Parsing successful" << std::endl;
	}
	else {
		std::cout << "Parsing failed" << std::endl;
	}
}

int main() {
	std::string line;

	do {
		std::getline(std::cin, line);
		ParseAndRun(line.cbegin(), line.cend());
	} while (!line.empty());

	return 0;
}
<|endoftext|>


<commit_before>/**
*  Copyright (C) 2016 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>

#include <gtest/gtest.h>

#include <repo/core/model/bson/repo_node_transformation.h>
#include <repo/core/model/bson/repo_bson_builder.h>
#include "../../../../repo_test_utils.h"

using namespace repo::core::model;

std::vector<float> identity =
{ 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };

std::vector<float> notId =
{ 1, 2, 3, 4,
5, 6, 7, 8,
9, 0.3f, 10, 11,
5342, 31, 0.6f, 12 };

std::vector<float> idInBoundary =
{ 1, 0, 0, 0,
0, 1, 0, (float)1e-6,
0, 0, 1, 0,
0, (float)1e-6, (float)1e-6, 1 };

std::vector<float> notIdInBoundary = { 1, 0, 0, 0,
0, 1, 0, (float)2e-5,
0, 0, 2, 0,
0, (float)2e-5, (float)2e-5, 1 };

TransformationNode makeTransformationNode(
	const std::vector<float> &matrix)
{
	RepoBSONBuilder bsonBuilder;
	RepoBSONBuilder rows;
	for (uint32_t i = 0; i < 4; ++i)
	{
		RepoBSONBuilder columns;
		for (uint32_t j = 0; j < 4; ++j){
			columns << std::to_string(j) << matrix[i * 4 + j];
		}
		rows.appendArray(std::to_string(i), columns.obj());
	}
	bsonBuilder.appendArray(REPO_NODE_LABEL_MATRIX, rows.obj());

	return bsonBuilder.obj();
}

TEST(RepoTransformationNodeTest, Constructor)
{
	auto empty = TransformationNode();

	EXPECT_TRUE(empty.isEmpty());
	EXPECT_EQ(NodeType::TRANSFORMATION, empty.getTypeAsEnum());

	auto repoBson = RepoBSON(BSON("test" << "blah" << "test2" << 2));

	auto fromRepoBSON = TransformationNode(repoBson);
	EXPECT_EQ(NodeType::TRANSFORMATION, fromRepoBSON.getTypeAsEnum());
	EXPECT_EQ(fromRepoBSON.nFields(), repoBson.nFields());
	EXPECT_EQ(0, fromRepoBSON.getFileList().size());
}

TEST(RepoTransformationNodeTest, IdentityTest)
{
	auto empty = TransformationNode();
	EXPECT_TRUE(empty.isIdentity());

	EXPECT_TRUE(makeTransformationNode(identity).isIdentity());
	EXPECT_TRUE(makeTransformationNode(idInBoundary).isIdentity());
	EXPECT_FALSE(makeTransformationNode(notId).isIdentity());
	EXPECT_FALSE(makeTransformationNode(notIdInBoundary).isIdentity());
}

TEST(RepoTransformationNodeTest, IdentityTest2)
{
	auto identity = TransformationNode::identityMat();

	ASSERT_EQ(4, identity.size());
	for (int i = 0; i < 4; ++i)
	{
		ASSERT_EQ(4, identity[i].size());
		for (int j = 0; j < 4; ++j)
		{
			float expectedOutcome = i % 4 == j ? 1 : 0;
			EXPECT_EQ(expectedOutcome, identity[i][j]);
		}
	}
}

TEST(RepoTransformationNodeTest, TypeTest)
{
	TransformationNode node = TransformationNode();

	EXPECT_EQ(REPO_NODE_TYPE_TRANSFORMATION, node.getType());
	EXPECT_EQ(NodeType::TRANSFORMATION, node.getTypeAsEnum());
}

TEST(RepoTransformationNodeTest, PositionDependantTest)
{
	TransformationNode node = TransformationNode();
	//transformation node should always be position dependant
	EXPECT_TRUE(node.positionDependant());
}

TEST(RepoTransformationNodeTest, SEqualTest)
{
	auto empty1 = TransformationNode();
	auto empty2 = TransformationNode();

	auto notEmpty1 = makeTransformationNode(notId);
	auto notEmpty2 = makeTransformationNode(notId);
	auto notEmpty3 = makeTransformationNode(identity);

	EXPECT_TRUE(empty1.sEqual(empty2));
	EXPECT_TRUE(empty2.sEqual(empty1));
	EXPECT_TRUE(empty1.sEqual(empty1));
	EXPECT_TRUE(notEmpty1.sEqual(notEmpty2));
	EXPECT_TRUE(notEmpty2.sEqual(notEmpty1));
	EXPECT_FALSE(notEmpty1.sEqual(empty2));
	EXPECT_TRUE(notEmpty3.sEqual(notEmpty3));
	EXPECT_FALSE(notEmpty3.sEqual(notEmpty2));
	EXPECT_FALSE(empty1.sEqual(notEmpty2));
}

TEST(RepoTransformationNodeTest, CloneAndApplyTransformationTest)
{
	auto empty = TransformationNode();

	TransformationNode modifiedEmpty = empty.cloneAndApplyTransformation(notId);

	EXPECT_EQ(empty.getTransMatrix(false), identity);
	EXPECT_EQ(modifiedEmpty.getTransMatrix(false), notId);

	auto filled = makeTransformationNode(notId);
	TransformationNode modifiedFilled = filled.cloneAndApplyTransformation(std::vector<float>());

	EXPECT_EQ(modifiedFilled.getTransMatrix(false).getData(), notId);
}

TEST(RepoTransformationNodeTest, GetTransMatrixTest)
{
	TransformationNode empty = TransformationNode();
	EXPECT_EQ(identity, empty.getTransMatrix(false));

	TransformationNode notEmpty = makeTransformationNode(notId);
	EXPECT_EQ(notId, notEmpty.getTransMatrix(false));

	//check transpose is done correctly
	auto notIdTransposed = notEmpty.getTransMatrix(true);

	auto notIdTransData = notIdTransposed.getData();
	ASSERT_EQ(notId.size(), notIdTransData.size());
	for (int i = 0; i < 4; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			int index = i * 4 + j;
			int transIndex = j * 4 + i;
			EXPECT_EQ(notId[index], notIdTransData[transIndex]);
		}
	}
}<commit_msg>#48 small commit<commit_after>/**
*  Copyright (C) 2016 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>

#include <gtest/gtest.h>

#include <repo/core/model/bson/repo_node_transformation.h>
#include <repo/core/model/bson/repo_bson_builder.h>
#include "../../../../repo_test_utils.h"

using namespace repo::core::model;

std::vector<float> identity =
{ 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };

std::vector<float> notId =
{ 1, 2, 3, 4,
5, 6, 7, 8,
9, 0.3f, 10, 11,
5342, 31, 0.6f, 12 };

std::vector<float> idInBoundary =
{ 1, 0, 0, 0,
0, 1, 0, (float)1e-6,
0, 0, 1, 0,
0, (float)1e-6, (float)1e-6, 1 };

std::vector<float> notIdInBoundary = { 1, 0, 0, 0,
0, 1, 0, (float)2e-5,
0, 0, 2, 0,
0, (float)2e-5, (float)2e-5, 1 };

TransformationNode makeTransformationNode(
	const std::vector<float> &matrix)
{
	RepoBSONBuilder bsonBuilder;
	RepoBSONBuilder rows;
	for (uint32_t i = 0; i < 4; ++i)
	{
		RepoBSONBuilder columns;
		for (uint32_t j = 0; j < 4; ++j){
			columns << std::to_string(j) << matrix[i * 4 + j];
		}
		rows.appendArray(std::to_string(i), columns.obj());
	}
	bsonBuilder.appendArray(REPO_NODE_LABEL_MATRIX, rows.obj());

	return bsonBuilder.obj();
}

TEST(RepoTransformationNodeTest, Constructor)
{
	auto empty = TransformationNode();

	EXPECT_TRUE(empty.isEmpty());
	EXPECT_EQ(NodeType::TRANSFORMATION, empty.getTypeAsEnum());

	auto repoBson = RepoBSON(BSON("test" << "blah" << "test2" << 2));

	auto fromRepoBSON = TransformationNode(repoBson);
	EXPECT_EQ(NodeType::TRANSFORMATION, fromRepoBSON.getTypeAsEnum());
	EXPECT_EQ(fromRepoBSON.nFields(), repoBson.nFields());
	EXPECT_EQ(0, fromRepoBSON.getFileList().size());
}

TEST(RepoTransformationNodeTest, IdentityTest)
{
	auto empty = TransformationNode();
	EXPECT_TRUE(empty.isIdentity());

	EXPECT_TRUE(makeTransformationNode(identity).isIdentity());
	EXPECT_TRUE(makeTransformationNode(idInBoundary).isIdentity());
	EXPECT_FALSE(makeTransformationNode(notId).isIdentity());
	EXPECT_FALSE(makeTransformationNode(notIdInBoundary).isIdentity());
}

TEST(RepoTransformationNodeTest, IdentityTest2)
{
	auto identity = TransformationNode::identityMat();

	ASSERT_EQ(4, identity.size());
	for (int i = 0; i < 4; ++i)
	{
		ASSERT_EQ(4, identity[i].size());
		for (int j = 0; j < 4; ++j)
		{
			float expectedOutcome = i % 4 == j ? 1 : 0;
			EXPECT_EQ(expectedOutcome, identity[i][j]);
		}
	}
}

TEST(RepoTransformationNodeTest, TypeTest)
{
	TransformationNode node = TransformationNode();

	EXPECT_EQ(REPO_NODE_TYPE_TRANSFORMATION, node.getType());
	EXPECT_EQ(NodeType::TRANSFORMATION, node.getTypeAsEnum());
}

TEST(RepoTransformationNodeTest, PositionDependantTest)
{
	TransformationNode node = TransformationNode();
	//transformation node should always be position dependant
	EXPECT_TRUE(node.positionDependant());
}

TEST(RepoTransformationNodeTest, SEqualTest)
{
	auto empty1 = TransformationNode();
	auto empty2 = TransformationNode();

	auto notEmpty1 = makeTransformationNode(notId);
	auto notEmpty2 = makeTransformationNode(notId);
	auto notEmpty3 = makeTransformationNode(identity);

	EXPECT_TRUE(empty1.sEqual(empty2));
	EXPECT_TRUE(empty2.sEqual(empty1));
	EXPECT_TRUE(empty1.sEqual(empty1));
	EXPECT_TRUE(notEmpty1.sEqual(notEmpty2));
	EXPECT_TRUE(notEmpty2.sEqual(notEmpty1));
	EXPECT_FALSE(notEmpty1.sEqual(empty2));
	EXPECT_TRUE(notEmpty3.sEqual(notEmpty3));
	EXPECT_FALSE(notEmpty3.sEqual(notEmpty2));
	EXPECT_FALSE(empty1.sEqual(notEmpty2));
}

TEST(RepoTransformationNodeTest, CloneAndApplyTransformationTest)
{
	auto empty = TransformationNode();

	TransformationNode modifiedEmpty = empty.cloneAndApplyTransformation(notId);

	EXPECT_EQ(empty.getTransMatrix(false), identity);
	EXPECT_EQ(modifiedEmpty.getTransMatrix(false), notId);

	auto filled = makeTransformationNode(notId);
	TransformationNode modifiedFilled = filled.cloneAndApplyTransformation(std::vector<float>());

	EXPECT_EQ(modifiedFilled.getTransMatrix(false), notId);
}

TEST(RepoTransformationNodeTest, GetTransMatrixTest)
{
	TransformationNode empty = TransformationNode();
	EXPECT_EQ(identity, empty.getTransMatrix(false));

	TransformationNode notEmpty = makeTransformationNode(notId);
	EXPECT_EQ(notId, notEmpty.getTransMatrix(false));

	//check transpose is done correctly
	auto notIdTransposed = notEmpty.getTransMatrix(true);

	auto notIdTransData = notIdTransposed.getData();
	ASSERT_EQ(notId.size(), notIdTransData.size());
	for (int i = 0; i < 4; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			int index = i * 4 + j;
			int transIndex = j * 4 + i;
			EXPECT_EQ(notId[index], notIdTransData[transIndex]);
		}
	}
}<|endoftext|>


<commit_before>// $Id: Sentence.cpp 1465 2007-09-27 14:16:28Z hieuhoang1972 $
// vim:tabstop=2

/***********************************************************************
  Moses - factored phrase-based language decoder
  Copyright (C) 2006 University of Edinburgh

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 ***********************************************************************/

#include "XmlOption.h"
#include <vector>
#include <string>
#include <iostream>
#include "Util.h"
#include "StaticData.h"

namespace {

std::string ParseXmlTagAttribute(const std::string& tag,const std::string& attributeName){
	/*TODO deal with unescaping \"*/
	string tagOpen = attributeName + "=\"";
	size_t contentsStart = tag.find(tagOpen);
	if (contentsStart == std::string::npos) return "";
	contentsStart += tagOpen.size();
	size_t contentsEnd = tag.find_first_of('"',contentsStart+1);
	if (contentsEnd == std::string::npos) {
		TRACE_ERR("Malformed XML attribute: "<< tag);
		return "";
	}
	size_t possibleEnd;
	while (tag.at(contentsEnd-1) == '\\' && (possibleEnd = tag.find_first_of('"',contentsEnd+1)) != std::string::npos) {
		contentsEnd = possibleEnd;
	}
	return tag.substr(contentsStart,contentsEnd-contentsStart);
}

std::string TrimXml(const std::string& str) {
	if (str.size() < 2) return str;
	if (str[0] == '<' && str[str.size() - 1] == '>') {
		return str.substr(1, str.size() - 2);
	} else { return str; }
}

bool isXmlTag(const std::string& tag)
{
	return tag[0] == '<';
}

inline std::vector<std::string> TokenizeXml(const std::string& str)
{
	std::string lbrack = "<";
	std::string rbrack = ">";
	std::vector<std::string> tokens;
	// Find first "non-delimiter".
	std::string::size_type cpos = 0;
	std::string::size_type lpos = 0;
	std::string::size_type rpos = 0;

	while (cpos != str.size()) {
  	lpos = str.find_first_of(lbrack, cpos);
		if (lpos != std::string::npos) {
			rpos = str.find_first_of(rbrack, lpos);
			if (rpos == std::string::npos) {
				TRACE_ERR("ERROR: malformed XML: " << str << endl);
				return tokens;
			}
		} else {
			tokens.push_back(str.substr(cpos));
			break;
		}
		if (lpos - cpos > 0)
			tokens.push_back(str.substr(cpos, lpos - cpos));
		tokens.push_back(str.substr(lpos, rpos-lpos+1));
		cpos = rpos + 1;
	}
	return tokens;
}

}

std::vector<XmlOption> ProcessAndStripXMLTags(std::string& line) {
	//parse XML markup in translation line
	std::vector<XmlOption> res;
	std::string rstr;
	if (line.find_first_of('<') == std::string::npos) { return res; }
	std::vector<std::string> xmlTokens = TokenizeXml(line);
	std::string tagName = "";
	std::string tagContents = "";
	std::vector<std::string> altTexts;
	std::vector<std::string> altProbs;
	size_t tagStart=0;
	size_t tagEnd=0;
	size_t curWord=0;
	int numUnary = 0;
	bool doClose = false;
	for (size_t xmlTokenPos = 0 ; xmlTokenPos < xmlTokens.size() ; xmlTokenPos++)
	{
		if(!isXmlTag(xmlTokens[xmlTokenPos]))
		{
			//phrase, not tag
			rstr += xmlTokens[xmlTokenPos];
			curWord = Tokenize(rstr).size();
		}
		else
		{
			//tag data
			std::string tag =  Trim(TrimXml(xmlTokens[xmlTokenPos]));
			VERBOSE(3,"XML TAG IS: " << tag << std::endl);
			std::string::size_type endOfName = tag.find_first_of(' ');
			std::string nextTagName = tag;
			bool isUnary = tag[tag.size() - 1] == '/';
			bool isOpen = tag[1] != '/';
			if (endOfName != std::string::npos) {
				nextTagName = tag.substr(0,endOfName);
				tagContents = tag.substr(endOfName+1);
			}
			if (isOpen)
			{
				//this is an open tag
				tagName = nextTagName;
				altTexts = TokenizeMultiCharSeparator(ParseXmlTagAttribute(tagContents,"english"), "||");
				altProbs = TokenizeMultiCharSeparator(ParseXmlTagAttribute(tagContents,"prob"), "||");
				std::string span = ParseXmlTagAttribute(tagContents,"span");
				tagStart = curWord;
				if (isUnary) {
					numUnary++;
					if (span.empty()) {
						TRACE_ERR("ERROR: unary tags must have a span attribute: " << line << endl);
						return res;
					}
					std::vector<std::string> ij = Tokenize(span, ",");
					if (ij.size() != 2) {
						TRACE_ERR("ERROR: span tag must be of the form \"i,j\": " << line << endl);
						return res;
					}
					tagStart = atoi(ij[0].c_str());
					tagEnd = atoi(ij[1].c_str());
					if (tagEnd < tagStart) {
						TRACE_ERR("ERROR: span tag " << span << " invalid" << endl);
						return res;
					}
					doClose = true;
					VERBOSE(3,"XML TAG IS UNARY" << endl);
				}
				VERBOSE(3,"XML TAG NAME IS: '" << tagName << "'" << endl);
				VERBOSE(3,"XML TAG ENGLISH IS: '" << altTexts[0] << "'" << endl);
				VERBOSE(3,"XML TAG PROB IS: '" << altProbs[0] << "'" << endl);
				VERBOSE(3,"XML TAG STARTS AT WORD: " << tagStart << endl);
				if (altTexts.size() != altProbs.size()) {
					TRACE_ERR("ERROR: Unequal number of probabilities and translation alternatives: " << line << endl);
					return res;
				}
			}
			else if ((nextTagName.size() == 0) || (nextTagName.at(0) != '/') || (nextTagName.substr(1) != tagName))
			{
				//mismatched tag, abort!
				TRACE_ERR("ERROR: tried to parse malformed XML with xml-input enabled: " << line << endl);
				return res;
			}
			else {
				doClose = true;
				tagEnd = curWord-1; //size is inclusive
			}
			if (doClose) {
				VERBOSE(3,"XML END TAG IS: " << nextTagName.substr(1) << endl);
				VERBOSE(3,"XML TAG ENDS AT WORD: " << tagEnd << endl);
				//store translation options into members

				//TODO: deal with multiple XML options here

				if (StaticData::Instance().GetXmlInputType() != XmlIgnore) {
					for (size_t i=0; i<altTexts.size(); ++i) {
						//only store options if we aren't ignoring them
						//set default probability
						float probValue = 1;
						if (altProbs[i] != "") probValue = Scan<float>(altProbs[i]);
						//Convert from prob to log-prob
						float scoreValue = FloorScore(TransformScore(probValue));
						XmlOption option(tagStart,tagEnd,altTexts[i],scoreValue);
						res.push_back(option);
					}
				}
				tagName= "";
				tagContents = "";
				altTexts.clear();
				altProbs.clear();
				doClose = false;
			}
		}
	}
	line = rstr;
	return res;
}

<commit_msg>fixed bug with XML markup<commit_after>// $Id: Sentence.cpp 1465 2007-09-27 14:16:28Z hieuhoang1972 $
// vim:tabstop=2

/***********************************************************************
  Moses - factored phrase-based language decoder
  Copyright (C) 2006 University of Edinburgh

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 ***********************************************************************/

#include "XmlOption.h"
#include <vector>
#include <string>
#include <iostream>
#include "Util.h"
#include "StaticData.h"

namespace {

std::string ParseXmlTagAttribute(const std::string& tag,const std::string& attributeName){
	/*TODO deal with unescaping \"*/
	string tagOpen = attributeName + "=\"";
	size_t contentsStart = tag.find(tagOpen);
	if (contentsStart == std::string::npos) return "";
	contentsStart += tagOpen.size();
	size_t contentsEnd = tag.find_first_of('"',contentsStart+1);
	if (contentsEnd == std::string::npos) {
		TRACE_ERR("Malformed XML attribute: "<< tag);
		return "";
	}
	size_t possibleEnd;
	while (tag.at(contentsEnd-1) == '\\' && (possibleEnd = tag.find_first_of('"',contentsEnd+1)) != std::string::npos) {
		contentsEnd = possibleEnd;
	}
	return tag.substr(contentsStart,contentsEnd-contentsStart);
}

std::string TrimXml(const std::string& str) {
	if (str.size() < 2) return str;
	if (str[0] == '<' && str[str.size() - 1] == '>') {
		return str.substr(1, str.size() - 2);
	} else { return str; }
}

bool isXmlTag(const std::string& tag)
{
	return tag[0] == '<';
}

inline std::vector<std::string> TokenizeXml(const std::string& str)
{
	std::string lbrack = "<";
	std::string rbrack = ">";
	std::vector<std::string> tokens;
	// Find first "non-delimiter".
	std::string::size_type cpos = 0;
	std::string::size_type lpos = 0;
	std::string::size_type rpos = 0;

	while (cpos != str.size()) {
  	lpos = str.find_first_of(lbrack, cpos);
		if (lpos != std::string::npos) {
			rpos = str.find_first_of(rbrack, lpos);
			if (rpos == std::string::npos) {
				TRACE_ERR("ERROR: malformed XML: " << str << endl);
				return tokens;
			}
		} else {
			tokens.push_back(str.substr(cpos));
			break;
		}
		if (lpos - cpos > 0)
			tokens.push_back(str.substr(cpos, lpos - cpos));
		tokens.push_back(str.substr(lpos, rpos-lpos+1));
		cpos = rpos + 1;
	}
	return tokens;
}

}

std::vector<XmlOption> ProcessAndStripXMLTags(std::string& line) {
	//parse XML markup in translation line
	std::vector<XmlOption> res;
	std::string rstr;
	if (line.find_first_of('<') == std::string::npos) { return res; }
	std::vector<std::string> xmlTokens = TokenizeXml(line);
	std::string tagName = "";
	std::string tagContents = "";
	std::vector<std::string> altTexts;
	std::vector<std::string> altProbs;
	size_t tagStart=0;
	size_t tagEnd=0;
	size_t curWord=0;
	int numUnary = 0;
	bool doClose = false;
	for (size_t xmlTokenPos = 0 ; xmlTokenPos < xmlTokens.size() ; xmlTokenPos++)
	{
		if(!isXmlTag(xmlTokens[xmlTokenPos]))
		{
			//phrase, not tag
			rstr += xmlTokens[xmlTokenPos];
			curWord = Tokenize(rstr).size();
		}
		else
		{
			//tag data
			std::string tag =  Trim(TrimXml(xmlTokens[xmlTokenPos]));
			VERBOSE(3,"XML TAG IS: " << tag << std::endl);
			std::string::size_type endOfName = tag.find_first_of(' ');
			std::string nextTagName = tag;
			bool isUnary = tag[tag.size() - 1] == '/';
			bool isOpen = tag[0] != '/';
			if (endOfName != std::string::npos) {
				nextTagName = tag.substr(0,endOfName);
				tagContents = tag.substr(endOfName+1);
			}
			if (isOpen)
			{
				//this is an open tag
				tagName = nextTagName;
				altTexts = TokenizeMultiCharSeparator(ParseXmlTagAttribute(tagContents,"english"), "||");
				altProbs = TokenizeMultiCharSeparator(ParseXmlTagAttribute(tagContents,"prob"), "||");
				std::string span = ParseXmlTagAttribute(tagContents,"span");
				tagStart = curWord;
				if (isUnary) {
					numUnary++;
					if (span.empty()) {
						TRACE_ERR("ERROR: unary tags must have a span attribute: " << line << endl);
						return res;
					}
					std::vector<std::string> ij = Tokenize(span, ",");
					if (ij.size() != 2) {
						TRACE_ERR("ERROR: span tag must be of the form \"i,j\": " << line << endl);
						return res;
					}
					tagStart = atoi(ij[0].c_str());
					tagEnd = atoi(ij[1].c_str());
					if (tagEnd < tagStart) {
						TRACE_ERR("ERROR: span tag " << span << " invalid" << endl);
						return res;
					}
					doClose = true;
					VERBOSE(3,"XML TAG IS UNARY" << endl);
				}
				VERBOSE(3,"XML TAG NAME IS: '" << tagName << "'" << endl);
				VERBOSE(3,"XML TAG ENGLISH IS: '" << altTexts[0] << "'" << endl);
				VERBOSE(3,"XML TAG PROB IS: '" << altProbs[0] << "'" << endl);
				VERBOSE(3,"XML TAG STARTS AT WORD: " << tagStart << endl);
				if (altTexts.size() != altProbs.size()) {
					TRACE_ERR("ERROR: Unequal number of probabilities and translation alternatives: " << line << endl);
					return res;
				}
			}
			else if ((nextTagName.size() == 0) || (nextTagName.at(0) != '/') || (nextTagName.substr(1) != tagName))
			{
				//mismatched tag, abort!
				TRACE_ERR("ERROR: tried to parse malformed XML with xml-input enabled: " << line << endl);
				return res;
			}
			else {
				doClose = true;
				tagEnd = curWord-1; //size is inclusive
			}
			if (doClose) {
				VERBOSE(3,"XML END TAG IS: " << nextTagName.substr(1) << endl);
				VERBOSE(3,"XML TAG ENDS AT WORD: " << tagEnd << endl);
				//store translation options into members

				//TODO: deal with multiple XML options here

				if (StaticData::Instance().GetXmlInputType() != XmlIgnore) {
					for (size_t i=0; i<altTexts.size(); ++i) {
						//only store options if we aren't ignoring them
						//set default probability
						float probValue = 1;
						if (altProbs[i] != "") probValue = Scan<float>(altProbs[i]);
						//Convert from prob to log-prob
						float scoreValue = FloorScore(TransformScore(probValue));
						XmlOption option(tagStart,tagEnd,altTexts[i],scoreValue);
						res.push_back(option);
					}
				}
				tagName= "";
				tagContents = "";
				altTexts.clear();
				altProbs.clear();
				doClose = false;
			}
		}
	}
	line = rstr;
	return res;
}

<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/lib/stdio.C $                                             */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2011,2014              */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include <stdint.h>
#include <stdio.h>
#include <util/sprintf.H>

class SprintfBuffer : public Util::ConsoleBufferInterface
{
    public:
        int putc(int c)
        {
            if ('\b' == c)
            {
                iv_pos--;
            }
            else if (iv_pos < iv_size)
            {
                iv_buffer[iv_pos++] = c;
            }
            else
            {
                iv_pos++;
            }
            return c;
        }

        explicit SprintfBuffer(char* buf, size_t size = UINT64_MAX) :
            iv_pos(0), iv_size(size), iv_buffer(buf) {};

        size_t operator()(int c) { return putc(c); }

    private:
        size_t iv_pos;
        size_t iv_size;
        char * iv_buffer;
};

int sprintf(char *str, const char * format, ...)
{
    using Util::vasprintf;

    va_list args;
    va_start(args, format);

    SprintfBuffer console(str);
    size_t count = vasprintf(console, format, args);

    va_end(args);
    console.putc('\0');
    return count;
}

int snprintf(char *str, size_t size, const char * format, ...)
{
    using Util::vasprintf;

    va_list args;
    va_start(args, format);

    SprintfBuffer console(str, size);
    size_t count = vasprintf(console, format, args);

    va_end(args);
    console.putc('\0');
    return count;
}

int vsprintf(char *str, const char * format, va_list args)
{
    using Util::vasprintf;

    SprintfBuffer console(str);
    size_t count = vasprintf(console, format, args);

    console.putc('\0');
    return count;
}

int vsnprintf(char *str, size_t size, const char * format, va_list args)
{
    SprintfBuffer console(str, size);
    size_t count = vasprintf(console, format, args);

    console.putc('\0');
    return count;
}
<commit_msg>Make snprintf always terminate the output buffer<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/lib/stdio.C $                                             */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2011,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include <stdint.h>
#include <stdio.h>
#include <util/sprintf.H>

class SprintfBuffer : public Util::ConsoleBufferInterface
{
    public:
        int putc(int c)
        {
            if ('\b' == c)
            {
                if (iv_pos > 0)
                {
                    iv_pos--;
                }
            }
            else if (iv_pos < iv_size)
            {
                iv_buffer[iv_pos++] = c;
            }
            else
            {
                iv_pos++;
            }
            return c;
        }

        void nullTerminate()
        {
            if (iv_size > 0)
            {
                if (iv_pos >= iv_size)
                {
                    iv_pos = iv_size - 1;
                }

                putc('\0');
            }
        }

        explicit SprintfBuffer(char* buf, size_t size = UINT64_MAX) :
            iv_pos(0), iv_size(size), iv_buffer(buf) {};

        size_t operator()(int c) { return putc(c); }

    private:
        size_t iv_pos;
        size_t iv_size;
        char * iv_buffer;
};

int sprintf(char *str, const char * format, ...)
{
    using Util::vasprintf;

    va_list args;
    va_start(args, format);

    SprintfBuffer console(str);
    size_t count = vasprintf(console, format, args);

    va_end(args);
    console.nullTerminate();
    return count;
}

int snprintf(char *str, size_t size, const char * format, ...)
{
    using Util::vasprintf;

    va_list args;
    va_start(args, format);

    SprintfBuffer console(str, size);
    size_t count = vasprintf(console, format, args);

    va_end(args);
    console.nullTerminate();
    return count;
}

int vsprintf(char *str, const char * format, va_list args)
{
    using Util::vasprintf;

    SprintfBuffer console(str);
    size_t count = vasprintf(console, format, args);

    console.nullTerminate();
    return count;
}

int vsnprintf(char *str, size_t size, const char * format, va_list args)
{
    SprintfBuffer console(str, size);
    size_t count = vasprintf(console, format, args);

    console.nullTerminate();
    return count;
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"
#if defined(DART_HOST_OS_MACOS)

#include "bin/platform.h"
#include "bin/platform_macos.h"

#include <CoreFoundation/CoreFoundation.h>

#if !DART_HOST_OS_IOS
#include <crt_externs.h>  // NOLINT
#endif                    // !DART_HOST_OS_IOS
#include <errno.h>        // NOLINT
#include <mach-o/dyld.h>
#include <signal.h>        // NOLINT
#include <sys/resource.h>  // NOLINT
#include <sys/sysctl.h>    // NOLINT
#include <sys/types.h>     // NOLINT
#include <sys/utsname.h>   // NOLINT
#include <unistd.h>        // NOLINT

#include <string>

#include "bin/console.h"
#include "bin/file.h"
#include "bin/platform_macos_cocoa.h"

namespace dart {
namespace bin {

const char* Platform::executable_name_ = NULL;
int Platform::script_index_ = 1;
char** Platform::argv_ = NULL;

static void segv_handler(int signal, siginfo_t* siginfo, void* context) {
  Syslog::PrintErr(
      "\n===== CRASH =====\n"
      "si_signo=%s(%d), si_code=%d, si_addr=%p\n",
      strsignal(siginfo->si_signo), siginfo->si_signo, siginfo->si_code,
      siginfo->si_addr);
  Dart_DumpNativeStackTrace(context);
  Dart_PrepareToAbort();
  abort();
}

bool Platform::Initialize() {
  // Turn off the signal handler for SIGPIPE as it causes the process
  // to terminate on writing to a closed pipe. Without the signal
  // handler error EPIPE is set instead.
  struct sigaction act = {};
  act.sa_handler = SIG_IGN;
  if (sigaction(SIGPIPE, &act, 0) != 0) {
    perror("Setting signal handler failed");
    return false;
  }

  // tcsetattr raises SIGTTOU if we try to set console attributes when
  // backgrounded, which suspends the process. Ignoring the signal prevents
  // us from being suspended and lets us fail gracefully instead.
  sigset_t signal_mask;
  sigemptyset(&signal_mask);
  sigaddset(&signal_mask, SIGTTOU);
  if (sigprocmask(SIG_BLOCK, &signal_mask, NULL) < 0) {
    perror("Setting signal handler failed");
    return false;
  }

  act.sa_flags = SA_SIGINFO;
  act.sa_sigaction = &segv_handler;
  if (sigemptyset(&act.sa_mask) != 0) {
    perror("sigemptyset() failed.");
    return false;
  }
  if (sigaddset(&act.sa_mask, SIGPROF) != 0) {
    perror("sigaddset() failed");
    return false;
  }
  if (sigaction(SIGSEGV, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGBUS, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGTRAP, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGILL, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  return true;
}

int Platform::NumberOfProcessors() {
  int32_t cpus = -1;
  size_t cpus_length = sizeof(cpus);
  if (sysctlbyname("hw.logicalcpu", &cpus, &cpus_length, NULL, 0) == 0) {
    return cpus;
  } else {
    // Failed, fallback to using sysconf.
    return sysconf(_SC_NPROCESSORS_ONLN);
  }
}

const char* Platform::OperatingSystem() {
#if DART_HOST_OS_IOS
  return "ios";
#else
  return "macos";
#endif
}

const char* Platform::OperatingSystemVersion() {
  std::string version(NSProcessInfoOperatingSystemVersionString());
  return DartUtils::ScopedCopyCString(version.c_str());
}

const char* Platform::LibraryPrefix() {
  return "lib";
}

const char* Platform::LibraryExtension() {
  return "dylib";
}

static const char* GetLocaleName() {
  CFLocaleRef locale = CFLocaleCopyCurrent();
  CFStringRef locale_string = CFLocaleGetIdentifier(locale);
  CFIndex len = CFStringGetLength(locale_string);
  CFIndex max_len =
      CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
  char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
  ASSERT(result != NULL);
  bool success =
      CFStringGetCString(locale_string, result, max_len, kCFStringEncodingUTF8);
  CFRelease(locale);
  if (!success) {
    return NULL;
  }
  return result;
}

static const char* GetPreferredLanguageName() {
  CFArrayRef languages = CFLocaleCopyPreferredLanguages();
  CFIndex languages_length = CFArrayGetCount(languages);
  if (languages_length < 1) {
    CFRelease(languages);
    return NULL;
  }
  CFTypeRef item =
      reinterpret_cast<CFTypeRef>(CFArrayGetValueAtIndex(languages, 0));
  CFTypeID item_type = CFGetTypeID(item);
  ASSERT(item_type == CFStringGetTypeID());
  CFStringRef language = reinterpret_cast<CFStringRef>(item);
  CFIndex len = CFStringGetLength(language);
  CFIndex max_len =
      CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
  char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
  ASSERT(result != NULL);
  bool success =
      CFStringGetCString(language, result, max_len, kCFStringEncodingUTF8);
  CFRelease(languages);
  if (!success) {
    return NULL;
  }
  return result;
}

const char* Platform::LocaleName() {
  // First see if there is a preferred language. If not, return the
  // current locale name.
  const char* preferred_language = GetPreferredLanguageName();
  return (preferred_language != NULL) ? preferred_language : GetLocaleName();
}

bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) {
  return gethostname(buffer, buffer_length) == 0;
}

char** Platform::Environment(intptr_t* count) {
#if DART_HOST_OS_IOS
  // TODO(zra,chinmaygarde): On iOS, environment variables are seldom used. Wire
  // this up if someone needs it. In the meantime, we return an empty array.
  char** result;
  result = reinterpret_cast<char**>(Dart_ScopeAllocate(1 * sizeof(*result)));
  if (result == NULL) {
    return NULL;
  }
  result[0] = NULL;
  *count = 0;
  return result;
#else
  // Using environ directly is only safe as long as we do not
  // provide access to modifying environment variables.
  // On MacOS you have to do a bit of magic to get to the
  // environment strings.
  char** environ = *(_NSGetEnviron());
  intptr_t i = 0;
  char** tmp = environ;
  while (*(tmp++) != NULL) {
    i++;
  }
  *count = i;
  char** result;
  result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result)));
  for (intptr_t current = 0; current < i; current++) {
    result[current] = environ[current];
  }
  return result;
#endif
}

const char* Platform::GetExecutableName() {
  return executable_name_;
}

const char* Platform::ResolveExecutablePath() {
  // Get the required length of the buffer.
  uint32_t path_size = 0;
  if (_NSGetExecutablePath(NULL, &path_size) == 0) {
    return NULL;
  }
  // Allocate buffer and get executable path.
  char* path = DartUtils::ScopedCString(path_size);
  if (_NSGetExecutablePath(path, &path_size) != 0) {
    return NULL;
  }
  // Return the canonical path as the returned path might contain symlinks.
  const char* canon_path = File::GetCanonicalPath(NULL, path);
  return canon_path;
}

intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
  // Get the required length of the buffer.
  uint32_t path_size = 0;
  if (_NSGetExecutablePath(nullptr, &path_size) == 0) {
    return -1;
  }
  if (path_size > result_size) {
    return -1;
  }
  if (_NSGetExecutablePath(result, &path_size) != 0) {
    return -1;
  }
  return path_size;
}

void Platform::SetProcessName(const char* name) {}

void Platform::Exit(int exit_code) {
  Console::RestoreConfig();
  Dart_PrepareToAbort();
  exit(exit_code);
}

void Platform::SetCoreDumpResourceLimit(int value) {
  rlimit limit = {static_cast<rlim_t>(value), static_cast<rlim_t>(value)};
  setrlimit(RLIMIT_CORE, &limit);
}

}  // namespace bin
}  // namespace dart

#endif  // defined(DART_HOST_OS_MACOS)
<commit_msg>[standalone, mac] Set the process title based on the main script.<commit_after>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"
#if defined(DART_HOST_OS_MACOS)

#include "bin/platform.h"
#include "bin/platform_macos.h"

#include <CoreFoundation/CoreFoundation.h>

#if !DART_HOST_OS_IOS
#include <crt_externs.h>  // NOLINT
#endif                    // !DART_HOST_OS_IOS
#include <dlfcn.h>        // NOLINT
#include <errno.h>        // NOLINT
#include <mach-o/dyld.h>
#include <signal.h>        // NOLINT
#include <sys/resource.h>  // NOLINT
#include <sys/sysctl.h>    // NOLINT
#include <sys/types.h>     // NOLINT
#include <sys/utsname.h>   // NOLINT
#include <unistd.h>        // NOLINT

#include <string>

#include "bin/console.h"
#include "bin/file.h"
#include "bin/platform_macos_cocoa.h"

namespace dart {
namespace bin {

const char* Platform::executable_name_ = NULL;
int Platform::script_index_ = 1;
char** Platform::argv_ = NULL;

static void segv_handler(int signal, siginfo_t* siginfo, void* context) {
  Syslog::PrintErr(
      "\n===== CRASH =====\n"
      "si_signo=%s(%d), si_code=%d, si_addr=%p\n",
      strsignal(siginfo->si_signo), siginfo->si_signo, siginfo->si_code,
      siginfo->si_addr);
  Dart_DumpNativeStackTrace(context);
  Dart_PrepareToAbort();
  abort();
}

bool Platform::Initialize() {
  // Turn off the signal handler for SIGPIPE as it causes the process
  // to terminate on writing to a closed pipe. Without the signal
  // handler error EPIPE is set instead.
  struct sigaction act = {};
  act.sa_handler = SIG_IGN;
  if (sigaction(SIGPIPE, &act, 0) != 0) {
    perror("Setting signal handler failed");
    return false;
  }

  // tcsetattr raises SIGTTOU if we try to set console attributes when
  // backgrounded, which suspends the process. Ignoring the signal prevents
  // us from being suspended and lets us fail gracefully instead.
  sigset_t signal_mask;
  sigemptyset(&signal_mask);
  sigaddset(&signal_mask, SIGTTOU);
  if (sigprocmask(SIG_BLOCK, &signal_mask, NULL) < 0) {
    perror("Setting signal handler failed");
    return false;
  }

  act.sa_flags = SA_SIGINFO;
  act.sa_sigaction = &segv_handler;
  if (sigemptyset(&act.sa_mask) != 0) {
    perror("sigemptyset() failed.");
    return false;
  }
  if (sigaddset(&act.sa_mask, SIGPROF) != 0) {
    perror("sigaddset() failed");
    return false;
  }
  if (sigaction(SIGSEGV, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGBUS, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGTRAP, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  if (sigaction(SIGILL, &act, NULL) != 0) {
    perror("sigaction() failed.");
    return false;
  }
  return true;
}

int Platform::NumberOfProcessors() {
  int32_t cpus = -1;
  size_t cpus_length = sizeof(cpus);
  if (sysctlbyname("hw.logicalcpu", &cpus, &cpus_length, NULL, 0) == 0) {
    return cpus;
  } else {
    // Failed, fallback to using sysconf.
    return sysconf(_SC_NPROCESSORS_ONLN);
  }
}

const char* Platform::OperatingSystem() {
#if DART_HOST_OS_IOS
  return "ios";
#else
  return "macos";
#endif
}

const char* Platform::OperatingSystemVersion() {
  std::string version(NSProcessInfoOperatingSystemVersionString());
  return DartUtils::ScopedCopyCString(version.c_str());
}

const char* Platform::LibraryPrefix() {
  return "lib";
}

const char* Platform::LibraryExtension() {
  return "dylib";
}

static const char* GetLocaleName() {
  CFLocaleRef locale = CFLocaleCopyCurrent();
  CFStringRef locale_string = CFLocaleGetIdentifier(locale);
  CFIndex len = CFStringGetLength(locale_string);
  CFIndex max_len =
      CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
  char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
  ASSERT(result != NULL);
  bool success =
      CFStringGetCString(locale_string, result, max_len, kCFStringEncodingUTF8);
  CFRelease(locale);
  if (!success) {
    return NULL;
  }
  return result;
}

static const char* GetPreferredLanguageName() {
  CFArrayRef languages = CFLocaleCopyPreferredLanguages();
  CFIndex languages_length = CFArrayGetCount(languages);
  if (languages_length < 1) {
    CFRelease(languages);
    return NULL;
  }
  CFTypeRef item =
      reinterpret_cast<CFTypeRef>(CFArrayGetValueAtIndex(languages, 0));
  CFTypeID item_type = CFGetTypeID(item);
  ASSERT(item_type == CFStringGetTypeID());
  CFStringRef language = reinterpret_cast<CFStringRef>(item);
  CFIndex len = CFStringGetLength(language);
  CFIndex max_len =
      CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
  char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
  ASSERT(result != NULL);
  bool success =
      CFStringGetCString(language, result, max_len, kCFStringEncodingUTF8);
  CFRelease(languages);
  if (!success) {
    return NULL;
  }
  return result;
}

const char* Platform::LocaleName() {
  // First see if there is a preferred language. If not, return the
  // current locale name.
  const char* preferred_language = GetPreferredLanguageName();
  return (preferred_language != NULL) ? preferred_language : GetLocaleName();
}

bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) {
  return gethostname(buffer, buffer_length) == 0;
}

char** Platform::Environment(intptr_t* count) {
#if DART_HOST_OS_IOS
  // TODO(zra,chinmaygarde): On iOS, environment variables are seldom used. Wire
  // this up if someone needs it. In the meantime, we return an empty array.
  char** result;
  result = reinterpret_cast<char**>(Dart_ScopeAllocate(1 * sizeof(*result)));
  if (result == NULL) {
    return NULL;
  }
  result[0] = NULL;
  *count = 0;
  return result;
#else
  // Using environ directly is only safe as long as we do not
  // provide access to modifying environment variables.
  // On MacOS you have to do a bit of magic to get to the
  // environment strings.
  char** environ = *(_NSGetEnviron());
  intptr_t i = 0;
  char** tmp = environ;
  while (*(tmp++) != NULL) {
    i++;
  }
  *count = i;
  char** result;
  result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result)));
  for (intptr_t current = 0; current < i; current++) {
    result[current] = environ[current];
  }
  return result;
#endif
}

const char* Platform::GetExecutableName() {
  return executable_name_;
}

const char* Platform::ResolveExecutablePath() {
  // Get the required length of the buffer.
  uint32_t path_size = 0;
  if (_NSGetExecutablePath(NULL, &path_size) == 0) {
    return NULL;
  }
  // Allocate buffer and get executable path.
  char* path = DartUtils::ScopedCString(path_size);
  if (_NSGetExecutablePath(path, &path_size) != 0) {
    return NULL;
  }
  // Return the canonical path as the returned path might contain symlinks.
  const char* canon_path = File::GetCanonicalPath(NULL, path);
  return canon_path;
}

intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
  // Get the required length of the buffer.
  uint32_t path_size = 0;
  if (_NSGetExecutablePath(nullptr, &path_size) == 0) {
    return -1;
  }
  if (path_size > result_size) {
    return -1;
  }
  if (_NSGetExecutablePath(result, &path_size) != 0) {
    return -1;
  }
  return path_size;
}

void Platform::SetProcessName(const char* name) {
  pthread_setname_np(name);

#if !defined(DART_HOST_OS_IOS) && !defined(DART_PRECOMPILED_RUNTIME)
  // Attempt to set the name displayed in ActivityMonitor.
  // https://codereview.chromium.org/659007/

  class ScopedDLHandle : public ValueObject {
   public:
    explicit ScopedDLHandle(void* handle) : handle_(handle) {}
    ~ScopedDLHandle() {
      if (handle_ != NULL) dlclose(handle_);
    }
    void* get() const { return handle_; }

   private:
    void* handle_;
    DISALLOW_COPY_AND_ASSIGN(ScopedDLHandle);
  };

  class ScopedCFStringRef : public ValueObject {
   public:
    explicit ScopedCFStringRef(const char* s)
        : ref_(CFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)) {}
    ~ScopedCFStringRef() {
      if (ref_ != NULL) CFRelease(ref_);
    }
    CFStringRef get() const { return ref_; }

   private:
    CFStringRef ref_;
    DISALLOW_COPY_AND_ASSIGN(ScopedCFStringRef);
  };

  ScopedDLHandle application_services_handle(
      dlopen("/System/Library/Frameworks/ApplicationServices.framework/"
             "Versions/A/ApplicationServices",
             RTLD_LAZY | RTLD_LOCAL));
  if (application_services_handle.get() == NULL) return;

  ScopedCFStringRef launch_services_bundle_name("com.apple.LaunchServices");
  CFBundleRef launch_services_bundle =
      CFBundleGetBundleWithIdentifier(launch_services_bundle_name.get());
  if (launch_services_bundle == NULL) return;

#define GET_FUNC(name, cstr)                                                   \
  ScopedCFStringRef name##_id(cstr);                                           \
  *reinterpret_cast<void**>(&name) = CFBundleGetFunctionPointerForName(        \
      launch_services_bundle, name##_id.get());                                \
  if (name == NULL) return;

#define GET_DATA(name, cstr)                                                   \
  ScopedCFStringRef name##_id(cstr);                                           \
  *reinterpret_cast<void**>(&name) =                                           \
      CFBundleGetDataPointerForName(launch_services_bundle, name##_id.get());  \
  if (name == NULL) return;

  CFTypeRef (*_LSGetCurrentApplicationASN)(void);
  GET_FUNC(_LSGetCurrentApplicationASN, "_LSGetCurrentApplicationASN");

  OSStatus (*_LSSetApplicationInformationItem)(int, CFTypeRef, CFStringRef,
                                               CFStringRef, CFDictionaryRef*);
  GET_FUNC(_LSSetApplicationInformationItem,
           "_LSSetApplicationInformationItem");

  CFDictionaryRef (*_LSApplicationCheckIn)(int, CFDictionaryRef);
  GET_FUNC(_LSApplicationCheckIn, "_LSApplicationCheckIn");

  void (*_LSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,
                                                                void*);
  GET_FUNC(_LSSetApplicationLaunchServicesServerConnectionStatus,
           "_LSSetApplicationLaunchServicesServerConnectionStatus");

  CFStringRef* _kLSDisplayNameKey;
  GET_DATA(_kLSDisplayNameKey, "_kLSDisplayNameKey");
  if (*_kLSDisplayNameKey == NULL) return;

  _LSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);

  _LSApplicationCheckIn(-2, CFBundleGetInfoDictionary(CFBundleGetMainBundle()));

  CFTypeRef asn;
  asn = _LSGetCurrentApplicationASN();
  if (asn == NULL) return;

  ScopedCFStringRef cf_name(name);
  _LSSetApplicationInformationItem(-2, asn, *_kLSDisplayNameKey, cf_name.get(),
                                   NULL);
#undef GET_DATA
#undef GET_FUNC
#endif  // !defined(DART_HOST_OS_IOS)
}

void Platform::Exit(int exit_code) {
  Console::RestoreConfig();
  Dart_PrepareToAbort();
  exit(exit_code);
}

void Platform::SetCoreDumpResourceLimit(int value) {
  rlimit limit = {static_cast<rlim_t>(value), static_cast<rlim_t>(value)};
  setrlimit(RLIMIT_CORE, &limit);
}

}  // namespace bin
}  // namespace dart

#endif  // defined(DART_HOST_OS_MACOS)
<|endoftext|>