<commit_before>// Copyright (C) 2010-2013 Joshua Boyce.
// See the file COPYING for copying permission.

#pragma once

#include <array>
#include <cstddef>
#include <exception>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include <windows.h>

#include <hadesmem/detail/assert.hpp>
#include <hadesmem/detail/protect_guard.hpp>
#include <hadesmem/detail/query_region.hpp>
#include <hadesmem/detail/read_impl.hpp>
#include <hadesmem/detail/static_assert.hpp>
#include <hadesmem/detail/type_traits.hpp>
#include <hadesmem/error.hpp>
#include <hadesmem/protect.hpp>

// TODO: Support custom string, vector, etc types. Also support custom
// allocators, traits, etc.

namespace hadesmem
{

template <typename T>
inline T Read(Process const& process, PVOID address)
{
  HADESMEM_DETAIL_ASSERT(address != nullptr);

  return detail::ReadImpl<T>(process, address);
}

template <typename T, std::size_t N>
inline std::array<T, N> Read(Process const& process, PVOID address)
{
  HADESMEM_DETAIL_STATIC_ASSERT(N != 0);

  HADESMEM_DETAIL_ASSERT(address != nullptr);

  return detail::ReadImpl<std::array<T, N>>(process, address);
}

template <typename T, std::size_t N, typename OutputIterator>
inline void Read(Process const& process, PVOID address, OutputIterator out)
{
  // TODO: Iterator checks for type and category.

  HADESMEM_DETAIL_STATIC_ASSERT(N != 0);

  HADESMEM_DETAIL_ASSERT(address != nullptr);

  auto const data = detail::ReadImpl<std::array<T, N>>(process, address);
  std::copy(&data[0], &data[0] + N, out);
}

template <typename T>
inline std::vector<T> ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count);

template <typename T, typename OutputIterator>
inline void Read(
  Process const& process,
  PVOID address,
  std::size_t n,
  OutputIterator out)
{
  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(n != 0);

  auto const data = ReadVector<T>(process, address, n);
  std::copy(std::begin(data), std::end(data), out);
}

// TODO: Clean up this function.
template <typename T, typename OutputIterator>
void ReadStringEx(
  Process const& process,
  PVOID address,
  OutputIterator data,
  std::size_t chunk_len)
{
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsCharType<T>::value);

  HADESMEM_DETAIL_ASSERT(chunk_len != 0);

  // TODO: Iterator checks for type and category.

  for (;;)
  {
    detail::ProtectGuard protect_guard(process, address,
      detail::ProtectGuardType::kRead);

    MEMORY_BASIC_INFORMATION const mbi = detail::Query(process, address);
    PVOID const region_next = static_cast<PBYTE>(mbi.BaseAddress) +
      mbi.RegionSize;

    T* cur = static_cast<T*>(address);
    while (cur + 1 <= region_next)
    {
      std::size_t const len_to_end =
        reinterpret_cast<DWORD_PTR>(region_next) -
        reinterpret_cast<DWORD_PTR>(cur);
      std::size_t const buf_len_bytes = (std::min)(chunk_len * sizeof(T),
        len_to_end);
      std::size_t const buf_len = buf_len_bytes / sizeof(T);

      std::vector<T> buf(buf_len);
      detail::ReadUnchecked(process, cur, buf.data(), buf.size() * sizeof(T));

      auto const iter = std::find(std::begin(buf), std::end(buf), T());
      std::copy(std::begin(buf), iter, data);

      if (iter != std::end(buf))
      {
        protect_guard.Restore();
        return;
      }

      cur += buf_len;
    }

    address = region_next;

    protect_guard.Restore();
  }
}

template <typename T, typename OutputIterator>
void ReadString(
  Process const& process,
  PVOID address,
  OutputIterator data)
{
  std::size_t const chunk_len = 128;
  return ReadStringEx<T>(process, address, data, chunk_len);
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
std::basic_string<T> ReadStringEx(
  Process const& process,
  PVOID address,
  std::size_t chunk_len)
{
  std::basic_string<T> data;
  ReadStringEx<T>(process, address, std::back_inserter(data), chunk_len);
  return data;
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
std::basic_string<T> ReadString(Process const& process, PVOID address)
{
  std::size_t const chunk_len = 128;
  return ReadStringEx<T>(process, address, chunk_len);
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
inline std::vector<T> ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count)
{
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsTriviallyCopyable<T>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsDefaultConstructible<T>::value);

  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(count != 0);

  std::vector<T> data(count);
  detail::ReadImpl(process, address, data.data(), sizeof(T) * count);
  return data;
}

template <typename T, typename OutputIterator>
inline void ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count,
  OutputIterator out)
{
  // TODO: Iterator checks for type and category.

  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsTriviallyCopyable<T>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsDefaultConstructible<T>::value);

  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(count != 0);

  std::vector<T> data = ReadVector<T>(process, address, count);
  std::copy(std::begin(data), std::end(data), out);
}

}
<commit_msg>* Add iterator category checks.<commit_after>// Copyright (C) 2010-2013 Joshua Boyce.
// See the file COPYING for copying permission.

#pragma once

#include <array>
#include <cstddef>
#include <exception>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include <windows.h>

#include <hadesmem/detail/assert.hpp>
#include <hadesmem/detail/protect_guard.hpp>
#include <hadesmem/detail/query_region.hpp>
#include <hadesmem/detail/read_impl.hpp>
#include <hadesmem/detail/static_assert.hpp>
#include <hadesmem/detail/type_traits.hpp>
#include <hadesmem/error.hpp>
#include <hadesmem/protect.hpp>

// TODO: Support custom string, vector, etc types. Also support custom
// allocators, traits, etc.

namespace hadesmem
{

template <typename T>
inline T Read(Process const& process, PVOID address)
{
  HADESMEM_DETAIL_ASSERT(address != nullptr);

  return detail::ReadImpl<T>(process, address);
}

template <typename T, std::size_t N>
inline std::array<T, N> Read(Process const& process, PVOID address)
{
  HADESMEM_DETAIL_STATIC_ASSERT(N != 0);

  HADESMEM_DETAIL_ASSERT(address != nullptr);

  return detail::ReadImpl<std::array<T, N>>(process, address);
}

template <typename T, std::size_t N, typename OutputIterator>
inline void Read(Process const& process, PVOID address, OutputIterator out)
{
  HADESMEM_DETAIL_STATIC_ASSERT(std::is_same<std::output_iterator_tag,
    typename std::iterator_traits<OutputIterator>::iterator_category>::value);

  HADESMEM_DETAIL_STATIC_ASSERT(N != 0);

  HADESMEM_DETAIL_ASSERT(address != nullptr);

  auto const data = detail::ReadImpl<std::array<T, N>>(process, address);
  std::copy(&data[0], &data[0] + N, out);
}

template <typename T>
inline std::vector<T> ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count);

template <typename T, typename OutputIterator>
inline void Read(
  Process const& process,
  PVOID address,
  std::size_t n,
  OutputIterator out)
{
  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(n != 0);

  auto const data = ReadVector<T>(process, address, n);
  std::copy(std::begin(data), std::end(data), out);
}

template <typename T, typename OutputIterator>
void ReadStringEx(
  Process const& process,
  PVOID address,
  OutputIterator data,
  std::size_t chunk_len)
{
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsCharType<T>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(std::is_same<std::output_iterator_tag,
    typename std::iterator_traits<OutputIterator>::iterator_category>::value);

  HADESMEM_DETAIL_ASSERT(chunk_len != 0);

  for (;;)
  {
    detail::ProtectGuard protect_guard(process, address,
      detail::ProtectGuardType::kRead);

    MEMORY_BASIC_INFORMATION const mbi = detail::Query(process, address);
    PVOID const region_next = static_cast<PBYTE>(mbi.BaseAddress) +
      mbi.RegionSize;

    T* cur = static_cast<T*>(address);
    while (cur + 1 <= region_next)
    {
      std::size_t const len_to_end =
        reinterpret_cast<DWORD_PTR>(region_next) -
        reinterpret_cast<DWORD_PTR>(cur);
      std::size_t const buf_len_bytes = (std::min)(chunk_len * sizeof(T),
        len_to_end);
      std::size_t const buf_len = buf_len_bytes / sizeof(T);

      std::vector<T> buf(buf_len);
      detail::ReadUnchecked(process, cur, buf.data(), buf.size() * sizeof(T));

      auto const iter = std::find(std::begin(buf), std::end(buf), T());
      std::copy(std::begin(buf), iter, data);

      if (iter != std::end(buf))
      {
        protect_guard.Restore();
        return;
      }

      cur += buf_len;
    }

    address = region_next;

    protect_guard.Restore();
  }
}

template <typename T, typename OutputIterator>
void ReadString(
  Process const& process,
  PVOID address,
  OutputIterator data)
{
  std::size_t const chunk_len = 128;
  return ReadStringEx<T>(process, address, data, chunk_len);
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
std::basic_string<T> ReadStringEx(
  Process const& process,
  PVOID address,
  std::size_t chunk_len)
{
  std::basic_string<T> data;
  ReadStringEx<T>(process, address, std::back_inserter(data), chunk_len);
  return data;
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
std::basic_string<T> ReadString(Process const& process, PVOID address)
{
  std::size_t const chunk_len = 128;
  return ReadStringEx<T>(process, address, chunk_len);
}

// TODO: Support containers with custom traits, allocators, etc.
template <typename T>
inline std::vector<T> ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count)
{
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsTriviallyCopyable<T>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsDefaultConstructible<T>::value);

  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(count != 0);

  std::vector<T> data(count);
  detail::ReadImpl(process, address, data.data(), sizeof(T) * count);
  return data;
}

template <typename T, typename OutputIterator>
inline void ReadVector(
  Process const& process,
  PVOID address,
  std::size_t count,
  OutputIterator out)
{
  HADESMEM_DETAIL_STATIC_ASSERT(std::is_same<std::output_iterator_tag,
    typename std::iterator_traits<OutputIterator>::iterator_category>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsTriviallyCopyable<T>::value);
  HADESMEM_DETAIL_STATIC_ASSERT(detail::IsDefaultConstructible<T>::value);

  HADESMEM_DETAIL_ASSERT(address != nullptr);
  HADESMEM_DETAIL_ASSERT(count != 0);

  std::vector<T> data = ReadVector<T>(process, address, count);
  std::copy(std::begin(data), std::end(data), out);
}

}
<|endoftext|>


<commit_before>#include <vector>

namespace data{
extern std::vector< std::vector<size_t> > docs;
}<commit_msg>Use namespace std to see if size_t will work<commit_after>#include <vector>

using namespace std;

namespace data{
extern std::vector< std::vector<size_t> > docs;
}<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017  Hidekazu Ikeno
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is

 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

///
/// \file aak_reduction.hpp
///
/// Sparse approximation of exponential sum using AAK theory.
///

#ifndef MXPFIT_AAK_REDUCTION_HPP
#define MXPFIT_AAK_REDUCTION_HPP

#include <algorithm>

#include <mxpfit/exponential_sum.hpp>
#include <mxpfit/quasi_cauchy_rrd.hpp>
#include <mxpfit/self_adjoint_coneigensolver.hpp>

namespace mxpfit
{

namespace detail
{
} // namespace: detail

///
/// ### AAKReduction
///
/// \brief Find a truncated exponential sum function with smaller number of
///        terms using AAK theory.
///
/// \tparam T  Scalar type of exponential sum function.
///
/// For a given exponential sum function
/// \f[
///   f(t)=\sum_{j=1}^{N} c_{j}^{} e^{-p_{j}^{} t}, \quad
///   (\mathrm{Re}(a_{j}) > 0).
/// \f]
///
/// and prescribed accuracy \f$\epsilon > 0,\f$ this class calculates truncated
/// exponential \f$\tilde{f}(t)\f$ sum such that
///
/// \f[
///   \tilde{f}(t)=\sum_{j=1}^{M} \tilde{c}_{j}^{}e^{-\tilde{p}_{j}^{} t}, \quad
///   \left| f(t)-\tilde{f}(t) \right| < \epsilon,
/// \f]
///
///
/// The signal \f$\boldsymbol{f}=(f_{k})_{k=0}^{\infty}\f$ sampled on
/// \f$k=0,1,\dots\f$ are expressed as
///
/// \f[
///   f_{k}:= f(k) = \sum_{j=1}^{N} c_{j} z_{j}^{k}
/// \f]
///
/// where \f$z_{j}=e^{-p_{j}}\in \mathbb{D}:=\{z \in \mathbb{C}:0<|z|<1\}.\f$
/// The problem to find truncated exponential sum can be recast to find a new
/// signal \f$\tilde{\boldsymbol{f}},\f$ a sparse approximation of signal
/// \f$\boldsymbol{f},\f$ such that
///
/// \f[
///   \tilde{f}_{k}=\sum_{j=1}^{M}\tilde{c}_{j}^{} \tilde{z}_{j}^{k}, \quad
///   \|\boldsymbol{f}-\tilde{\boldsymbol{f}}\|<\epsilon. \f$
/// \f]
///
/// Then, the exponents of reduced exponential sum
/// \f$\tilde{f}(t)\f$ are obtained as \f$\tilde{p}_{j}=\tilde{z}_{j}.\f$
///
///
/// #### References
///
/// 1. Gerlind Plonka and Vlada Pototskaia, "Application of the AAK theory for
///    sparse approximation of exponential sums", arXiv 1609.09603v1 (2016).
///    [URL: https://arxiv.org/abs/1609.09603v1]
/// 2. Gregory Beylkin and Lucas Monzón, "On approximation of functions by
///    exponential sums", Appl. Comput. Harmon. Anal. **19** (2005) 17-48.
///    [DOI: https://doi.org/10.1016/j.acha.2005.01.003]
/// 3. T. S. Haut and G. Beylkin, "FAST AND ACCURATE CON-EIGENVALUE ALGORITHM
///    FOR OPTIMAL RATIONAL APPROXIMATIONS", SIAM J. Matrix Anal. Appl. **33**
///    (2012) 1101-1125.
///    [DOI: https://doi.org/10.1137/110821901]
/// 4. Terry Haut, Gregory Beylkin, and Lucas Monzón, "Solving Burgers’
///    equation using optimal rational approximations", Appl. Comput. Harmon.
///    Anal. **34** (2013) 83-95.
///    [DOI: https://doi.org/10.1016/j.acha.2012.03.004]
///
template <typename T>
class AAKReduction
{
public:
    using Scalar        = T;
    using RealScalar    = typename Eigen::NumTraits<Scalar>::Real;
    using ComplexScalar = std::complex<RealScalar>;
    using Index         = Eigen::Index;

    using VectorType = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
    using MatrixType = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
    using ResultType = ExponentialSum<Scalar>;

    ///
    /// Compute truncated exponential sum \f$ \hat{f}(t) \f$
    ///
    /// \tparam DerivedF type of exponential sum inheriting ExponentialSumBase
    ///
    /// \param[in] orig original exponential sum function, \f$ f(t) \f$
    /// \param[in] threshold  prescribed accuracy \f$0 < \epsilon \ll 1\f$
    ///
    /// \return An instance of ExponentialSum represents \f$\hat{f}(t)\f$
    ///
    template <typename DerivedF>
    ResultType compute(const ExponentialSumBase<DerivedF>& orig,
                       RealScalar threshold);

private:
    enum
    {
        IsComplex = Eigen::NumTraits<Scalar>::IsComplex,
    };

    using ConeigenSolverType = SelfAdjointConeigenSolver<Scalar>;
    using RRDType =
        QuasiCauchyRRD<Scalar, QuasiCauchyRRDFunctorLogPole<Scalar>>;
};

template <typename T>
template <typename DerivedF>
typename AAKReduction<T>::ResultType
AAKReduction<T>::compute(const ExponentialSumBase<DerivedF>& orig,
                         RealScalar threshold)
{
    static const auto eps = Eigen::NumTraits<RealScalar>::epsilon();
    using Eigen::numext::abs;
    using Eigen::numext::real;

    const Index n0 = orig.size();
    std::cout << "*** order of original function: " << n0 << std::endl;

    //-------------------------------------------------------------------------
    // Solve con-eigenvalue problem of the Cauchy-like matrix \f$C\f$ with
    // elements
    //
    // C(i,j)= sqrt(c[i]) * sqrt(conj(c[j])) / (1 - z[i] * conj(z[j]))
    //
    // where z[i] = exp(-p[i]).
    //-------------------------------------------------------------------------
    //
    // Rewrite the matrix element C(i,j) as
    //
    //                 a[i] * b[j]
    //   C(i,j) = ----------------------
    //             exp(x[i]) - exp(y[j])
    //
    // where,
    //
    //   a[i] = sqrt(c[i]) / exp(-p[i])
    //   b[j] = sqrt(conj(c[j]))
    //   x[i] = p[i]
    //   y[j] = -conj(p[j])
    //
    VectorType b(orig.weights().conjugate().sqrt());
    VectorType a(b.array().conjugate() * orig.exponents().exp());
    VectorType x(orig.exponents());
    VectorType y(-orig.exponents().conjugate());
    //
    // Compute partial Cholesky decomposition of matrix C, such that,
    //
    //   C = (P * L) * D^2 * (P * L)^H,
    //
    // where
    //
    //   - L: (n, m) matrix
    //   - D: (m, m) real diagonal matrix
    //   - P: (n, n) permutation matrix
    //
    //
    RRDType rrd;

    rrd.setThreshold(threshold * threshold * eps);
    rrd.compute(a, b, x, y);

    std::cout << "*** rank after quasi-Cauchy RRD: " << rrd.rank() << std::endl;

    //
    // Compute con-eigendecomposition
    //
    //   C = X D^2 X^H = U^C S U^T,
    //
    // where
    //
    //   - `X = P * L`: Cholesky factor (n, k)
    //   - `S`: (k, k) diagonal matrix. Diagonal elements `S(i,i)` are
    //          con-eigenvalues sorted in decreasing order.
    //   - `U`: (n, k) matrix. k-th column hold a con-eigenvector corresponding
    //          to k-th con-eigenvalue. The columns of `U` are orthogonal in the
    //          sense that `U^T * U = I`.
    //
    ConeigenSolverType ceig;
    ceig.compute(rrd.matrixPL(), rrd.vectorD());
    //
    // Determines the order of reduced system, \f$ M \f$ from the Hankel
    // singular values, which are corresponding to the con-eigenvalues of matrix
    // `C`.
    //
    const auto& sigma = ceig.coneigenvalues();
    auto pos          = std::upper_bound(
        sigma.data(), sigma.data() + sigma.size(), threshold,
        [&](RealScalar lhs, RealScalar rhs) { return lhs >= rhs; });
    const Index n1 = static_cast<Index>(pos - sigma.data());
    std::cout << "*** order after reduction: " << n1 << std::endl;

    if (n1 == Index())
    {
        return ResultType();
    }

    std::cout << "    sigma = " << sigma(n1 - 1) << std::endl;

    //-------------------------------------------------------------------------
    // Find the `n1` roots of rational function
    //
    //             n1  sqrt(alpha[i]) * conj(u[i])
    //   v(eta) = Sum  ---------------------------
    //            i=1     1 - conj(z[i]) * eta
    //
    // where eta is on the unit disk.
    //-------------------------------------------------------------------------

    //
    // Barycentric form of Lagrange interpolation
    //
    // VectorType& tau = x;
    // std::sort(tau.data(), tau.data() + tau.size(),
    //           [&](const Scalar& lhs, const Scalar& rhs) {
    //               return real(lhs) > real(rhs);
    //           });
    // VectorType& s = y;

    ResultType ret;

    return ret;
}

} // namespace: mxpfit

#endif /* MXPFIT_AAK_REDUCTION_HPP */
<commit_msg>Add functions for continued fraction interpolation<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017  Hidekazu Ikeno
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is

 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

///
/// \file aak_reduction.hpp
///
/// Sparse approximation of exponential sum using AAK theory.
///

#ifndef MXPFIT_AAK_REDUCTION_HPP
#define MXPFIT_AAK_REDUCTION_HPP

#include <algorithm>

#include <mxpfit/exponential_sum.hpp>
#include <mxpfit/quasi_cauchy_rrd.hpp>
#include <mxpfit/self_adjoint_coneigensolver.hpp>

namespace mxpfit
{

namespace detail
{

///
/// \internal
///
/// Create a rational function \f$ f(x)\f$ in the continued fraction form from a
/// given finite set of points \f$ x_{i} \f$ and their function values
/// \f$y_{i}.\f$
///
/// \f[
///   f(x)=\cfrac{a_{0}}{1+a_{1}\cfrac{x-x_{0}}{1+a_{2}\cfrac{x-x_{1}}{1+\cdots}}}
/// \f]
///
/// \param[in] x  set of points
/// \param[in] y function values at given points `x`. All elements of `y` should
///              not be zero.
/// \param[out] a  the coefficients of continued fraction interpolation
///

template <typename VecX, typename VecY, typename VecA>
void continued_fraction_interpolation_coeffs(const VecX& x, const VecY& y,
                                             VecA& a)
{
    using ResultType = typename VecY::Scalar;
    using Real       = typename Eigen::NumTraits<ResultType>::Real;
    using Index      = Eigen::Index;

    static const Real tiny = Eigen::NumTraits<Real>::min();

    const auto a0 = y[0];
    a[0]          = a0;

    for (Index i = 1; i < x.size(); ++i)
    {
        auto v        = a0 / y[i];
        const auto xi = x[i];
        for (Index j = 0; j < i - 1; ++j)
        {
            v -= Real(1);
            if (v == ResultType())
            {
                v = tiny;
            }
            v = (a[j + 1] * (xi - x[j])) / v;
        }
        v -= Real(1);
        if (v == ResultType())
        {
            v = tiny;
        }
        a[i] = (xi - x[i - 1]) / v;
    }
}

//
// Compute value of the continued fraction interpolation f(x) and its derivative
// f'(x).
//
// Let define R_{i}(x) recursively as
//
//  R_{n-1}(x) = a_{n-2} / [1 + a_{n-1} (x - x_{n-1})]
//  R_{i}(x) = a_{i-1} / [1 +  (x - x_{i}) R_{i+1}(x)]  (i=n-2,n-3,...1)
//
// then
//
//  f(x) = a_{0} / [1 + (x - x_{0}) R_{1}(x)].
//
// The derivative of f(x) becomes
//
//   f'(x) = -a_{0} / [1 + (x - x_{0}) R_{1}(x)]^2
//         * [R_{1}(x) + (x - x_{0}) R_{1}'(x)]
//
// The derivative of R_{i}(x) can also evaluated recursively as
//
//   R_{i}'(x) = -a_{i-1} / [1 +  (x - x_{i}) R_{i+1}(x)]^2
//             * [R_{i+1}(x) + (x - x_{i}) R_{i+1}'(x)]
//
// with
//
//   R_{n-1}'(x) = -a_{n-2} a_{n-1} / [1 + a_{n-1}(x - x_{n-1})]^2
//
template <typename VecX, typename VecA>
void continued_fraction_interpolation_eval_with_derivative(
    typename VecX::Scalar x, const VecX& xi, const VecA& ai)
{
    using ResultType = typename VecA::Scalar;
    using Real       = typename Eigen::NumTraits<ResultType>::Real;
    using Index      = Eigen::Index;

    static const Real tiny   = Eigen::NumTraits<Real>::min();
    constexpr const Real one = Real(1);

    const auto n = ai.size();

    auto d  = one + ai[n - 1] * (x - xi[n - 1]);
    auto f  = ai[n - 2] / d;      // R_{n-1}(x)
    auto df = -ai[n - 1] * f / d; // R_{n-1}'(x)

    for (Index k = n - 2; k > 0; --k)
    {
        d = one + (x - xi[k]) * f;
        if (d == ResultType())
        {
            d = tiny;
        }
        const auto f_pre = f;

        f  = ai[k - 1] / d;
        df = -f * (f_pre + (x - xi[k]) * df) / d;
    }

    // now f = f(x), df = f'(x)
}

template <typename VecX, typename VecY, typename VecA>
struct continued_fraction_interpolation
{
    using ArgumentType = typename VecX::Scalar;
    using ResultType   = typename VecY::Scalar;
    using Index        = Eigen::Index;

    continued_fraction_interpolation(const VecX& x, VecY& y, VecA& a)
        : m_x(x), m_y(y), m_a(a)
    {
        assert(m_x.size() == m_y.size());
        assert(m_a.size() == m_x.size());
        compute_coeffs();
    }

    ResultType operator()(ArgumentType x) const;

    ResultType derivative(ArgumentType x) const;

private:
    void compute_coeffs();
    const VecX& m_x;
    VecY& m_y;
    VecA& m_a;
};

template <typename VecX, typename VecY, typename VecA>
void continued_fraction_interpolation<VecX, VecY, VecA>::compute_coeffs()
{
    using Real             = typename Eigen::NumTraits<ResultType>::Real;
    static const Real tiny = Eigen::NumTraits<Real>::min();

    m_a[0] = m_y[0];

    for (Index i = 1; i < m_x.size(); ++i)
    {
        const auto xi = m_x[i];
        ResultType v  = m_a[0] / m_y[i];
        for (Index j = 0; j < i - 1; ++j)
        {
            v -= Real(1);
            if (v == ResultType())
            {
                v = tiny;
            }
            v = (m_a[j + 1] * (xi - m_x[j])) / v;
        }
        v -= Real(1);
        if (v == ResultType())
        {
            v = tiny;
        }
        m_a[i] = (xi - m_x[i - 1]) / v;
    }
}

template <typename VecX, typename VecY, typename VecA>
typename VecY::Scalar continued_fraction_interpolation<VecX, VecY, VecA>::
operator()(ArgumentType x) const
{
    using Real = typename Eigen::NumTraits<ResultType>::Real;
    constexpr static const Real one = Real(1);

    ResultType u = one;

    for (Index k = m_a.size() - 1; k > 0; --k)
    {
        // TODO: Need zero check?
        u = one + m_a[k] * (x - m_x[k - 1]) / u;
    }

    return m_a[0] / u;
}

template <typename VecX, typename VecY, typename VecA>
typename VecY::Scalar
continued_fraction_interpolation<VecX, VecY, VecA>::derivative(
    ArgumentType x) const
{
    using Real = typename Eigen::NumTraits<ResultType>::Real;
    constexpr static const Real one = Real(1);

    auto& u                    = m_y; // overwrite m_y
    ResultType u(u.size() - 1) = one;

    for (Index k = m_a.size() - 1; k > 0; --k)
    {
        // TODO: Need zero check?
        u = one + m_a[k] * (x - m_x[k - 1]) / u;
    }

    return m_a[0] / u;
}

} // namespace: detail

///
/// ### AAKReduction
///
/// \brief Find a truncated exponential sum function with smaller number of
///        terms using AAK theory.
///
/// \tparam T  Scalar type of exponential sum function.
///
/// For a given exponential sum function
/// \f[
///   f(t)=\sum_{j=1}^{N} c_{j}^{} e^{-p_{j}^{} t}, \quad
///   (\mathrm{Re}(a_{j}) > 0).
/// \f]
///
/// and prescribed accuracy \f$\epsilon > 0,\f$ this class calculates truncated
/// exponential \f$\tilde{f}(t)\f$ sum such that
///
/// \f[
///   \tilde{f}(t)=\sum_{j=1}^{M} \tilde{c}_{j}^{}e^{-\tilde{p}_{j}^{} t}, \quad
///   \left| f(t)-\tilde{f}(t) \right| < \epsilon,
/// \f]
///
///
/// The signal \f$\boldsymbol{f}=(f_{k})_{k=0}^{\infty}\f$ sampled on
/// \f$k=0,1,\dots\f$ are expressed as
///
/// \f[
///   f_{k}:= f(k) = \sum_{j=1}^{N} c_{j} z_{j}^{k}
/// \f]
///
/// where \f$z_{j}=e^{-p_{j}}\in \mathbb{D}:=\{z \in \mathbb{C}:0<|z|<1\}.\f$
/// The problem to find truncated exponential sum can be recast to find a new
/// signal \f$\tilde{\boldsymbol{f}},\f$ a sparse approximation of signal
/// \f$\boldsymbol{f},\f$ such that
///
/// \f[
///   \tilde{f}_{k}=\sum_{j=1}^{M}\tilde{c}_{j}^{} \tilde{z}_{j}^{k}, \quad
///   \|\boldsymbol{f}-\tilde{\boldsymbol{f}}\|<\epsilon. \f$
/// \f]
///
/// Then, the exponents of reduced exponential sum
/// \f$\tilde{f}(t)\f$ are obtained as \f$\tilde{p}_{j}=\tilde{z}_{j}.\f$
///
///
/// #### References
///
/// 1. Gerlind Plonka and Vlada Pototskaia, "Application of the AAK theory for
///    sparse approximation of exponential sums", arXiv 1609.09603v1 (2016).
///    [URL: https://arxiv.org/abs/1609.09603v1]
/// 2. Gregory Beylkin and Lucas Monzón, "On approximation of functions by
///    exponential sums", Appl. Comput. Harmon. Anal. **19** (2005) 17-48.
///    [DOI: https://doi.org/10.1016/j.acha.2005.01.003]
/// 3. T. S. Haut and G. Beylkin, "FAST AND ACCURATE CON-EIGENVALUE ALGORITHM
///    FOR OPTIMAL RATIONAL APPROXIMATIONS", SIAM J. Matrix Anal. Appl. **33**
///    (2012) 1101-1125.
///    [DOI: https://doi.org/10.1137/110821901]
/// 4. Terry Haut, Gregory Beylkin, and Lucas Monzón, "Solving Burgers’
///    equation using optimal rational approximations", Appl. Comput. Harmon.
///    Anal. **34** (2013) 83-95.
///    [DOI: https://doi.org/10.1016/j.acha.2012.03.004]
///
template <typename T>
class AAKReduction
{
public:
    using Scalar        = T;
    using RealScalar    = typename Eigen::NumTraits<Scalar>::Real;
    using ComplexScalar = std::complex<RealScalar>;
    using Index         = Eigen::Index;

    using VectorType = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
    using MatrixType = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
    using ResultType = ExponentialSum<Scalar>;

    ///
    /// Compute truncated exponential sum \f$ \hat{f}(t) \f$
    ///
    /// \tparam DerivedF type of exponential sum inheriting ExponentialSumBase
    ///
    /// \param[in] orig original exponential sum function, \f$ f(t) \f$
    /// \param[in] threshold  prescribed accuracy \f$0 < \epsilon \ll 1\f$
    ///
    /// \return An instance of ExponentialSum represents \f$\hat{f}(t)\f$
    ///
    template <typename DerivedF>
    ResultType compute(const ExponentialSumBase<DerivedF>& orig,
                       RealScalar threshold);

private:
    enum
    {
        IsComplex = Eigen::NumTraits<Scalar>::IsComplex,
    };

    using ConeigenSolverType = SelfAdjointConeigenSolver<Scalar>;
    using RRDType =
        QuasiCauchyRRD<Scalar, QuasiCauchyRRDFunctorLogPole<Scalar>>;
};

template <typename T>
template <typename DerivedF>
typename AAKReduction<T>::ResultType
AAKReduction<T>::compute(const ExponentialSumBase<DerivedF>& orig,
                         RealScalar threshold)
{
    static const auto eps = Eigen::NumTraits<RealScalar>::epsilon();
    using Eigen::numext::abs;
    using Eigen::numext::conj;
    using Eigen::numext::real;

    const Index n0 = orig.size();
    std::cout << "*** order of original function: " << n0 << std::endl;

    //-------------------------------------------------------------------------
    // Solve con-eigenvalue problem of the Cauchy-like matrix \f$C\f$ with
    // elements
    //
    // C(i,j)= sqrt(c[i]) * sqrt(conj(c[j])) / (1 - z[i] * conj(z[j]))
    //
    // where z[i] = exp(-p[i]).
    //-------------------------------------------------------------------------
    //
    // Rewrite the matrix element C(i,j) as
    //
    //                 a[i] * b[j]
    //   C(i,j) = ----------------------
    //             exp(x[i]) - exp(y[j])
    //
    // where,
    //
    //   a[i] = sqrt(c[i]) / exp(-p[i])
    //   b[j] = sqrt(conj(c[j]))
    //   x[i] = log(z[i]) = p[i]
    //   y[j] = log(1/conj(z[i])) = -conj(p[j])
    //
    VectorType b(orig.weights().conjugate().sqrt());
    VectorType a(b.array().conjugate() * orig.exponents().exp());
    VectorType x(orig.exponents());
    VectorType y(-orig.exponents().conjugate());
    //
    // Compute partial Cholesky decomposition of matrix C, such that,
    //
    //   C = (P * L) * D^2 * (P * L)^H,
    //
    // where
    //
    //   - L: (n, m) matrix
    //   - D: (m, m) real diagonal matrix
    //   - P: (n, n) permutation matrix
    //
    //
    RRDType rrd;

    rrd.setThreshold(threshold * threshold * eps);
    rrd.compute(a, b, x, y);

    std::cout << "*** rank after quasi-Cauchy RRD: " << rrd.rank() << std::endl;

    //
    // Compute con-eigendecomposition
    //
    //   C = X D^2 X^H = U^C S U^T,
    //
    // where
    //
    //   - `X = P * L`: Cholesky factor (n, k)
    //   - `S`: (k, k) diagonal matrix. Diagonal elements `S(i,i)` are
    //          con-eigenvalues sorted in decreasing order.
    //   - `U`: (n, k) matrix. k-th column hold a con-eigenvector corresponding
    //          to k-th con-eigenvalue. The columns of `U` are orthogonal in the
    //          sense that `U^T * U = I`.
    //
    ConeigenSolverType ceig;
    ceig.compute(rrd.matrixPL(), rrd.vectorD());
    //
    // Determines the order of reduced system, \f$ M \f$ from the Hankel
    // singular values, which are corresponding to the con-eigenvalues of matrix
    // `C`.
    //
    const auto& sigma = ceig.coneigenvalues();
    auto pos          = std::upper_bound(
        sigma.data(), sigma.data() + sigma.size(), threshold,
        [&](RealScalar lhs, RealScalar rhs) { return lhs >= rhs; });
    const Index n1 = static_cast<Index>(pos - sigma.data());
    std::cout << "*** order after reduction: " << n1 << std::endl;

    if (n1 == Index())
    {
        return ResultType();
    }

    std::cout << "    sigma = " << sigma(n1 - 1) << std::endl;

    //-------------------------------------------------------------------------
    // Find the `n1` roots of rational function
    //
    //             n0  sqrt(alpha[i]) * conj(u[i])
    //   v(eta) = Sum  ---------------------------
    //            i=1     1 - conj(z[i]) * eta
    //
    // where eta is on the unit disk.
    //-------------------------------------------------------------------------

    auto vec_u = ceig.coneigenvectors().col(n1 - 1);
    //
    // Approximation of v(eta) by continued fraction as
    //
    // v(eta) = a1 / (1+a2(eta-z1)/(1+a3(eta-z2)/(1+...)))
    //

    a(0) = conj(vec_u(0)) / b(0);
    for (Index i = 1; i < n0; ++i)
    {

        auto t = a(0) * b(i) / conj(vec_u(i));
        for (Index j = 0; j < i; ++i)
        {
        }
        a(i) = t;
    }

    //
    // Barycentric form of Lagrange interpolation
    //
    // VectorType& tau = x;
    // std::sort(tau.data(), tau.data() + tau.size(),
    //           [&](const Scalar& lhs, const Scalar& rhs) {
    //               return real(lhs) > real(rhs);
    //           });
    // VectorType& s = y;

    ResultType ret;

    return ret;
}

} // namespace: mxpfit

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


<commit_before>/** Compiler deficiency workarounds for libpqxx clients.
 *
 * Copyright (c) 2002-2017, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_COMPILER_PUBLIC
#define PQXX_H_COMPILER_PUBLIC

// Workarounds & definitions that need to be included even in library's headers
#include "pqxx/config-public-compiler.h"

// Workarounds for SUN Workshop 6
#if defined(__SUNPRO_CC)
#define PQXX_PRIVATE __hidden
#endif	// __SUNPRO_CC

#if defined(__GNUC__) && defined(PQXX_HAVE_GCC_CONST)
#define PQXX_CONST __attribute__ ((const))
#else
#define PQXX_CONST
#endif

#if defined(PQXX_HAVE_DEPRECATED)
#define PQXX_DEPRECATED [[deprecated]]
#elif defined(__GNUC__) && defined(PQXX_HAVE_GCC_DEPRECATED)
#define PQXX_DEPRECATED __attribute__ ((deprecated))
#else
#define PQXX_DEPRECATED
#endif

#if defined(__GNUC__) && defined(PQXX_HAVE_GCC_PURE)
#define PQXX_PURE __attribute__ ((pure))
#else
#define PQXX_PURE
#endif

// Workarounds for Windows
#ifdef _WIN32

/* For now, export DLL symbols if _DLL is defined.  This is done automatically
 * by the compiler when linking to the dynamic version of the runtime library,
 * according to "gzh"
 */
// TODO: Define custom macro to govern how libpqxx will be linked to client
#if !defined(PQXX_LIBEXPORT) && defined(PQXX_SHARED)
#define PQXX_LIBEXPORT __declspec(dllimport)
#endif	// !PQXX_LIBEXPORT && PQXX_SHARED

// Workarounds for Microsoft Visual C++
#ifdef _MSC_VER

// Suppress vtables on abstract classes.
#define PQXX_NOVTABLE __declspec(novtable)

// Automatically link with the appropriate libpq (static or dynamic, debug or
// release).  The default is to use the release DLL.  Define PQXX_PQ_STATIC to
// link to a static version of libpq, and _DEBUG to link to a debug version.
// The two may be combined.
#if defined(PQXX_AUTOLINK)
#if defined(PQXX_PQ_STATIC)
#ifdef _DEBUG
#pragma comment(lib, "libpqd")
#else
#pragma comment(lib, "libpq")
#endif
#else
#ifdef _DEBUG
#pragma comment(lib, "libpqddll")
#else
#pragma comment(lib, "libpqdll")
#endif
#endif
#endif

// If we're not compiling libpqxx itself, automatically link with the
// appropriate libpqxx library.  To link with the libpqxx DLL, define
// PQXX_SHARED; the default is to link with the static library.  A static link
// is the recommended practice.
//
// The preprocessor macro PQXX_INTERNAL is used to detect whether we
// are compiling the libpqxx library itself.  When you compile the library
// yourself using your own project file, make sure to include this macro.
#if defined(PQXX_AUTOLINK) && !defined(PQXX_INTERNAL)
  #ifdef PQXX_SHARED
    #ifdef _DEBUG
      #pragma comment(lib, "libpqxxD")
    #else
      #pragma comment(lib, "libpqxx")
    #endif
  #else // !PQXX_SHARED
    #ifdef _DEBUG
      #pragma comment(lib, "libpqxx_staticD")
    #else
      #pragma comment(lib, "libpqxx_static")
    #endif
  #endif
#endif

#endif	// _MSC_VER
#endif	// _WIN32

#ifndef PQXX_LIBEXPORT
#define PQXX_LIBEXPORT
#endif

#ifndef PQXX_PRIVATE
#define PQXX_PRIVATE
#endif

#ifndef PQXX_NOVTABLE
#define PQXX_NOVTABLE
#endif

#endif
<commit_msg>Document some macros.<commit_after>/** Compiler deficiency workarounds for libpqxx clients.
 *
 * Copyright (c) 2002-2017, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_COMPILER_PUBLIC
#define PQXX_H_COMPILER_PUBLIC

// Workarounds & definitions that need to be included even in library's headers
#include "pqxx/config-public-compiler.h"

// Workarounds for SUN Workshop 6
#if defined(__SUNPRO_CC)
#define PQXX_PRIVATE __hidden
#endif	// __SUNPRO_CC

#if defined(__GNUC__) && defined(PQXX_HAVE_GCC_CONST)
/// Declare function without effects and without reading anything but its args.
#define PQXX_CONST __attribute__ ((const))
#else
#define PQXX_CONST
#endif

#if defined(PQXX_HAVE_DEPRECATED)
/// Mark an item as deprecated.
#define PQXX_DEPRECATED [[deprecated]]
#elif defined(__GNUC__) && defined(PQXX_HAVE_GCC_DEPRECATED)
#define PQXX_DEPRECATED __attribute__ ((deprecated))
#else
#define PQXX_DEPRECATED
#endif

#if defined(__GNUC__) && defined(PQXX_HAVE_GCC_PURE)
/// Declare function "pure": no side effects, only reads globals and its args.
#define PQXX_PURE __attribute__ ((pure))
#else
#define PQXX_PURE
#endif

// Workarounds for Windows
#ifdef _WIN32

/* For now, export DLL symbols if _DLL is defined.  This is done automatically
 * by the compiler when linking to the dynamic version of the runtime library,
 * according to "gzh"
 */
// TODO: Define custom macro to govern how libpqxx will be linked to client
#if !defined(PQXX_LIBEXPORT) && defined(PQXX_SHARED)
#define PQXX_LIBEXPORT __declspec(dllimport)
#endif	// !PQXX_LIBEXPORT && PQXX_SHARED

// Workarounds for Microsoft Visual C++
#ifdef _MSC_VER

// Suppress vtables on abstract classes.
#define PQXX_NOVTABLE __declspec(novtable)

// Automatically link with the appropriate libpq (static or dynamic, debug or
// release).  The default is to use the release DLL.  Define PQXX_PQ_STATIC to
// link to a static version of libpq, and _DEBUG to link to a debug version.
// The two may be combined.
#if defined(PQXX_AUTOLINK)
#if defined(PQXX_PQ_STATIC)
#ifdef _DEBUG
#pragma comment(lib, "libpqd")
#else
#pragma comment(lib, "libpq")
#endif
#else
#ifdef _DEBUG
#pragma comment(lib, "libpqddll")
#else
#pragma comment(lib, "libpqdll")
#endif
#endif
#endif

// If we're not compiling libpqxx itself, automatically link with the
// appropriate libpqxx library.  To link with the libpqxx DLL, define
// PQXX_SHARED; the default is to link with the static library.  A static link
// is the recommended practice.
//
// The preprocessor macro PQXX_INTERNAL is used to detect whether we
// are compiling the libpqxx library itself.  When you compile the library
// yourself using your own project file, make sure to include this macro.
#if defined(PQXX_AUTOLINK) && !defined(PQXX_INTERNAL)
  #ifdef PQXX_SHARED
    #ifdef _DEBUG
      #pragma comment(lib, "libpqxxD")
    #else
      #pragma comment(lib, "libpqxx")
    #endif
  #else // !PQXX_SHARED
    #ifdef _DEBUG
      #pragma comment(lib, "libpqxx_staticD")
    #else
      #pragma comment(lib, "libpqxx_static")
    #endif
  #endif
#endif

#endif	// _MSC_VER
#endif	// _WIN32

#ifndef PQXX_LIBEXPORT
#define PQXX_LIBEXPORT
#endif

#ifndef PQXX_PRIVATE
#define PQXX_PRIVATE
#endif

#ifndef PQXX_NOVTABLE
#define PQXX_NOVTABLE
#endif

#endif
<|endoftext|>


<commit_before>#ifndef IZENELIB_UTIL_SKETCH_FMSKETCH_H_
#define IZENELIB_UTIL_SKETCH_FMSKETCH_H_

#include "ICardinality.hpp"
#include "Level2Sketch.hpp"
#include <util/hashFunction.h>
#include <common/type_defs.h>
#include <vector>
#include <stdint.h>
#include <math.h>

#define BITSET_SIZE 64

NS_IZENELIB_UTIL_BEGIN

template <typename ElemType>
class FMSketch : public ICardinality<ElemType>
{
public:
    typedef std::vector<std::bitset<BITSET_SIZE> >  FMSketchT;
    typedef ElemType DataTypeT;
    typedef ICardinality<DataTypeT> BaseType;
    typedef FMSketch<DataTypeT> ThisType;
    typedef Level2Sketch<DataTypeT>  Level2SketchT;

    FMSketch(uint64_t seed, int k)
        : seed_(seed),
        fm_k_((size_t)k)
    {
        sketch_.resize(fm_k_);
        level2sketches_.resize(fm_k_);
        for(size_t i = 0; i < (size_t)fm_k_; ++i)
            level2sketches_[i].resize(BITSET_SIZE);
    }

    size_t size() const
    {
        return sketch_.size();
    }

    void load(std::istream& is)
    {
        is.read((char*)&seed_, sizeof(seed_));
        is.read((char*)&fm_k_, sizeof(fm_k_));
        FMSketchT().swap(sketch_);
        sketch_.reserve(fm_k_);
        level2sketches_.resize(fm_k_);
        for(size_t i = 0; i < fm_k_; ++i)
        {
            char data[BITSET_SIZE + 1];
            is.read((char*)&data, BITSET_SIZE);
            data[BITSET_SIZE] = '\0';
            sketch_.push_back(std::bitset<BITSET_SIZE>(std::string(data)));
            level2sketches_[i].load(is);
        }
    }

    void save(std::ostream& os) const
    {
        os.write((const char*)&seed_, sizeof(seed_));
        os.write((const char*)&fm_k_, sizeof(fm_k_));
        for(size_t i = 0; i < fm_k_; ++i)
        {
            os.write((const char*)sketch_[i].to_string().data(), BITSET_SIZE);
            level2sketches_[i].save(os);
        }
    }

    void updateSketch(const DataTypeT& data)
    {
        for(size_t i = 0; i < fm_k_; ++i)
        {
            uint64_t v = izenelib::util::MurmurHash64A(&data,
                sizeof(DataTypeT), seed_ + i);
            uint8_t lsb = LSB(v);
            sketch_[i].set(lsb);
            level2sketches_[i].updateBucket(lsb, data);
        }
    }

    size_t setUnionEstimator(const ThisType& src) const
    {
        double f = (1 + E) * fm_k_ / 8;
        size_t index = 0;
        size_t count = 0;
        while(index < BITSET_SIZE)
        {
            count = 0;
            for(size_t i = 0; i < fm_k_; ++i)
            {
                if(!level2sketches_[i].emptyBucket(index) ||
                    !src.level2sketches_[i].emptyBucket(index))
                {
                    count++;
                }
            }
            if(count <= f)
                break;
            else
                index++;
        }
        double p = count/fm_k_;
        double R = pow(2, index + 1);
        return log(1 - p)/log(1 - 1/R);
    }

    size_t setIntersectEstimator(const ThisType& src) const
    {
        if(src.size() == 0 || size() == 0)
            return 0;

        assert(src.size() == size());
        size_t sum = 0;
        size_t count = 0;
        size_t union_card = setUnionEstimator(src);
        for(size_t i = 0; i < fm_k_; ++i)
        {
            size_t index = std::ceil(std::log(2*union_card/(1 - E)));
            //size_t index = std::ceil(std::log(2*union_card/(level2sketches_.size()*(1 - E)*(1 - E))));
            if(index > BITSET_SIZE)
                continue;
            int atomic_estimate = level2sketches_[i].atomicIntersectEstimator(index, src.level2sketches_[i]);
            if(atomic_estimate != -1)
            {
                sum += atomic_estimate;
                count++;
            }
        }
        if(count == 0)
            return 0;
        return sum*union_card/count;
    }

    size_t intersectCard(const BaseType* src) const
    {
        const ThisType* fm_src = dynamic_cast<const ThisType*>(src);
        if(fm_src == NULL)
        {
            throw -1;
        }

        return setIntersectEstimator(*fm_src);
    }

    size_t getCardinate() const
    {
        if(sketch_.empty())
            return 0;
        int sum = 0;
        for(size_t i = 0; i < fm_k_; ++i)
        {
            int leftmost_zero = BITSET_SIZE;
            for(int m = BITSET_SIZE - 1; m >= 0; --m)
            {
                if(!sketch_[i].test(m))
                    leftmost_zero = m;
            }
            sum += leftmost_zero;
        }
        return 1.2928 * pow(2, sum/fm_k_);
    }

    void unionSketch(const BaseType* src)
    {
        if(src->size() == 0)
            return;

        const ThisType* fm_src = dynamic_cast<const ThisType*>(src);
        if(fm_src == NULL)
        {
            throw -1;
        }
        if(sketch_.empty())
        {
            *this = *fm_src;
            return;
        }
        assert(src->size() == size());
        const FMSketchT& tmp_src = fm_src->sketch_;
        for(size_t i = 0; i < sketch_.size(); ++i)
        {
            sketch_[i] |= tmp_src[i];
            level2sketches_[i].unionLevel2Sketch(fm_src->level2sketches_[i]);
        }
    }

    static size_t setUnionEstimator(const std::vector<ThisType>& sketches)
    {
        if(sketches.empty())
            return 0;
        size_t fm_k = sketches[0].fm_k_;
        double f = (1 + E) * fm_k / 8;
        size_t index = 0;
        std::vector<Level2SketchT> tmp_level2sketches;
        tmp_level2sketches.resize(sketches.size());
        size_t count = 0;
        while(index < BITSET_SIZE)
        {
            count = 0;
            for(size_t i = 0; i < fm_k; ++i)
            {
                for(size_t j = 0; j < sketches.size(); ++j)
                {
                    tmp_level2sketches[j] = sketches[j].level2sketches_[i];
                }
                if(atomicUnionBucketEstimator(index, tmp_level2sketches))
                {
                    count++;
                }
            }
            if(count <= f)
                break;
            else
                index++;
        }
        double p = count/fm_k;
        double R = pow(2, index + 1);
        return log(1 - p)/log(1 - 1/R);
    }

    static size_t setUnionWithIntersectEstimator(const std::vector<ThisType>& union_sketch,
        const std::vector<ThisType>& filter_sketch)
    {
        if(union_sketch.empty() || filter_sketch.empty())
            return 0;
        size_t sum = 0;
        size_t count = 0;
        size_t fm_k = union_sketch[0].fm_k_;
        std::vector<ThisType> all_sketches = union_sketch;
        all_sketches.insert(all_sketches.end(), filter_sketch.begin(), filter_sketch.end());
        size_t union_card = setUnionEstimator(all_sketches);

        std::vector<Level2SketchT> tmp_level2sketches;
        tmp_level2sketches.resize(union_sketch.size());
        std::vector<Level2SketchT> tmpfilter_level2sketches;
        tmpfilter_level2sketches.resize(filter_sketch.size());

        for(size_t i = 0; i < fm_k; ++i)
        {
            size_t index = std::ceil(std::log(2*union_card/(1 - E)));
            //size_t index = std::ceil(std::log(2*union_card/(level2sketches_.size()*(1 - E)*(1 - E))));
            if(index > BITSET_SIZE)
                continue;
            for(size_t j = 0; j < union_sketch.size(); ++j)
            {
                tmp_level2sketches[j] = union_sketch[j].level2sketches_[i];
            }
            for(size_t j = 0; j < filter_sketch.size(); ++j)
            {
                tmpfilter_level2sketches[j] = filter_sketch[j].level2sketches_[i];
            }
            int atomic_estimate = atomicUnionWithIntersectBucketEstimator(index, tmp_level2sketches,
                tmpfilter_level2sketches);
            if(atomic_estimate != -1)
            {
                sum += atomic_estimate;
                count++;
            }
        }
        if(count == 0)
            return 0;
        return sum*union_card/count;
    }

private:
    inline uint8_t LSB(uint64_t v)
    {
        for(uint8_t i = 0; i < 64; ++i)
        {
            if((v & ((uint64_t)1 << i)) != 0)
                return i;
        }
        return 63;
    }

    // the bits used for bucket index.
    uint64_t seed_;
    size_t fm_k_;
    FMSketchT sketch_;
    std::vector<Level2SketchT> level2sketches_;
    static const double E = 0.618;
};

NS_IZENELIB_UTIL_END

#endif
<commit_msg>use the time-efficient synopse for fmsketch<commit_after>#ifndef IZENELIB_UTIL_SKETCH_FMSKETCH_H_
#define IZENELIB_UTIL_SKETCH_FMSKETCH_H_

#include "ICardinality.hpp"
#include "Level2Sketch.hpp"
#include <util/hashFunction.h>
#include <common/type_defs.h>
#include <vector>
#include <stdint.h>
#include <math.h>

#define BITSET_SIZE 64

NS_IZENELIB_UTIL_BEGIN

template <typename ElemType>
class FMSketch : public ICardinality<ElemType>
{
public:
    typedef std::vector<std::bitset<BITSET_SIZE> >  FMSketchT;
    typedef ElemType DataTypeT;
    typedef ICardinality<DataTypeT> BaseType;
    typedef FMSketch<DataTypeT> ThisType;
    typedef Level2Sketch<DataTypeT>  Level2SketchT;

    FMSketch(uint64_t seed, int k)
        : seed_(seed),
        fm_k_((size_t)k)
    {
        sketch_.resize(fm_k_);
        level2sketches_.resize(fm_k_);
        for(size_t i = 0; i < (size_t)fm_k_; ++i)
            level2sketches_[i].resize(BITSET_SIZE);
    }

    size_t size() const
    {
        return sketch_.size();
    }

    void load(std::istream& is)
    {
        is.read((char*)&seed_, sizeof(seed_));
        is.read((char*)&fm_k_, sizeof(fm_k_));
        FMSketchT().swap(sketch_);
        sketch_.reserve(fm_k_);
        level2sketches_.resize(fm_k_);
        for(size_t i = 0; i < fm_k_; ++i)
        {
            char data[BITSET_SIZE + 1];
            is.read((char*)&data, BITSET_SIZE);
            data[BITSET_SIZE] = '\0';
            sketch_.push_back(std::bitset<BITSET_SIZE>(std::string(data)));
            level2sketches_[i].load(is);
        }
    }

    void save(std::ostream& os) const
    {
        os.write((const char*)&seed_, sizeof(seed_));
        os.write((const char*)&fm_k_, sizeof(fm_k_));
        for(size_t i = 0; i < fm_k_; ++i)
        {
            os.write((const char*)sketch_[i].to_string().data(), BITSET_SIZE);
            level2sketches_[i].save(os);
        }
    }

    void updateSketch(const DataTypeT& data)
    {
        uint64_t v = izenelib::util::MurmurHash64A(&data,
            sizeof(DataTypeT), seed_);
        uint8_t lsb = LSB(v/fm_k_);
        sketch_[v % fm_k_].set(lsb);
        level2sketches_[v % fm_k_].updateBucket(lsb, data);
    }

    size_t setUnionEstimator(const ThisType& src) const
    {
        double f = (1 + E) * (double)fm_k_ / 8;
        size_t index = 0;
        size_t count = 0;
        while(index < BITSET_SIZE)
        {
            count = 0;
            for(size_t i = 0; i < fm_k_; ++i)
            {
                if(!level2sketches_[i].emptyBucket(index) ||
                    !src.level2sketches_[i].emptyBucket(index))
                {
                    count++;
                }
            }
            if(count <= f)
                break;
            else
                index++;
        }
        double p = count/(double)fm_k_;
        double R = pow(2, index + 1);
        return log(1 - p)/log(1 - 1/R) * fm_k_;
    }

    size_t setIntersectEstimator(const ThisType& src) const
    {
        if(src.size() == 0 || size() == 0)
            return 0;

        assert(src.size() == size());
        size_t sum = 0;
        size_t count = 0;
        size_t union_card = setUnionEstimator(src);
        for(size_t i = 0; i < fm_k_; ++i)
        {
            //size_t index = std::ceil(std::log(2*union_card/(1 - E)));
            size_t index = std::ceil(std::log(2 * (double)union_card / (fm_k_ * (1 - E) * (1 - E))));
            if(index > BITSET_SIZE)
                continue;
            int atomic_estimate = level2sketches_[i].atomicIntersectEstimator(index, src.level2sketches_[i]);
            if(atomic_estimate != -1)
            {
                sum += atomic_estimate;
                count++;
            }
        }
        if(count == 0)
            return 0;
        return sum*union_card/count;
    }

    size_t intersectCard(const BaseType* src) const
    {
        const ThisType* fm_src = dynamic_cast<const ThisType*>(src);
        if(fm_src == NULL)
        {
            throw -1;
        }

        return setIntersectEstimator(*fm_src);
    }

    size_t getCardinate() const
    {
        if(sketch_.empty())
            return 0;
        int sum = 0;
        for(size_t i = 0; i < fm_k_; ++i)
        {
            int leftmost_zero = BITSET_SIZE;
            for(int m = BITSET_SIZE - 1; m >= 0; --m)
            {
                if(!sketch_[i].test(m))
                    leftmost_zero = m;
            }
            sum += leftmost_zero;
        }
        return 1.2928 * pow(2, sum/(double)fm_k_) * fm_k_;
    }

    void unionSketch(const BaseType* src)
    {
        if(src->size() == 0)
            return;

        const ThisType* fm_src = dynamic_cast<const ThisType*>(src);
        if(fm_src == NULL)
        {
            throw -1;
        }
        if(sketch_.empty())
        {
            *this = *fm_src;
            return;
        }
        assert(src->size() == size());
        const FMSketchT& tmp_src = fm_src->sketch_;
        for(size_t i = 0; i < sketch_.size(); ++i)
        {
            sketch_[i] |= tmp_src[i];
            level2sketches_[i].unionLevel2Sketch(fm_src->level2sketches_[i]);
        }
    }

    static size_t setUnionEstimator(const std::vector<ThisType>& sketches)
    {
        if(sketches.empty())
            return 0;
        size_t fm_k = sketches[0].fm_k_;
        double f = (1 + E) * (double)fm_k / 8;
        size_t index = 0;
        std::vector<Level2SketchT> tmp_level2sketches;
        tmp_level2sketches.resize(sketches.size());
        size_t count = 0;
        while(index < BITSET_SIZE)
        {
            count = 0;
            for(size_t i = 0; i < fm_k; ++i)
            {
                for(size_t j = 0; j < sketches.size(); ++j)
                {
                    tmp_level2sketches[j] = sketches[j].level2sketches_[i];
                }
                if(atomicUnionBucketEstimator(index, tmp_level2sketches))
                {
                    count++;
                }
            }
            if(count <= f)
                break;
            else
                index++;
        }
        double p = count/(double)fm_k;
        double R = pow(2, index + 1);
        return log(1 - p)/log(1 - 1/R) * fm_k;
    }

    static size_t setUnionWithIntersectEstimator(const std::vector<ThisType>& union_sketch,
        const std::vector<ThisType>& filter_sketch)
    {
        if(union_sketch.empty() || filter_sketch.empty())
            return 0;
        size_t sum = 0;
        size_t count = 0;
        size_t fm_k = union_sketch[0].fm_k_;
        std::vector<ThisType> all_sketches = union_sketch;
        all_sketches.insert(all_sketches.end(), filter_sketch.begin(), filter_sketch.end());
        size_t union_card = setUnionEstimator(all_sketches);

        std::vector<Level2SketchT> tmp_level2sketches;
        tmp_level2sketches.resize(union_sketch.size());
        std::vector<Level2SketchT> tmpfilter_level2sketches;
        tmpfilter_level2sketches.resize(filter_sketch.size());

        for(size_t i = 0; i < fm_k; ++i)
        {
            //size_t index = std::ceil(std::log(2*union_card/(1 - E)));
            size_t index = std::ceil(std::log(2 * (double)union_card / ( fm_k * (1 - E) * (1 - E) )));
            if(index > BITSET_SIZE)
                continue;
            for(size_t j = 0; j < union_sketch.size(); ++j)
            {
                tmp_level2sketches[j] = union_sketch[j].level2sketches_[i];
            }
            for(size_t j = 0; j < filter_sketch.size(); ++j)
            {
                tmpfilter_level2sketches[j] = filter_sketch[j].level2sketches_[i];
            }
            int atomic_estimate = atomicUnionWithIntersectBucketEstimator(index, tmp_level2sketches,
                tmpfilter_level2sketches);
            if(atomic_estimate != -1)
            {
                sum += atomic_estimate;
                count++;
            }
        }
        if(count == 0)
            return 0;
        return sum*union_card/count;
    }

private:
    inline uint8_t LSB(uint64_t v)
    {
        for(uint8_t i = 0; i < 64; ++i)
        {
            if((v & ((uint64_t)1 << i)) != 0)
                return i;
        }
        return 63;
    }

    // the bits used for bucket index.
    uint64_t seed_;
    size_t fm_k_;
    FMSketchT sketch_;
    std::vector<Level2SketchT> level2sketches_;
    static const double E = 0.618;
};

NS_IZENELIB_UTIL_END

#endif
<|endoftext|>


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

#include <znc/Modules.h>
#include <znc/User.h>

class CNickServ : public CModule {
    void DoNickCommand(const CString& sCmd, const CString& sNick) {
        MCString msValues;
        msValues["nickname"] = sNick;
        msValues["password"] = GetNV("Password");
        PutIRC(CString::NamedFormat(GetNV(sCmd), msValues));
    }

  public:
    void SetCommand(const CString& sLine) {
        SetNV("Password", sLine.Token(1, true));
        PutModule("Password set");
    }

    void ClearCommand(const CString& sLine) { DelNV("Password"); }

    void SetNSNameCommand(const CString& sLine) {
        SetNV("NickServName", sLine.Token(1, true));
        PutModule("NickServ name set");
    }

    void ClearNSNameCommand(const CString& sLine) { DelNV("NickServName"); }

    void ViewCommandsCommand(const CString& sLine) {
        PutModule("IDENTIFY " + GetNV("IdentifyCmd"));
    }

    void SetCommandCommand(const CString& sLine) {
        CString sCmd = sLine.Token(1);
        CString sNewCmd = sLine.Token(2, true);
        if (sCmd.Equals("IDENTIFY")) {
            SetNV("IdentifyCmd", sNewCmd);
        } else {
            PutModule("No such editable command. See ViewCommands for list.");
            return;
        }
        PutModule("Ok");
    }

    MODCONSTRUCTOR(CNickServ) {
        AddHelpCommand();
        AddCommand("Set",
                   static_cast<CModCommand::ModCmdFunc>(&CNickServ::SetCommand),
                   "password");
        AddCommand("Clear", static_cast<CModCommand::ModCmdFunc>(
                                &CNickServ::ClearCommand),
                   "", "Clear your nickserv password");
        AddCommand("SetNSName", static_cast<CModCommand::ModCmdFunc>(
                                    &CNickServ::SetNSNameCommand),
                   "nickname",
                   "Set NickServ name (Useful on networks like EpiKnet, where "
                   "NickServ is named Themis)");
        AddCommand("ClearNSName", static_cast<CModCommand::ModCmdFunc>(
                                      &CNickServ::ClearNSNameCommand),
                   "", "Reset NickServ name to default (NickServ)");
        AddCommand("ViewCommands", static_cast<CModCommand::ModCmdFunc>(
                                       &CNickServ::ViewCommandsCommand),
                   "",
                   "Show patterns for lines, which are being sent to NickServ");
        AddCommand("SetCommand", static_cast<CModCommand::ModCmdFunc>(
                                     &CNickServ::SetCommandCommand),
                   "cmd new-pattern", "Set pattern for commands");
    }

    ~CNickServ() override {}

    bool OnLoad(const CString& sArgs, CString& sMessage) override {
        if (!sArgs.empty() && sArgs != "<hidden>") {
            SetNV("Password", sArgs);
            SetArgs("<hidden>");
        }

        if (GetNV("IdentifyCmd").empty()) {
            SetNV("IdentifyCmd", "NICKSERV IDENTIFY {password}");
        }

        return true;
    }

    void HandleMessage(CNick& Nick, const CString& sMessage) {
        CString sNickServName = (!GetNV("NickServName").empty())
                                    ? GetNV("NickServName")
                                    : "NickServ";
        if (!GetNV("Password").empty() && Nick.NickEquals(sNickServName) &&
            (sMessage.find("msg") != CString::npos ||
             sMessage.find("authenticate") != CString::npos ||
             sMessage.find("choose a different nickname") != CString::npos ||
             sMessage.find("please choose a different nick") != CString::npos ||
             sMessage.find("If this is your nick, identify yourself with") !=
                 CString::npos ||
             sMessage.find("If this is your nick, type") != CString::npos ||
             sMessage.find("This is a registered nickname, please identify") !=
                 CString::npos ||
             sMessage.StripControls_n().find(
                 "type /NickServ IDENTIFY password") != CString::npos ||
             sMessage.StripControls_n().find(
                 "type /msg NickServ IDENTIFY password") != CString::npos) &&
            sMessage.AsUpper().find("IDENTIFY") != CString::npos &&
            sMessage.find("help") == CString::npos) {
            MCString msValues;
            msValues["password"] = GetNV("Password");
            PutIRC(CString::NamedFormat(GetNV("IdentifyCmd"), msValues));
        }
    }

    EModRet OnPrivMsg(CNick& Nick, CString& sMessage) override {
        HandleMessage(Nick, sMessage);
        return CONTINUE;
    }

    EModRet OnPrivNotice(CNick& Nick, CString& sMessage) override {
        HandleMessage(Nick, sMessage);
        return CONTINUE;
    }
};

template <>
void TModInfo<CNickServ>(CModInfo& Info) {
    Info.SetWikiPage("nickserv");
    Info.SetHasArgs(true);
    Info.SetArgsHelpText("Please enter your nickserv password.");
}

NETWORKMODULEDEFS(CNickServ, "Auths you with NickServ")
<commit_msg>nickserv: support messages from X3 services<commit_after>/*
 * Copyright (C) 2004-2016 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Modules.h>
#include <znc/User.h>

class CNickServ : public CModule {
    void DoNickCommand(const CString& sCmd, const CString& sNick) {
        MCString msValues;
        msValues["nickname"] = sNick;
        msValues["password"] = GetNV("Password");
        PutIRC(CString::NamedFormat(GetNV(sCmd), msValues));
    }

  public:
    void SetCommand(const CString& sLine) {
        SetNV("Password", sLine.Token(1, true));
        PutModule("Password set");
    }

    void ClearCommand(const CString& sLine) { DelNV("Password"); }

    void SetNSNameCommand(const CString& sLine) {
        SetNV("NickServName", sLine.Token(1, true));
        PutModule("NickServ name set");
    }

    void ClearNSNameCommand(const CString& sLine) { DelNV("NickServName"); }

    void ViewCommandsCommand(const CString& sLine) {
        PutModule("IDENTIFY " + GetNV("IdentifyCmd"));
    }

    void SetCommandCommand(const CString& sLine) {
        CString sCmd = sLine.Token(1);
        CString sNewCmd = sLine.Token(2, true);
        if (sCmd.Equals("IDENTIFY")) {
            SetNV("IdentifyCmd", sNewCmd);
        } else {
            PutModule("No such editable command. See ViewCommands for list.");
            return;
        }
        PutModule("Ok");
    }

    MODCONSTRUCTOR(CNickServ) {
        AddHelpCommand();
        AddCommand("Set",
                   static_cast<CModCommand::ModCmdFunc>(&CNickServ::SetCommand),
                   "password");
        AddCommand("Clear", static_cast<CModCommand::ModCmdFunc>(
                                &CNickServ::ClearCommand),
                   "", "Clear your nickserv password");
        AddCommand("SetNSName", static_cast<CModCommand::ModCmdFunc>(
                                    &CNickServ::SetNSNameCommand),
                   "nickname",
                   "Set NickServ name (Useful on networks like EpiKnet, where "
                   "NickServ is named Themis)");
        AddCommand("ClearNSName", static_cast<CModCommand::ModCmdFunc>(
                                      &CNickServ::ClearNSNameCommand),
                   "", "Reset NickServ name to default (NickServ)");
        AddCommand("ViewCommands", static_cast<CModCommand::ModCmdFunc>(
                                       &CNickServ::ViewCommandsCommand),
                   "",
                   "Show patterns for lines, which are being sent to NickServ");
        AddCommand("SetCommand", static_cast<CModCommand::ModCmdFunc>(
                                     &CNickServ::SetCommandCommand),
                   "cmd new-pattern", "Set pattern for commands");
    }

    ~CNickServ() override {}

    bool OnLoad(const CString& sArgs, CString& sMessage) override {
        if (!sArgs.empty() && sArgs != "<hidden>") {
            SetNV("Password", sArgs);
            SetArgs("<hidden>");
        }

        if (GetNV("IdentifyCmd").empty()) {
            SetNV("IdentifyCmd", "NICKSERV IDENTIFY {password}");
        }

        return true;
    }

    void HandleMessage(CNick& Nick, const CString& sMessage) {
        CString sNickServName = (!GetNV("NickServName").empty())
                                    ? GetNV("NickServName")
                                    : "NickServ";
        if (!GetNV("Password").empty() && Nick.NickEquals(sNickServName) &&
            (sMessage.find("msg") != CString::npos ||
             sMessage.find("authenticate") != CString::npos ||
             sMessage.find("choose a different nickname") != CString::npos ||
             sMessage.find("please choose a different nick") != CString::npos ||
             sMessage.find("If this is your nick, identify yourself with") !=
                 CString::npos ||
             sMessage.find("If this is your nick, type") != CString::npos ||
             sMessage.find("This is a registered nickname, please identify") !=
                 CString::npos ||
             sMessage.find("is a registered nick - you must auth to account") !=
                 CString::npos ||
             sMessage.StripControls_n().find(
                 "type /NickServ IDENTIFY password") != CString::npos ||
             sMessage.StripControls_n().find(
                 "type /msg NickServ IDENTIFY password") != CString::npos) &&
            sMessage.AsUpper().find("IDENTIFY") != CString::npos &&
            sMessage.find("help") == CString::npos) {
            MCString msValues;
            msValues["password"] = GetNV("Password");
            PutIRC(CString::NamedFormat(GetNV("IdentifyCmd"), msValues));
        }
    }

    EModRet OnPrivMsg(CNick& Nick, CString& sMessage) override {
        HandleMessage(Nick, sMessage);
        return CONTINUE;
    }

    EModRet OnPrivNotice(CNick& Nick, CString& sMessage) override {
        HandleMessage(Nick, sMessage);
        return CONTINUE;
    }
};

template <>
void TModInfo<CNickServ>(CModInfo& Info) {
    Info.SetWikiPage("nickserv");
    Info.SetHasArgs(true);
    Info.SetArgsHelpText("Please enter your nickserv password.");
}

NETWORKMODULEDEFS(CNickServ, "Auths you with NickServ")
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include <cstdlib>
#include <system_error>

#pragma push_macro("WIN32_LEAN_AND_MEAN")
#pragma push_macro("NOMINMAX")
#ifndef WIN32_LEAN_AND_MEAN
#  define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#  define NOMINMAX
#endif
#include <Windows.h>
#include <shellapi.h>
#pragma pop_macro("WIN32_LEAN_AND_MEAN")
#pragma pop_macro("NOMINMAX")

#include "EngineWin.hpp"
#include "NativeWindowWin.hpp"
#include "ShellExecuteErrorCategory.hpp"
#include "../../input/windows/InputSystemWin.hpp"
#include "../../utils/Log.hpp"

namespace ouzel::core::windows
{
    namespace
    {
        std::vector<std::string> parseArgs(int argc, LPWSTR* argv)
        {
            std::vector<std::string> result;
            if (argv)
                for (int i = 0; i < argc; ++i)
                {
                    const int bufferSize = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr);
                    if (bufferSize == 0)
                        throw std::system_error(GetLastError(), std::system_category(), "Failed to convert wide char to UTF-8");

                    std::vector<char> buffer(bufferSize);
                    if (WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, buffer.data(), bufferSize, nullptr, nullptr) == 0)
                        throw std::system_error(GetLastError(), std::system_category(), "Failed to convert wide char to UTF-8");

                    result.push_back(buffer.data());
                }

            return result;
        }

        const ShellExecuteErrorCategory shellExecuteErrorCategory{};
    }

    Engine::Engine(int argc, LPWSTR* argv):
        core::Engine(parseArgs(argc, argv))
    {
    }

    static void translateMessage(HWND window, const std::set<HACCEL>& accelerators, MSG& message)
    {
        bool translate = true;

        for (HACCEL accelerator : accelerators)
        {
            if (TranslateAccelerator(window, accelerator, &message))
                translate = false;
        }

        if (translate)
        {
            TranslateMessage(&message);
            DispatchMessage(&message);
        }
    }

    void Engine::run()
    {
        init();
        start();

        auto inputWin = static_cast<input::windows::InputSystem*>(inputManager->getInputSystem());
        auto windowWin = static_cast<NativeWindow*>(window->getNativeWindow());

        MSG message;

        while (active)
        {
            if (!paused)
            {
                if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
                {
                    translateMessage(windowWin->getNativeWindow(),
                                     windowWin->accelerators, message);

                    if (message.message == WM_QUIT)
                    {
                        exit();
                        break;
                    }
                }
            }
            else
            {
                const BOOL ret = GetMessage(&message, nullptr, 0, 0);
                if (ret == -1)
                    throw std::system_error(GetLastError(), std::system_category(), "Failed to get message");

                if (ret == 0)
                {
                    exit();
                    break;
                }
                else
                {
                    translateMessage(windowWin->getNativeWindow(),
                                     windowWin->accelerators, message);
                }
            }

            inputWin->update();
        }

        exit();
    }

    void Engine::runOnMainThread(const std::function<void()>& func)
    {
        auto windowWin = static_cast<NativeWindow*>(window->getNativeWindow());

        std::unique_lock lock(executeMutex);
        executeQueue.push(func);
        lock.unlock();

        if (!PostMessage(windowWin->getNativeWindow(), WM_USER, 0, 0))
            throw std::system_error(GetLastError(), std::system_category(), "Failed to post message");

    }

    void Engine::executeAll()
    {
        std::function<void()> func;

        for (;;)
        {
            std::unique_lock lock(executeMutex);

            if (executeQueue.empty())
                break;

            func = std::move(executeQueue.front());
            executeQueue.pop();
            lock.unlock();

            if (func) func();
        }
    }

    void Engine::openUrl(const std::string& url)
    {
        const int buferSize = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
        if (buferSize == 0)
            throw std::system_error(GetLastError(), std::system_category(), "Failed to convert UTF-8 to wide char");

        auto buffer = std::make_unique<WCHAR[]>(buferSize);
        if (MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, buffer.get(), buferSize) == 0)
            throw std::system_error(GetLastError(), std::system_category(), "Failed to convert UTF-8 to wide char");

        // Result of the ShellExecuteW can be cast only to an int (https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
        const auto result = ShellExecuteW(nullptr, L"open", buffer.get(), nullptr, nullptr, SW_SHOWNORMAL);
        int status;
        std::memcpy(&status, &result, sizeof(status));
        if (status <= 32)
            throw std::system_error(status, shellExecuteErrorCategory, "Failed to execute open");
    }
}
<commit_msg>Refactor the code<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include <cstdlib>
#include <system_error>

#pragma push_macro("WIN32_LEAN_AND_MEAN")
#pragma push_macro("NOMINMAX")
#ifndef WIN32_LEAN_AND_MEAN
#  define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#  define NOMINMAX
#endif
#include <Windows.h>
#include <shellapi.h>
#pragma pop_macro("WIN32_LEAN_AND_MEAN")
#pragma pop_macro("NOMINMAX")

#include "EngineWin.hpp"
#include "NativeWindowWin.hpp"
#include "ShellExecuteErrorCategory.hpp"
#include "../../input/windows/InputSystemWin.hpp"
#include "../../utils/Log.hpp"

namespace ouzel::core::windows
{
    namespace
    {
        std::vector<std::string> parseArgs(int argc, LPWSTR* argv)
        {
            std::vector<std::string> result;
            if (argv)
                for (int i = 0; i < argc; ++i)
                {
                    const int bufferSize = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr);
                    if (bufferSize == 0)
                        throw std::system_error(GetLastError(), std::system_category(), "Failed to convert wide char to UTF-8");

                    std::vector<char> buffer(bufferSize);
                    if (WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, buffer.data(), bufferSize, nullptr, nullptr) == 0)
                        throw std::system_error(GetLastError(), std::system_category(), "Failed to convert wide char to UTF-8");

                    result.push_back(buffer.data());
                }

            return result;
        }

        const ShellExecuteErrorCategory shellExecuteErrorCategory{};
    }

    Engine::Engine(int argc, LPWSTR* argv):
        core::Engine(parseArgs(argc, argv))
    {
    }

    static void translateMessage(HWND window, const std::set<HACCEL>& accelerators, MSG& message)
    {
        bool translate = true;

        for (HACCEL accelerator : accelerators)
            if (TranslateAccelerator(window, accelerator, &message))
                translate = false;

        if (translate)
        {
            TranslateMessage(&message);
            DispatchMessage(&message);
        }
    }

    void Engine::run()
    {
        init();
        start();

        auto inputWin = static_cast<input::windows::InputSystem*>(inputManager->getInputSystem());
        auto windowWin = static_cast<NativeWindow*>(window->getNativeWindow());

        while (active)
        {
            if (!paused)
            {
                MSG message;
                if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
                {
                    translateMessage(windowWin->getNativeWindow(),
                                     windowWin->accelerators, message);

                    if (message.message == WM_QUIT)
                    {
                        exit();
                        break;
                    }
                }
            }
            else
            {
                MSG message;
                const BOOL ret = GetMessage(&message, nullptr, 0, 0);
                if (ret == -1)
                    throw std::system_error(GetLastError(), std::system_category(), "Failed to get message");

                if (ret == 0)
                {
                    exit();
                    break;
                }
                else
                {
                    translateMessage(windowWin->getNativeWindow(),
                                     windowWin->accelerators, message);
                }
            }

            inputWin->update();
        }

        exit();
    }

    void Engine::runOnMainThread(const std::function<void()>& func)
    {
        auto windowWin = static_cast<NativeWindow*>(window->getNativeWindow());

        std::unique_lock lock(executeMutex);
        executeQueue.push(func);
        lock.unlock();

        if (!PostMessage(windowWin->getNativeWindow(), WM_USER, 0, 0))
            throw std::system_error(GetLastError(), std::system_category(), "Failed to post message");

    }

    void Engine::executeAll()
    {
        std::function<void()> func;

        for (;;)
        {
            std::unique_lock lock(executeMutex);

            if (executeQueue.empty())
                break;

            func = std::move(executeQueue.front());
            executeQueue.pop();
            lock.unlock();

            if (func) func();
        }
    }

    void Engine::openUrl(const std::string& url)
    {
        const int buferSize = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
        if (buferSize == 0)
            throw std::system_error(GetLastError(), std::system_category(), "Failed to convert UTF-8 to wide char");

        auto buffer = std::make_unique<WCHAR[]>(buferSize);
        if (MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, buffer.get(), buferSize) == 0)
            throw std::system_error(GetLastError(), std::system_category(), "Failed to convert UTF-8 to wide char");

        // Result of the ShellExecuteW can be cast only to an int (https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
        const auto result = ShellExecuteW(nullptr, L"open", buffer.get(), nullptr, nullptr, SW_SHOWNORMAL);
        int status;
        std::memcpy(&status, &result, sizeof(status));
        if (status <= 32)
            throw std::system_error(status, shellExecuteErrorCategory, "Failed to execute open");
    }
}
<|endoftext|>


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

#ifndef METHCLA_PLUGIN_HPP_INCLUDED
#define METHCLA_PLUGIN_HPP_INCLUDED

#include <methcla/log.hpp>
#include <methcla/plugin.h>
#include <oscpp/server.hpp>

#include <functional>
#include <cstring>

// NOTE: This API is unstable and subject to change!

namespace Methcla { namespace Plugin {

    template <class Synth> class World
    {
        const Methcla_World* m_context;

    public:
        World(const Methcla_World* context)
            : m_context(context)
        { }

        double sampleRate() const
        {
            return methcla_world_samplerate(m_context);
        }

        size_t blockSize() const
        {
            return methcla_world_block_size(m_context);
        }

        Methcla_Time currentTime() const
        {
            return methcla_world_current_time(m_context);
        }

        void* alloc(size_t size) const
        {
            return methcla_world_alloc(m_context, size);
        }

        void* allocAligned(size_t alignment, size_t size) const
        {
            return methcla_world_alloc_aligned(m_context, alignment, size);
        }

        void free(void* ptr)
        {
            methcla_world_free(m_context, ptr);
        }

        void performCommand(Methcla_HostPerformFunction perform, void* data)
        {
            methcla_world_perform_command(m_context, perform, data);
        }

        LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo)
        {
            using namespace std::placeholders;
            return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel);
        }

        void synthRetain(Synth* synth) const
        {
            methcla_world_synth_retain(m_context, synth);
        }

        void synthRelease(Synth* synth) const
        {
            methcla_world_synth_release(m_context, synth);
        }

        void synthDone(Synth* synth) const
        {
            methcla_world_synth_done(m_context, synth);
        }
    };

    class HostContext
    {
        const Methcla_Host* m_context;

    public:
        HostContext(const Methcla_Host* context)
            : m_context(context)
        {}

        LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo)
        {
            using namespace std::placeholders;
            return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel);
        }
    };

    class NoPorts
    {
    public:
        enum Port { };

        static size_t numPorts() { return 0; }

        static Methcla_PortDescriptor descriptor(Port)
        {
            Methcla_PortDescriptor result;
            std::memset(&result, 0, sizeof(result));
            return result;
        }
    };

    class PortDescriptor
    {
    public:
        static Methcla_PortDescriptor make(Methcla_PortDirection direction, Methcla_PortType type, Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            Methcla_PortDescriptor pd;
            pd.direction = direction;
            pd.type = type;
            pd.flags = flags;
            return pd;
        }

        static Methcla_PortDescriptor audioInput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Input, kMethcla_AudioPort, flags);
        }

        static Methcla_PortDescriptor audioOutput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Output, kMethcla_AudioPort, flags);
        }

        static Methcla_PortDescriptor controlInput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Input, kMethcla_ControlPort, flags);
        }

        static Methcla_PortDescriptor controlOutput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Output, kMethcla_ControlPort, flags);
        }
    };

    template <class Options, class PortDescriptor> class StaticSynthOptions
    {
    public:
        typedef Options Type;

        static void
        configure( const void* tag_buffer
                 , size_t tag_buffer_size
                 , const void* arg_buffer
                 , size_t arg_buffer_size
                 , Methcla_SynthOptions* options )
        {
            OSCPP::Server::ArgStream args(
                OSCPP::ReadStream(tag_buffer, tag_buffer_size),
                OSCPP::ReadStream(arg_buffer, arg_buffer_size)
            );
            new (options) Type(args);
        }

        static bool
        port_descriptor( const Methcla_SynthOptions*
                       , Methcla_PortCount index
                       , Methcla_PortDescriptor* port )
        {
            if (index < PortDescriptor::numPorts())
            {
                *port = PortDescriptor::descriptor(static_cast<typename PortDescriptor::Port>(index));
                return true;
            }
            return false;
        }
    };

    namespace detail
    {
        template <class Synth, bool Condition>
        class IfSynthDefHasActivate
        {
        public:
            static inline void exec(const Methcla_World*, Synth*) { }
        };

        template <class Synth>
        class IfSynthDefHasActivate<Synth, true>
        {
        public:
            static inline void exec(const Methcla_World* context, Synth* synth)
                { synth->activate(World<Synth>(context)); }
        };

        template <class Synth, bool Condition>
        class IfSynthDefHasCleanup
        {
        public:
            static inline void exec(const Methcla_World*, Synth*) { }
        };

        template <class Synth>
        class IfSynthDefHasCleanup<Synth, true>
        {
        public:
            static inline void exec(const Methcla_World* context, Synth* synth)
                { synth->cleanup(World<Synth>(context)); }
        };
    } // namespace detail

    enum SynthDefFlags
    {
        kSynthDefDefaultFlags = 0x00,
        kSynthDefHasActivate  = 0x01,
        kSynthDefHasCleanup   = 0x02
    };

    template <class Synth, class Options, class PortDescriptor, SynthDefFlags Flags=kSynthDefDefaultFlags> class SynthDef
    {
        static void
        construct( const Methcla_World* context
                 , const Methcla_SynthDef* synthDef
                 , const Methcla_SynthOptions* options
                 , Methcla_Synth* synth )
        {
            assert(context != nullptr);
            assert(options != nullptr);
            new (synth) Synth(World<Synth>(context), synthDef, *static_cast<const typename Options::Type*>(options));
        }

        static void
        connect( Methcla_Synth* synth
               , Methcla_PortCount port
               , void* data)
        {
            static_cast<Synth*>(synth)->connect(static_cast<typename PortDescriptor::Port>(port), data);
        }

        static void
        activate(const Methcla_World* context, Methcla_Synth* synth)
        {
            detail::IfSynthDefHasActivate<
                Synth,
                (Flags & kSynthDefHasActivate) == kSynthDefHasActivate
            >::exec(context, static_cast<Synth*>(synth));
        }

        static void
        process(const Methcla_World* context, Methcla_Synth* synth, size_t numFrames)
        {
            static_cast<Synth*>(synth)->process(World<Synth>(context), numFrames);
        }

        static void
        destroy(const Methcla_World* context, Methcla_Synth* synth)
        {
            // Call cleanup method
            detail::IfSynthDefHasActivate<
                Synth,
                (Flags & kSynthDefHasCleanup) == kSynthDefHasCleanup
            >::exec(context, static_cast<Synth*>(synth));
            // Call destructor
            static_cast<Synth*>(synth)->~Synth();
        }

    public:
        void operator()(const Methcla_Host* host, const char* uri)
        {
            static const Methcla_SynthDef kSynthDef =
            {
                uri,
                sizeof(Synth),
                sizeof(typename Options::Type),
                Options::configure,
                Options::port_descriptor,
                construct,
                connect,
                activate,
                process,
                destroy
            };
            methcla_host_register_synthdef(host, &kSynthDef);
        }
    };

    template <class Synth, class Options, class Ports, SynthDefFlags Flags=kSynthDefDefaultFlags>
        using StaticSynthDef
        = SynthDef<Synth, StaticSynthOptions<Options,Ports>, Ports, Flags>;
} }

#endif // METHCLA_PLUGIN_HPP_INCLUDED
<commit_msg>Remove deprecated plugin interface methods<commit_after>// Copyright 2013 Samplecount S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef METHCLA_PLUGIN_HPP_INCLUDED
#define METHCLA_PLUGIN_HPP_INCLUDED

#include <methcla/log.hpp>
#include <methcla/plugin.h>
#include <oscpp/server.hpp>

#include <functional>
#include <cstring>

// NOTE: This API is unstable and subject to change!

namespace Methcla { namespace Plugin {

    template <class Synth> class World
    {
        const Methcla_World* m_context;

    public:
        World(const Methcla_World* context)
            : m_context(context)
        { }

        double sampleRate() const
        {
            return methcla_world_samplerate(m_context);
        }

        size_t blockSize() const
        {
            return methcla_world_block_size(m_context);
        }

        Methcla_Time currentTime() const
        {
            return methcla_world_current_time(m_context);
        }

        void* alloc(size_t size) const
        {
            return methcla_world_alloc(m_context, size);
        }

        void* allocAligned(size_t alignment, size_t size) const
        {
            return methcla_world_alloc_aligned(m_context, alignment, size);
        }

        void free(void* ptr)
        {
            methcla_world_free(m_context, ptr);
        }

        void performCommand(Methcla_HostPerformFunction perform, void* data)
        {
            methcla_world_perform_command(m_context, perform, data);
        }

        LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo)
        {
            using namespace std::placeholders;
            return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel);
        }

        void synthDone(Synth* synth) const
        {
            methcla_world_synth_done(m_context, synth);
        }
    };

    class HostContext
    {
        const Methcla_Host* m_context;

    public:
        HostContext(const Methcla_Host* context)
            : m_context(context)
        {}

        LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo)
        {
            using namespace std::placeholders;
            return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel);
        }
    };

    class NoPorts
    {
    public:
        enum Port { };

        static size_t numPorts() { return 0; }

        static Methcla_PortDescriptor descriptor(Port)
        {
            Methcla_PortDescriptor result;
            std::memset(&result, 0, sizeof(result));
            return result;
        }
    };

    class PortDescriptor
    {
    public:
        static Methcla_PortDescriptor make(Methcla_PortDirection direction, Methcla_PortType type, Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            Methcla_PortDescriptor pd;
            pd.direction = direction;
            pd.type = type;
            pd.flags = flags;
            return pd;
        }

        static Methcla_PortDescriptor audioInput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Input, kMethcla_AudioPort, flags);
        }

        static Methcla_PortDescriptor audioOutput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Output, kMethcla_AudioPort, flags);
        }

        static Methcla_PortDescriptor controlInput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Input, kMethcla_ControlPort, flags);
        }

        static Methcla_PortDescriptor controlOutput(Methcla_PortFlags flags=kMethcla_PortFlags)
        {
            return make(kMethcla_Output, kMethcla_ControlPort, flags);
        }
    };

    template <class Options, class PortDescriptor> class StaticSynthOptions
    {
    public:
        typedef Options Type;

        static void
        configure( const void* tag_buffer
                 , size_t tag_buffer_size
                 , const void* arg_buffer
                 , size_t arg_buffer_size
                 , Methcla_SynthOptions* options )
        {
            OSCPP::Server::ArgStream args(
                OSCPP::ReadStream(tag_buffer, tag_buffer_size),
                OSCPP::ReadStream(arg_buffer, arg_buffer_size)
            );
            new (options) Type(args);
        }

        static bool
        port_descriptor( const Methcla_SynthOptions*
                       , Methcla_PortCount index
                       , Methcla_PortDescriptor* port )
        {
            if (index < PortDescriptor::numPorts())
            {
                *port = PortDescriptor::descriptor(static_cast<typename PortDescriptor::Port>(index));
                return true;
            }
            return false;
        }
    };

    namespace detail
    {
        template <class Synth, bool Condition>
        class IfSynthDefHasActivate
        {
        public:
            static inline void exec(const Methcla_World*, Synth*) { }
        };

        template <class Synth>
        class IfSynthDefHasActivate<Synth, true>
        {
        public:
            static inline void exec(const Methcla_World* context, Synth* synth)
                { synth->activate(World<Synth>(context)); }
        };

        template <class Synth, bool Condition>
        class IfSynthDefHasCleanup
        {
        public:
            static inline void exec(const Methcla_World*, Synth*) { }
        };

        template <class Synth>
        class IfSynthDefHasCleanup<Synth, true>
        {
        public:
            static inline void exec(const Methcla_World* context, Synth* synth)
                { synth->cleanup(World<Synth>(context)); }
        };
    } // namespace detail

    enum SynthDefFlags
    {
        kSynthDefDefaultFlags = 0x00,
        kSynthDefHasActivate  = 0x01,
        kSynthDefHasCleanup   = 0x02
    };

    template <class Synth, class Options, class PortDescriptor, SynthDefFlags Flags=kSynthDefDefaultFlags> class SynthDef
    {
        static void
        construct( const Methcla_World* context
                 , const Methcla_SynthDef* synthDef
                 , const Methcla_SynthOptions* options
                 , Methcla_Synth* synth )
        {
            assert(context != nullptr);
            assert(options != nullptr);
            new (synth) Synth(World<Synth>(context), synthDef, *static_cast<const typename Options::Type*>(options));
        }

        static void
        connect( Methcla_Synth* synth
               , Methcla_PortCount port
               , void* data)
        {
            static_cast<Synth*>(synth)->connect(static_cast<typename PortDescriptor::Port>(port), data);
        }

        static void
        activate(const Methcla_World* context, Methcla_Synth* synth)
        {
            detail::IfSynthDefHasActivate<
                Synth,
                (Flags & kSynthDefHasActivate) == kSynthDefHasActivate
            >::exec(context, static_cast<Synth*>(synth));
        }

        static void
        process(const Methcla_World* context, Methcla_Synth* synth, size_t numFrames)
        {
            static_cast<Synth*>(synth)->process(World<Synth>(context), numFrames);
        }

        static void
        destroy(const Methcla_World* context, Methcla_Synth* synth)
        {
            // Call cleanup method
            detail::IfSynthDefHasActivate<
                Synth,
                (Flags & kSynthDefHasCleanup) == kSynthDefHasCleanup
            >::exec(context, static_cast<Synth*>(synth));
            // Call destructor
            static_cast<Synth*>(synth)->~Synth();
        }

    public:
        void operator()(const Methcla_Host* host, const char* uri)
        {
            static const Methcla_SynthDef kSynthDef =
            {
                uri,
                sizeof(Synth),
                sizeof(typename Options::Type),
                Options::configure,
                Options::port_descriptor,
                construct,
                connect,
                activate,
                process,
                destroy
            };
            methcla_host_register_synthdef(host, &kSynthDef);
        }
    };

    template <class Synth, class Options, class Ports, SynthDefFlags Flags=kSynthDefDefaultFlags>
        using StaticSynthDef
        = SynthDef<Synth, StaticSynthOptions<Options,Ports>, Ports, Flags>;
} }

#endif // METHCLA_PLUGIN_HPP_INCLUDED
<|endoftext|>