text:
/* ----------------------------------------------------------------------- *//**
 *
 * @file boost.hpp
 *
 *//* ----------------------------------------------------------------------- */

#define LIST_CONTINUOUS_PROB_DISTR \
    MADLIB_ITEM(beta) \
    MADLIB_ITEM(cauchy) \
    MADLIB_ITEM(chi_squared) \
    MADLIB_ITEM(fisher_f) \
    MADLIB_ITEM(exponential) \
    MADLIB_ITEM(extreme_value) \
    MADLIB_ITEM(gamma) \
    MADLIB_ITEM(inverse_chi_squared) \
    MADLIB_ITEM(inverse_gamma) \
    MADLIB_ITEM(laplace) \
    MADLIB_ITEM(logistic) \
    MADLIB_ITEM(lognormal) \
    MADLIB_ITEM(non_central_beta) \
    MADLIB_ITEM(non_central_chi_squared) \
    MADLIB_ITEM(non_central_f) \
    MADLIB_ITEM(non_central_t) \
    MADLIB_ITEM(normal) \
    MADLIB_ITEM(pareto) \
    MADLIB_ITEM(rayleigh) \
    MADLIB_ITEM(triangular) \
    MADLIB_ITEM(uniform) \
    MADLIB_ITEM(weibull)
// FIXME: MADLIB-513: Pending Boost bug 6934, we currently do not support the
// inverse Gaussian distribution. https://svn.boost.org/trac/boost/ticket/6934
//    MADLIB_ITEM(inverse_gaussian)
// For Student's t distribution, see student.hpp

#define LIST_DISCRETE_PROB_DISTR \
    MADLIB_ITEM(bernoulli) \
    MADLIB_ITEM(binomial) \
    MADLIB_ITEM(geometric) \
    MADLIB_ITEM(hypergeometric) \
    MADLIB_ITEM(negative_binomial) \
    MADLIB_ITEM(poisson)

#define MADLIB_ITEM(dist) \
    DECLARE_UDF(prob, dist ## _cdf) \
    DECLARE_UDF(prob, dist ## _pdf) \
    DECLARE_UDF(prob, dist ## _quantile)

LIST_CONTINUOUS_PROB_DISTR

#undef MADLIB_ITEM

#define MADLIB_ITEM(dist) \
    DECLARE_UDF(prob, dist ## _cdf) \
    DECLARE_UDF(prob, dist ## _pmf) \
    DECLARE_UDF(prob, dist ## _quantile)

LIST_DISCRETE_PROB_DISTR

#undef MADLIB_ITEM

#ifndef MADLIB_MODULES_PROB_BOOST_HPP
#define MADLIB_MODULES_PROB_BOOST_HPP

#include <boost/math/distributions.hpp>

namespace madlib {

namespace modules {

namespace prob {

namespace {
// No need to make this visable beyond this translation unit.

enum ProbFnOverride {
    kResultIsReady = 0,
    kLetBoostCalculate,
    kLetBoostCalculateUsingValue
};

/**
 * @brief Via (partial) specialization, this class offers a way to override
 *     boost's domain checks
 *
 * Some boost functions have domain checks we would like to override. E.g.,
 * boost's CDF and PDF for the Fisher F-distribution raise a domain error if the
 * input argument is <0 or infinity. By using madlib::modules::prob::cdf() (and
 * pdf/quantile, respectively), these domain checks can be overridden.
 * In MADlib, we always prefer returning correct values as opposed to raising
 * errors. E.g., for the CDF, it is the correct mathematical behavior to simply
 * return 0 if the input argument for the Fisher F-distribution is < 0.
 *
 * The following functions return \c true if boost's implementation should be
 * called, and they return \c false if the function result has alrady been
 * stored in \c outResult and boost's implementation should not be called any
 * more.
 *
 * Note that C++03 does not support partial specialization of functions. We
 * therefore must partially specialize the whole \c DomainCheck class.
 */
template <class Distribution>
struct DomainCheck {
    typedef typename Distribution::value_type RealType;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution&, const RealType&, RealType&) {
        return kLetBoostCalculate;
    }

    static ProbFnOverride pdf(const Distribution&, const RealType&, RealType&) {
        return kLetBoostCalculate;
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution&, const RealType&,
        RealType&) {

        return kLetBoostCalculate;
    }
};

/**
 * @brief Domain-check overrides for distribution functions with support in
 *     \f$ \mathbb R \f$
 *
 * We override the domain check by treating -infinity and infinity as part of
 * the domain. Some, but not all, boost functions would raise a domain_error
 * instead. See:
 * http://www.boost.org/doc/libs/1_49_0/libs/math/doc/sf_and_dist/html/math_toolkit/backgrounders/implementation.html#math_toolkit.backgrounders.implementation.handling_of_floating_point_infinity
 */
template <class Distribution>
struct RealDomainCheck {
    typedef typename Distribution::value_type RealType;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution&, const RealType& inX,
        RealType& outResult) {

        if (boost::math::isinf(inX)) {
            if (inX < 0)
                outResult = Complement ? 1 : 0;
            else
                outResult = Complement ? 0 : 1;
            return kResultIsReady;
        }
        return kLetBoostCalculate;
    }

    static ProbFnOverride pdf(const Distribution&, const RealType& inX,
        RealType& outResult) {

        if (boost::math::isinf(inX)) {
            outResult = 0;
            return kResultIsReady;
        }
        return kLetBoostCalculate;
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution&, const RealType&, RealType&) {
        return kLetBoostCalculate;
    }
};

/**
 * @brief Domain-check overrides for distribution functions with support in
 *     \f$ [0, \infty) \f$
 */
template <class Distribution>
struct PositiveDomainCheck : public RealDomainCheck<Distribution> {
    typedef RealDomainCheck<Distribution> Base;
    typedef typename Base::RealType RealType;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0) {
            outResult = Complement ? 1 : 0;
            return kResultIsReady;
        }
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0) {
            outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Domain-check overrides for distribution functions with support in
 *     \f$ [0, 1] \f$
 */
template <class Distribution>
struct ZeroOneDomainCheck : public PositiveDomainCheck<Distribution> {
    typedef PositiveDomainCheck<Distribution> Base;
    typedef typename Base::RealType RealType;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution&, const RealType& inX,
        RealType& outResult) {

        if (inX < 0)
            outResult = Complement ? 1 : 0;
        else if (inX > 1)
            outResult = Complement ? 0 : 1;
        else
            return kLetBoostCalculate;

        return kResultIsReady;
    }

    static ProbFnOverride pdf(const Distribution&, const RealType& inX,
        RealType& outResult) {

        if (inX < 0 || inX > 1) {
            outResult = 0;
            return kResultIsReady;
        }
        return kLetBoostCalculate;
    }
};

/**
 * @brief Domain-check overrides for distribution functions with support in
 *     \f$ \mathbb Z \f$
 */
template <class Distribution>
struct IntegerDomainCheck : public RealDomainCheck<Distribution> {
    typedef RealDomainCheck<Distribution> Base;
    typedef typename Base::RealType RealType;

    static ProbFnOverride internalMakeIntegral(ProbFnOverride inAction,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "IntegerDomainCheck::internalMakeIntegral(...)";

        if (inAction != kResultIsReady) {
            if (std::isnan(inX)) {
                outResult = boost::math::policies::raise_domain_error<RealType>(
                    function,
                    "Random variate must be integral but was: %1%.",
                    inX,
                    typename Distribution::policy_type());
                inAction = kResultIsReady;
            } else {
                outResult = std::floor(inX);
                inAction = kLetBoostCalculateUsingValue;
            }
        }
        return inAction;
    }

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        return internalMakeIntegral(
            Base::template cdf<Complement>(inDist, inX, outResult), inX,
                outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        return internalMakeIntegral(
            Base::pdf(inDist, inX, outResult), inX, outResult);
    }
};

/**
 * @brief Domain-check overrides for distribution functions with support in
 *     \f$ \mathbb N_0 \f$
 */
template <class Distribution>
struct NonNegativeIntegerDomainCheck : public IntegerDomainCheck<Distribution> {
    typedef IntegerDomainCheck<Distribution> Base;
    typedef typename Base::RealType RealType;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0) {
            outResult = Complement ? 1 : 0;
            return kResultIsReady;
        }
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0) {
            outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Boost only accepts 0 or 1 for random variate
 *
 * Due to boost bug 6937, we also need to override the domain check for quantile
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::bernoulli_distribution<RealType, Policy> >
  : public IntegerDomainCheck<
        boost::math::bernoulli_distribution<RealType, Policy>
    > {
    typedef boost::math::bernoulli_distribution<RealType, Policy> Distribution;
    typedef IntegerDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0)
            outResult = Complement ? 1 : 0;
        else if (inX > 1)
            outResult = Complement ? 0 : 1;
        else
            return Base::template cdf<Complement>(inDist, inX, outResult);

        return kResultIsReady;
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inX < 0 || inX > 1) {
            outResult = 0;
            return kResultIsReady;
        } else
            return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<bernoulli_distribution<%1%> >::quantile(...)";

        if (!boost::math::detail::check_probability(function,
            inDist.success_fraction(), &outResult, Policy())) {
            return kResultIsReady;
        } else if (inP == 1 || inP == 0){
            outResult = inP;
            return kResultIsReady;
        } else {
            return Base::template quantile<Complement>(inDist, inP, outResult);
        }
    }
};

/**
 * @brief Boost only accepts a limited range for random variates
 *
 * Due to boost bug 6937, we also need to override the domain check for quantile
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::binomial_distribution<RealType, Policy> >
  : public IntegerDomainCheck<
        boost::math::binomial_distribution<RealType, Policy>
    > {
    typedef boost::math::binomial_distribution<RealType, Policy> Distribution;
    typedef IntegerDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<binomial_distribution<%1%> >::cdf(...)";

        if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
            inDist.success_fraction(), &outResult, Policy())) {
            return kResultIsReady;
        } else if (inX < 0) {
            outResult = Complement ? 1 : 0;
            return kResultIsReady;
        } else if (inX > inDist.trials()) {
            outResult = Complement ? 0 : 1;
            return kResultIsReady;
        }
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<binomial_distribution<%1%> >::pdf(...)";

        if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
            inDist.success_fraction(), &outResult, Policy())) {
            return kResultIsReady;
        } else if (inX < 0 || inX > inDist.trials()) {
            outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<binomial_distribution<%1%> >::quantile(...)";

        if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
                inDist.success_fraction(), &outResult, Policy())
            || !boost::math::detail::check_probability(function, inP,
                &outResult, Policy())) {
            return kResultIsReady;
        } else if (inP == 1) {
            outResult = inDist.trials();
            return kResultIsReady;
        } else if (inP == 0) {
            outResult = 0;
            return kResultIsReady;
        } else if (inDist.success_fraction() == 1) {
            outResult = inDist.trials();
            return kResultIsReady;
        } else if (inDist.success_fraction() == 0) {
            outResult = 0;
            return kResultIsReady;
        }
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to override the domain checks
 *
 * FIXME: No boost bug filed so far
 * Boost does not catch the case where lambda is non-finite.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::exponential_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::exponential_distribution<RealType, Policy>
    > {
    typedef boost::math::exponential_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static bool check_dist(const char* function, RealType lambda,
        RealType* result, const Policy& pol) {

        if (!boost::math::isfinite(lambda)) {
            *result = boost::math::policies::raise_domain_error<RealType>(
                function,
                "The scale parameter \"lambda\" must be finite, but was: %1%.",
                lambda, pol);
            return false;
        }
        return true;
    }

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<exponential_distribution<%1%> >::cdf(...)";

        if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
            return kResultIsReady;
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<exponential_distribution<%1%> >::pdf(...)";

        if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
            return kResultIsReady;
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<exponential_distribution<%1%> >::quantile(...)";

        if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
            return kResultIsReady;
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to override the domain check for
 *     quantile
 *
 * FIXME: No boost bug filed so far
 * Boost does not catch the case where the location or scale parameters are
 * non-finite.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::extreme_value_distribution<RealType, Policy> >
  : public RealDomainCheck<
        boost::math::extreme_value_distribution<RealType, Policy>
    > {
    typedef boost::math::extreme_value_distribution<RealType, Policy> Distribution;
    typedef RealDomainCheck<Distribution> Base;

    static bool check_dist(const char* function, RealType location,
        RealType scale, RealType* result, const Policy& pol) {

        return
            boost::math::detail::check_location(function, location, result, pol)
            && boost::math::detail::check_scale(function, scale, result, pol);
    }

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<extreme_value_distribution<%1%> >::cdf(...)";

        if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
            Policy()))
            return kResultIsReady;
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<extreme_value_distribution<%1%> >::pdf(...)";

        if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
            Policy()))
            return kResultIsReady;
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<extreme_value_distribution<%1%> >::quantile(...)";

        if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
            Policy()))
            return kResultIsReady;
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug 6937, we need to override the domain check for
 *     quantile
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::fisher_f_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::fisher_f_distribution<RealType, Policy>
    > {
    typedef boost::math::fisher_f_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<fisher_f_distribution<%1%> >::quantile(...)";

        if (!boost::math::detail::check_df(function,
            inDist.degrees_of_freedom1(), &outResult, Policy())
            || !boost::math::detail::check_df(function,
                inDist.degrees_of_freedom2(), &outResult, Policy()))
            return kResultIsReady;

        if (std::isnan(inP)) {
            outResult = boost::math::policies::raise_domain_error<RealType>(
                function,
                "Probability argument is %1%, but must be >= 0 and <= 1!", inP,
                Policy());
            return kResultIsReady;
        }
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXXX, we override the domain check for gamma: pdf
 *
 * For the gamma distribution, boost's pdf always returns 0 for x = 0. That is
 * wrong.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::gamma_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::gamma_distribution<RealType, Policy>
    > {
    typedef boost::math::gamma_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<gamma_distribution<%1%> >::pdf(...)";
        RealType shape = inDist.shape();
        RealType scale = inDist.scale();
        if (!boost::math::detail::check_gamma(function, scale, shape,
            &outResult, Policy()))
            return kResultIsReady;

        if (inX == 0) {
            if (shape == 1)
                outResult = 1. / scale;
            else if (shape < 1)
                outResult
                    = boost::math::policies::raise_overflow_error<RealType>(
                        function, 0, Policy());
            else
                return kLetBoostCalculate;

            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Due to boost bug 6937, we need to override the domain check for
 *     quantile
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::geometric_distribution<RealType, Policy> >
  : public NonNegativeIntegerDomainCheck<
        boost::math::geometric_distribution<RealType, Policy>
    > {
    typedef boost::math::geometric_distribution<RealType, Policy> Distribution;
    typedef NonNegativeIntegerDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        if (inDist.success_fraction() == 1 && !std::isnan(inX)) {
            if (inX < 0)
                outResult = Complement ? 1 : 0;
            else if (inX > 0)
                outResult = Complement ? 0 : 1;
            else /* if (inX == 0) */
                outResult = 1;
            return kResultIsReady;
        }
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<geometric_distribution<%1%> >::quantile(...)";

        if (!boost::math::detail::check_probability(function,
            inDist.success_fraction(), &outResult, Policy()))
            return kResultIsReady;

        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Boost only accepts a limited range for random variates
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::hypergeometric_distribution<RealType, Policy> >
  : public NonNegativeIntegerDomainCheck<
        boost::math::hypergeometric_distribution<RealType, Policy>
    > {
    typedef boost::math::hypergeometric_distribution<RealType, Policy> Distribution;
    typedef NonNegativeIntegerDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        // inDist's parameters are all unsigned, so we need to be careful in the
        // comparison!
        if (inX + inDist.total() < inDist.defective() + inDist.sample_count())
            outResult = Complement ? 1 : 0;
        else if (inX > inDist.sample_count() || inX > inDist.defective())
            outResult = Complement ? 0 : 1;
        else
            return Base::template cdf<Complement>(inDist, inX, outResult);

        return kResultIsReady;
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        // inDist's parameters are all unsigned, so we need to be careful in the
        // comparison!
        if (inX + inDist.total() < inDist.defective() + inDist.sample_count()
            || inX > inDist.sample_count() || inX > inDist.defective()) {
            outResult = 0;
            return kResultIsReady;
        } else
            return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Boost returns a small non-zero value for quantile(0) instead of 0
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::inverse_gamma_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::inverse_gamma_distribution<RealType, Policy>
    > {
    typedef boost::math::inverse_gamma_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<inverse_gamma_distribution<%1%> >::quantile(...)";

        if(!boost::math::detail::check_inverse_gamma(function, inDist.scale(),
            inDist.shape(), &outResult, Policy()))
            return kResultIsReady;
        else if (inP == 0) {
            outResult = 0;
            return kResultIsReady;
        }
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to override the domain check for
 *     quantile
 *
 * FIXME: No boost bug filed so far
 * Boost does not catch the case where location or scale are not finite.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::lognormal_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::lognormal_distribution<RealType, Policy>
    > {
    typedef boost::math::lognormal_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static bool check_dist(const char* function, RealType location,
        RealType scale, RealType* result, const Policy& pol) {

        if (!boost::math::detail::check_location(function, location, result,
                pol)
            || !boost::math::detail::check_scale(function, scale, result, pol))
            return false;
        return true;
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<lognormal_distribution<%1%> >::pdf(...)";

        if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
            Policy()))
            return kResultIsReady;
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<lognormal_distribution<%1%> >::quantile(...)";

        if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
            Policy()))
            return kResultIsReady;
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug 6937, we need to override the domain check for
 *     quantile.
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 *
 * Also, we want to raise an error if the success probability is 0, because the
 * distribution is not well-defined in that case.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::negative_binomial_distribution<RealType, Policy> >
  : public NonNegativeIntegerDomainCheck<
        boost::math::negative_binomial_distribution<RealType, Policy>
    > {
    typedef boost::math::negative_binomial_distribution<RealType, Policy> Distribution;
    typedef NonNegativeIntegerDomainCheck<Distribution> Base;

    static bool check_dist(const char* function, const RealType& r,
        const RealType& p, RealType* result, const Policy& pol) {

        if (!boost::math::negative_binomial_detail::check_dist(function, r, p,
                result, pol)) {
            return false;
        } else if (p == 0) {
            *result = boost::math::policies::raise_domain_error<RealType>(
                function,
                "Probability argument is %1%, but must be > 0 and <= 1!",
                p, pol);
            return false;
        }
        return true;
    }

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<negative_binomial_distribution<%1%> >::cdf(...)";

        if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
            &outResult, Policy()))
            return kResultIsReady;
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<negative_binomial_distribution<%1%> >::pdf(...)";

        if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
            &outResult, Policy()))
            return kResultIsReady;
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<negative_binomial_distribution<%1%> >::quantile(...)";

        if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
            &outResult, Policy())) {
            return kResultIsReady;
        } else if (inDist.success_fraction() == 1) {
            // distribution is single-point measure, i.e., same for complement
            outResult = 0;
            return kResultIsReady;
        }
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to check the corner cases for the pdf
 *
 * FIXME: No boost bug filed so far
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_beta_distribution<RealType, Policy> >
  : public ZeroOneDomainCheck<
        boost::math::non_central_beta_distribution<RealType, Policy>
    > {
    typedef boost::math::non_central_beta_distribution<RealType, Policy> Distribution;
    typedef ZeroOneDomainCheck<Distribution> Base;

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<non_central_beta_distribution<%1%> >::quantile(...)";

        if (inX == 0 || inX == 1) {
            if(!boost::math::beta_detail::check_alpha(function, inDist.alpha(),
                    &outResult, Policy())
                || !boost::math::beta_detail::check_beta(function,
                    inDist.beta(), &outResult, Policy())
                || !boost::math::detail::check_non_centrality(function,
                    inDist.non_centrality(), &outResult, Policy()))
                return kResultIsReady;

            if (inX == 0) {
                if (inDist.alpha() < 1)
                    outResult = boost::math::policies
                        ::raise_overflow_error<RealType>(function, 0, Policy());
                else if (inDist.alpha() == 1)
                    outResult = inDist.beta()
                              * std::exp(-inDist.non_centrality()/2.);
                else /* if (inDist.alpha() > 1) */
                    outResult = 0;
                return kResultIsReady;
            } else /* if (inX == 1) */ {
                if (inDist.beta() < 1)
                    outResult = boost::math::policies
                        ::raise_overflow_error<RealType>(function, 0, Policy());
                else if (inDist.beta() == 1)
                    outResult = inDist.alpha() + inDist.non_centrality()/2.;
                else /* if (inDist.beta() > 1) */
                    outResult = 0;
                return kResultIsReady;
            }
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to check the corner cases for the pdf
 *
 * FIXME: No boost bug filed so far
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_chi_squared_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::non_central_chi_squared_distribution<RealType, Policy>
    > {
    typedef boost::math::non_central_chi_squared_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<non_central_chi_squared_distribution<%1%> >::quantile(...)";

        if (inX == 0) {
            if(!boost::math::detail::check_df(function,
                    inDist.degrees_of_freedom(), &outResult, Policy())
                || !boost::math::detail::check_non_centrality(function,
                    inDist.non_centrality(), &outResult, Policy()))
                return kResultIsReady;

            if (inDist.degrees_of_freedom() < 2)
                outResult = boost::math::policies
                    ::raise_overflow_error<RealType>(function, 0, Policy());
            else if (inDist.degrees_of_freedom() == 2)
                // In this case, f(x) = exp(-lambda/2) * g(x), where g(x)
                // is the densitiy of a chi-squared distributed RV with 2
                // degrees of freedom, i.e., f(x) = exp(-lambda/2) / 2
                outResult = std::exp(-inDist.non_centrality()/2.) / 2.;
            else /* if (inDist.degrees_of_freedom() > 2) */
                outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to check the corner cases for the pdf
 *
 * FIXME: No boost bug filed so far
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_f_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::non_central_f_distribution<RealType, Policy>
    > {
    typedef boost::math::non_central_f_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<non_central_f_distribution<%1%> >::quantile(...)";

        if (inX == 0) {
            if(!boost::math::detail::check_df(function,
                    inDist.degrees_of_freedom1(), &outResult, Policy())
                || !boost::math::detail::check_df(function,
                    inDist.degrees_of_freedom2(), &outResult, Policy())
                || !boost::math::detail::check_non_centrality(function,
                    inDist.non_centrality(), &outResult, Policy()))
                return kResultIsReady;

            if (inDist.degrees_of_freedom1() < 2)
                outResult = boost::math::policies
                    ::raise_overflow_error<RealType>(function, 0, Policy());
            else if (inDist.degrees_of_freedom1() == 2)
                // In this case, f(x) = exp(-lambda/2)
                outResult = std::exp(-inDist.non_centrality()/2.);
            else /* if (inDist.degrees_of_freedom1() > 2) */
                outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }
};

/**
 * @brief Override the domain check for Pareto: quantile
 *
 * For the Pareto distribution, boost sometimes returns max_value instead
 * of infinity. We override that.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::pareto_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::pareto_distribution<RealType, Policy>
    > {
    typedef boost::math::pareto_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<pareto_distribution<%1%> >::cdf(...)";

        if (inX <= inDist.scale()) {
            if (boost::math::detail::check_pareto(function, inDist.scale(),
                inDist.shape(), &outResult, Policy()))
                outResult = 0;
            return kResultIsReady;
        }
        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<pareto_distribution<%1%> >::pdf(...)";

        if (inX < inDist.scale()) {
            if (boost::math::detail::check_pareto(function, inDist.scale(),
                inDist.shape(), &outResult, Policy()))
                outResult = 0;
            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<pareto_distribution<%1%> >::quantile(...)";

        if (!boost::math::detail::check_pareto(function, inDist.scale(),
            inDist.shape(), &outResult, Policy()))
            return kResultIsReady;
        else if (inP == 1) {
            outResult = boost::math::policies::raise_overflow_error<RealType>(
                function, 0, Policy());
            return kResultIsReady;
        }
        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug 6937, we need to override the domain check for
 *     quantile
 *
 * https://svn.boost.org/trac/boost/ticket/6937
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::poisson_distribution<RealType, Policy> >
  : public NonNegativeIntegerDomainCheck<
        boost::math::poisson_distribution<RealType, Policy>
    > {
    typedef boost::math::poisson_distribution<RealType, Policy> Distribution;
    typedef NonNegativeIntegerDomainCheck<Distribution> Base;

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<poisson_distribution<%1%> >::quantile(...)";

        if (!boost::math::poisson_detail::check_dist(function,
            inDist.mean(), &outResult, Policy()))
            return kResultIsReady;
        else if (inP == 1) {
            outResult = boost::math::policies::raise_overflow_error<RealType>(
                function, 0, Policy());
            return kResultIsReady;
        }

        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bug XXX, we need to override the domain check for
 *     quantile
 *
 * FIXME: No boost bug filed so far
 * Boost does not catch the case where sigma is NaN.
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::rayleigh_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::rayleigh_distribution<RealType, Policy>
    > {
    typedef boost::math::rayleigh_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    static bool check_sigma_finite(const char* function, RealType sigma,
        RealType* result, const Policy& pol) {

        if (!boost::math::isfinite(sigma)) {
            *result = boost::math::policies::raise_domain_error<RealType>(
                function,
                "The scale parameter \"sigma\" must be finite, but was: %1%.",
                sigma, pol);
            return false;
        }
        return true;
    }

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<rayleigh_distribution<%1%> >::cdf(...)";

        if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
            return kResultIsReady;

        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist,
        const RealType& inX, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<rayleigh_distribution<%1%> >::pdf(...)";

        if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
            return kResultIsReady;

        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<rayleigh_distribution<%1%> >::quantile(...)";

        if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
            return kResultIsReady;

        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

/**
 * @brief Due to boost bugs 6938 and 6939, we need to override the domain checks
 *
 * https://svn.boost.org/trac/boost/ticket/6938
 * https://svn.boost.org/trac/boost/ticket/6939
 */
template <class RealType, class Policy>
struct DomainCheck<boost::math::weibull_distribution<RealType, Policy> >
  : public PositiveDomainCheck<
        boost::math::weibull_distribution<RealType, Policy>
    > {
    typedef boost::math::weibull_distribution<RealType, Policy> Distribution;
    typedef PositiveDomainCheck<Distribution> Base;

    // BEGIN Copied from boost/math/distributions/weibull.hpp (v1.49), but replaced
    // "<" check by "<=".
    static bool check_weibull_shape(
        const char* function,
        RealType shape,
        RealType* result, const Policy& pol) {

        if((shape <= 0) || !(boost::math::isfinite)(shape)) {
            *result = boost::math::policies::raise_domain_error<RealType>(
                function,
                "Shape parameter is %1%, but must be > 0 !", shape, pol);
            return false;
        }
        return true;
    }
    // END Copied from boost/math/distributions/weibull.hpp (v1.49)

    template <bool Complement>
    static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<weibull_distribution<%1%> >::cdf(...)";
        RealType shape = inDist.shape();
        if (!check_weibull_shape(function, shape, &outResult, Policy()))
            return kResultIsReady;

        return Base::template cdf<Complement>(inDist, inX, outResult);
    }

    static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
        RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<weibull_distribution<%1%> >::pdf(...)";
        RealType shape = inDist.shape();
        if (!check_weibull_shape(function, shape, &outResult, Policy()))
            return kResultIsReady;

        if (inX == 0) {
            if (shape == 1)
                outResult = 1;
            else if (shape < 1)
                outResult = boost::math::policies::raise_overflow_error<RealType>(
                    function, 0, Policy());
            else
                return kLetBoostCalculate;

            return kResultIsReady;
        }
        return Base::pdf(inDist, inX, outResult);
    }

    template <bool Complement>
    static ProbFnOverride quantile(const Distribution& inDist,
        const RealType& inP, RealType& outResult) {

        static const char* function = "madlib::modules::prob::<unnamed>::"
            "DomainCheck<weibull_distribution<%1%> >::quantile(...)";
        RealType shape = inDist.shape();
        if (!check_weibull_shape(function, shape, &outResult, Policy()))
            return kResultIsReady;

        return Base::template quantile<Complement>(inDist, inP, outResult);
    }
};

#define DOMAIN_CHECK_OVERRIDE(dist, check) \
    template <class RealType, class Policy> \
    struct DomainCheck<boost::math::dist ## _distribution<RealType, Policy> > \
      : public check<boost::math::dist ## _distribution<RealType, Policy> > { };

DOMAIN_CHECK_OVERRIDE(beta, ZeroOneDomainCheck)
DOMAIN_CHECK_OVERRIDE(chi_squared, PositiveDomainCheck)
DOMAIN_CHECK_OVERRIDE(laplace, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(non_central_t, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(triangular, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(uniform, RealDomainCheck)
// The following lines are currently commented out. Each of these is a boost
// deficiency unfortunately. See above.
// DOMAIN_CHECK_OVERRIDE(exponential, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(extreme_value, RealDomainCheck)
// DOMAIN_CHECK_OVERRIDE(fisher_f, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(gamma, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(geometric, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(lognormal, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(hypergeometric, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(inverse_gamma, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(negative_binomial, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_beta, ZeroOneDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_chi_squared, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_f, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(pareto, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(poisson, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(rayleigh, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(weibull, PositiveDomainCheck)

#undef DOMAIN_CHECK_OVERRIDE

} // anonymous namespace

#define DEFINE_BOOST_WRAPPER(_dist, _what, _domain_check_what) \
    template <class RealType, class Policy> \
    inline \
    RealType \
    _what(const boost::math::_dist ## _distribution<RealType, Policy>& dist, \
        const RealType& x) { \
        \
        typedef boost::math::_dist ## _distribution<RealType, Policy> Dist; \
        RealType result; \
        switch (DomainCheck<Dist>::_domain_check_what(dist, x, result)) { \
            case kResultIsReady: return result; \
            case kLetBoostCalculate: return boost::math::_what(dist, x); \
            case kLetBoostCalculateUsingValue: \
                return boost::math::_what(dist, result); \
            default: throw std::logic_error("Unexpected case detected in " \
                "domain-check override for a boost probability function."); \
        } \
    }

#define DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, _what) \
    template <class RealType, class Policy> \
    inline \
    RealType \
    _what(const boost::math::complemented2_type< \
        boost::math::_dist ## _distribution<RealType, Policy>, \
        RealType \
    >& c) { \
        typedef boost::math::_dist ## _distribution<RealType, Policy> Dist; \
        typedef boost::math::complemented2_type<Dist, RealType> Complement; \
        RealType result; \
        switch (DomainCheck<Dist>::template _what<true>(c.dist, c.param, result)) { \
            case kResultIsReady: return result; \
            case kLetBoostCalculate: return boost::math::_what(c); \
            case kLetBoostCalculateUsingValue: \
                return boost::math::_what(Complement(c.dist, result)); \
            default: throw std::logic_error("Unexpected case detected in " \
                "domain-check override for a boost probability function."); \
        } \
    }

#define DEFINE_BOOST_PROBABILITY_DISTR(_dist) \
    typedef boost::math::_dist ## _distribution< \
        double, boost_mathkit_policy> _dist; \
    \
    DEFINE_BOOST_WRAPPER(_dist, cdf, template cdf<false>) \
    DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, cdf) \
    DEFINE_BOOST_WRAPPER(_dist, pdf, pdf) \
    DEFINE_BOOST_WRAPPER(_dist, quantile, template quantile<false>) \
    DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, quantile)

#define MADLIB_ITEM(_dist) \
    DEFINE_BOOST_PROBABILITY_DISTR(_dist)

// Note that boost also uses the pdf() if actually a probability mass function
// is meant
LIST_CONTINUOUS_PROB_DISTR
LIST_DISCRETE_PROB_DISTR

#undef MADLIB_ITEM
#undef DEFINE_PROBABILITY_DISTR
#undef DEFINE_BOOST_WRAPPER
#undef LIST_CONTINUOUS_PROB_DISTR

} // namespace prob

} // namespace modules

} // namespace regress

#endif // defined(MADLIB_MODULES_PROB_BOOST_HPP)

ext: hpp


text: #include <boost/iostreams/skip.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
 * Copyright (c) 2016-2018 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include "pksav/pksav_call.hpp"

#include "c/cpp_to_c.hpp"
#include "c/error_internal.hpp"

#ifdef _MSC_VER
#    pragma warning(disable: 4273) // Inconsistent DLL linkage
#endif
extern "C" {
    const char* pkmn_strerror();
}

#include <pkmn-c/types.h>

#include <pksav.h>

#include <gtest/gtest.h>

#include <boost/assign.hpp>
#include <boost/thread/mutex.hpp>

#include <stdexcept>
#include <string>

class pkmn_test_exception: public std::exception {
    public:
        explicit pkmn_test_exception(const char* msg):
            msg_(msg) {}

        explicit pkmn_test_exception(const std::string& msg):
            msg_(msg) {}

        virtual ~pkmn_test_exception() throw() {}

        virtual const char* what() const throw() {
            return msg_.c_str();
        }

    protected:
        std::string msg_;
};

#define BUFFER_LEN 1024

/*
 * Converting C++ exceptions to C error codes
 */

enum pkmn_error throw_nothing() {
    PKMN_CPP_TO_C()
}

enum pkmn_error throw_unknown() {
    PKMN_CPP_TO_C(
        throw 5;
    )
}

// The "enum" tag must be removed for MSVC to compile.
template <typename exception_type>
pkmn_error throw_exception(
    const std::string& msg
) {
    PKMN_CPP_TO_C(
        throw exception_type(msg);
    )
}

enum pkmn_error throw_feature_not_in_game_error(
    const std::string& feature,
    const std::string& game
) {
    PKMN_CPP_TO_C(
        throw pkmn::feature_not_in_game_error(
                  feature,
                  game
              );
    )
}

enum pkmn_error throw_pksav_error(
    pksav_error pksav_error_code
) {
    PKMN_CPP_TO_C(
        PKSAV_CALL(pksav_error_code)
    )
}

enum pkmn_error throw_pkmn_unimplemented_error() {
    PKMN_CPP_TO_C(
        throw pkmn::unimplemented_error();
    )
}

TEST(cpp_to_c_test, exception_to_error_code_test) {
    enum pkmn_error error = PKMN_ERROR_NONE;

    error = throw_nothing();
    EXPECT_EQ(PKMN_ERROR_NONE, error);
    EXPECT_STREQ("None", pkmn_strerror());

    error = throw_exception<pkmn::feature_not_in_build_error>("Qt support");
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_BUILD_ERROR, error);
    EXPECT_STREQ("This feature is not part of this build of LibPKMN: Qt support", pkmn_strerror());

    error = throw_feature_not_in_game_error("Contests", "Generation I");
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_GAME_ERROR, error);
    EXPECT_STREQ("Contests not in Generation I", pkmn_strerror());

    error = throw_exception<pkmn::feature_not_in_game_error>("feature_not_in_game_error");
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_GAME_ERROR, error);
    EXPECT_STREQ("feature_not_in_game_error", pkmn_strerror());

    error = throw_pksav_error(PKSAV_ERROR_INVALID_SAVE);
    EXPECT_EQ(PKMN_ERROR_INTERNAL_ERROR, error);
    EXPECT_STREQ("Internal error: PKSav returned the following error: \"Invalid save file\"", pkmn_strerror());

    error = throw_pkmn_unimplemented_error();
    EXPECT_EQ(PKMN_ERROR_UNIMPLEMENTED_ERROR, error);
    EXPECT_STREQ("Currently unimplemented.", pkmn_strerror());

    error = throw_exception<std::invalid_argument>("invalid_argument");
    EXPECT_EQ(PKMN_ERROR_INVALID_ARGUMENT, error);
    EXPECT_STREQ("invalid_argument", pkmn_strerror());

    error = throw_exception<std::domain_error>("domain_error");
    EXPECT_EQ(PKMN_ERROR_DOMAIN_ERROR, error);
    EXPECT_STREQ("domain_error", pkmn_strerror());

    error = throw_exception<std::length_error>("length_error");
    EXPECT_EQ(PKMN_ERROR_LENGTH_ERROR, error);
    EXPECT_STREQ("length_error", pkmn_strerror());

    error = throw_exception<std::out_of_range>("out_of_range");
    EXPECT_EQ(PKMN_ERROR_OUT_OF_RANGE, error);
    EXPECT_STREQ("out_of_range", pkmn_strerror());

    error = throw_exception<std::logic_error>("logic_error");
    EXPECT_EQ(PKMN_ERROR_LOGIC_ERROR, error);
    EXPECT_STREQ("logic_error", pkmn_strerror());

    error = throw_exception<std::range_error>("range_error");
    EXPECT_EQ(PKMN_ERROR_RANGE_ERROR, error);
    EXPECT_STREQ("range_error", pkmn_strerror());

    error = throw_exception<std::overflow_error>("overflow_error");
    EXPECT_EQ(PKMN_ERROR_OVERFLOW_ERROR, error);
    EXPECT_STREQ("overflow_error", pkmn_strerror());

    error = throw_exception<std::underflow_error>("underflow_error");
    EXPECT_EQ(PKMN_ERROR_UNDERFLOW_ERROR, error);
    EXPECT_STREQ("underflow_error", pkmn_strerror());

    error = throw_exception<std::runtime_error>("runtime_error");
    EXPECT_EQ(PKMN_ERROR_RUNTIME_ERROR, error);
    EXPECT_STREQ("runtime_error", pkmn_strerror());

    error = throw_exception<pkmn_test_exception>("std_exception");
    EXPECT_EQ(PKMN_ERROR_STD_EXCEPTION, error);
    EXPECT_STREQ("std_exception", pkmn_strerror());

    error = throw_unknown();
    EXPECT_EQ(PKMN_ERROR_UNKNOWN_ERROR, error);
    EXPECT_STREQ("Unknown error", pkmn_strerror());
}

/*
 * Converting C++ exceptions to C error codes (with handle)
 */

typedef struct {
    boost::mutex error_mutex;
    std::string last_error;
} pkmn_test_handle_t;

enum pkmn_error throw_nothing_with_handle(
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,)
}

enum pkmn_error throw_unknown_with_handle(
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,
        throw 5;
    )
}

// The "enum" tag must be removed for MSVC to compile.
template <typename exception_type>
pkmn_error throw_exception_with_handle(
    const std::string& msg,
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,
        throw exception_type(msg);
    )
}

enum pkmn_error throw_feature_not_in_game_error_with_handle(
    const std::string& feature,
    const std::string& game,
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,
        throw pkmn::feature_not_in_game_error(
                  feature,
                  game
              );
    )
}

enum pkmn_error throw_pksav_error_with_handle(
    pksav_error pksav_error_code,
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,
        PKSAV_CALL(pksav_error_code)
    )
}

enum pkmn_error throw_pkmn_unimplemented_error_with_handle(
    pkmn_test_handle_t* handle
) {
    PKMN_CPP_TO_C_WITH_HANDLE(handle,
        throw pkmn::unimplemented_error();
    )
}

TEST(cpp_to_c_test, exception_to_error_code_with_handle_test) {
    enum pkmn_error error = PKMN_ERROR_NONE;
    pkmn_test_handle_t test_handle;

    error = throw_nothing_with_handle(&test_handle);
    EXPECT_EQ(PKMN_ERROR_NONE, error);
    EXPECT_EQ("None", test_handle.last_error);

    error = throw_exception_with_handle<pkmn::feature_not_in_build_error>(
        "Qt support", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_BUILD_ERROR, error);
    EXPECT_EQ("This feature is not part of this build of LibPKMN: Qt support", test_handle.last_error);
    EXPECT_STREQ("This feature is not part of this build of LibPKMN: Qt support", pkmn_strerror());

    error = throw_feature_not_in_game_error_with_handle(
        "Contests", "Generation I", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_GAME_ERROR, error);
    EXPECT_EQ("Contests not in Generation I", test_handle.last_error);
    EXPECT_STREQ("Contests not in Generation I", pkmn_strerror());

    error = throw_exception_with_handle<pkmn::feature_not_in_game_error>(
        "feature_not_in_game_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_FEATURE_NOT_IN_GAME_ERROR, error);
    EXPECT_EQ("feature_not_in_game_error", test_handle.last_error);
    EXPECT_STREQ("feature_not_in_game_error", pkmn_strerror());

    error = throw_pksav_error_with_handle(
        PKSAV_ERROR_INVALID_SAVE, &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_INTERNAL_ERROR, error);
    EXPECT_EQ("Internal error: PKSav returned the following error: \"Invalid save file\"", test_handle.last_error);
    EXPECT_STREQ("Internal error: PKSav returned the following error: \"Invalid save file\"", pkmn_strerror());

    error = throw_pkmn_unimplemented_error_with_handle(&test_handle);
    EXPECT_EQ(PKMN_ERROR_UNIMPLEMENTED_ERROR, error);
    EXPECT_EQ("Currently unimplemented.", test_handle.last_error);
    EXPECT_STREQ("Currently unimplemented.", pkmn_strerror());

    error = throw_exception_with_handle<std::invalid_argument>(
        "invalid_argument", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_INVALID_ARGUMENT, error);
    EXPECT_EQ("invalid_argument", test_handle.last_error);
    EXPECT_STREQ("invalid_argument", pkmn_strerror());

    error = throw_exception_with_handle<std::domain_error>(
        "domain_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_DOMAIN_ERROR, error);
    EXPECT_EQ("domain_error", test_handle.last_error);
    EXPECT_STREQ("domain_error", pkmn_strerror());

    error = throw_exception_with_handle<std::length_error>(
        "length_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_LENGTH_ERROR, error);
    EXPECT_EQ("length_error", test_handle.last_error);
    EXPECT_STREQ("length_error", pkmn_strerror());

    error = throw_exception_with_handle<std::out_of_range>(
        "out_of_range", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_OUT_OF_RANGE, error);
    EXPECT_EQ("out_of_range", test_handle.last_error);
    EXPECT_STREQ("out_of_range", pkmn_strerror());

    error = throw_exception_with_handle<std::logic_error>(
        "logic_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_LOGIC_ERROR, error);
    EXPECT_EQ("logic_error", test_handle.last_error);
    EXPECT_STREQ("logic_error", pkmn_strerror());

    error = throw_exception_with_handle<std::range_error>(
        "range_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_RANGE_ERROR, error);
    EXPECT_EQ("range_error", test_handle.last_error);
    EXPECT_STREQ("range_error", pkmn_strerror());

    error = throw_exception_with_handle<std::overflow_error>(
        "overflow_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_OVERFLOW_ERROR, error);
    EXPECT_EQ("overflow_error", test_handle.last_error);
    EXPECT_STREQ("overflow_error", pkmn_strerror());

    error = throw_exception_with_handle<std::underflow_error>(
        "underflow_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_UNDERFLOW_ERROR, error);
    EXPECT_EQ("underflow_error", test_handle.last_error);
    EXPECT_STREQ("underflow_error", pkmn_strerror());

    error = throw_exception_with_handle<std::runtime_error>(
        "runtime_error", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_RUNTIME_ERROR, error);
    EXPECT_EQ("runtime_error", test_handle.last_error);
    EXPECT_STREQ("runtime_error", pkmn_strerror());

    error = throw_exception_with_handle<pkmn_test_exception>(
        "std_exception", &test_handle
    );
    EXPECT_EQ(PKMN_ERROR_STD_EXCEPTION, error);
    EXPECT_EQ("std_exception", test_handle.last_error);
    EXPECT_STREQ("std_exception", pkmn_strerror());

    error = throw_unknown_with_handle(&test_handle);
    EXPECT_EQ(PKMN_ERROR_UNKNOWN_ERROR, error);
    EXPECT_EQ("Unknown error", test_handle.last_error);
    EXPECT_STREQ("Unknown error", pkmn_strerror());
}

/*
 * Converting C++ types to C types
 */

TEST(cpp_to_c_test, list_cpp_to_c_test)
{
    std::vector<int> int_vector = {1, 2, 3, 4, 5};
    std::vector<double> double_vector = {6.0, 7.0, 8.0};

    int int_list[BUFFER_LEN] = {0};
    double double_list[BUFFER_LEN] = {0.0};

    size_t output_size;

    // Int test

    pkmn::c::list_cpp_to_c(
        int_vector,
        int_list,
        int_vector.size() - 1, // Too short
        &output_size
    );
    EXPECT_EQ(int_vector.size(), output_size);
    for(size_t index = 0; index < int_vector.size(); ++index)
    {
        if(index == (int_vector.size() - 1))
        {
            EXPECT_EQ(0, int_list[index]);
        }
        else
        {
            EXPECT_EQ(int_vector[index], int_list[index]);
        }
    }

    std::memset(
        int_list,
        0,
        sizeof(int_list)
    );

    pkmn::c::list_cpp_to_c(
        int_vector,
        int_list,
        int_vector.size(),
        &output_size
    );
    EXPECT_EQ(int_vector.size(), output_size);
    for(size_t index = 0; index < int_vector.size(); ++index)
    {
        EXPECT_EQ(int_vector[index], int_list[index]);
    }

    // Double list

    pkmn::c::list_cpp_to_c(
        double_vector,
        double_list,
        double_vector.size() - 1, // Too short
        &output_size
    );
    EXPECT_EQ(double_vector.size(), output_size);
    for(size_t index = 0; index < double_vector.size(); ++index)
    {
        if(index == (double_vector.size() - 1))
        {
            EXPECT_DOUBLE_EQ(0.0, double_list[index]);
        }
        else
        {
            EXPECT_DOUBLE_EQ(double_vector[index], double_list[index]);
        }
    }

    std::memset(
        double_list,
        0,
        sizeof(double_list)
    );

    pkmn::c::list_cpp_to_c(
        double_vector,
        double_list,
        double_vector.size(),
        &output_size
    );
    EXPECT_EQ(double_vector.size(), output_size);
    for(size_t index = 0; index < double_vector.size(); ++index)
    {
        EXPECT_DOUBLE_EQ(double_vector[index], double_list[index]);
    }
}

enum class cpp_enum
{
    VALUE1 = 0,
    VALUE2,
    VALUE3
};

enum c_enum
{
    C_ENUM_VALUE1,
    C_ENUM_VALUE2,
    C_ENUM_VALUE3
};

struct c_test_enum_list
{
    c_enum* p_enums;
    size_t length;
};

TEST(cpp_to_c_test, enum_list_cpp_to_c_test)
{
    const std::vector<cpp_enum> cpp_enum_vector =
    {
        cpp_enum::VALUE2,
        cpp_enum::VALUE1,
        cpp_enum::VALUE3
    };
    struct c_test_enum_list c_enum_list =
    {
        nullptr,
        0
    };

    pkmn::c::enum_list_cpp_to_c<cpp_enum, enum c_enum, c_test_enum_list>(
        cpp_enum_vector,
        &c_enum_list
    );

    ASSERT_NE(nullptr, c_enum_list.p_enums);
    EXPECT_EQ(3ULL, c_enum_list.length);

    EXPECT_EQ(C_ENUM_VALUE2, c_enum_list.p_enums[0]);
    EXPECT_EQ(C_ENUM_VALUE1, c_enum_list.p_enums[1]);
    EXPECT_EQ(C_ENUM_VALUE3, c_enum_list.p_enums[2]);

    std::free(c_enum_list.p_enums);
    c_enum_list.length = 0;
}

struct c_test_enum_map
{
    int* p_values;
    size_t length;
};

TEST(cpp_to_c_test, enum_map_cpp_to_c)
{
    const std::map<cpp_enum, int> cpp_enum_map =
    {
        {cpp_enum::VALUE1, rand()},
        {cpp_enum::VALUE3, rand()}
    };
    struct c_test_enum_map c_enum_map =
    {
        nullptr,
        0ULL
    };

    pkmn::c::enum_map_cpp_to_c<cpp_enum, c_enum, c_test_enum_map, int, cpp_enum::VALUE3>(
        cpp_enum_map,
        &c_enum_map
    );

    ASSERT_NE(nullptr, c_enum_map.p_values);
    EXPECT_EQ(3ULL, c_enum_map.length);

    EXPECT_EQ(
        cpp_enum_map.at(cpp_enum::VALUE1),
        c_enum_map.p_values[C_ENUM_VALUE1]
    );
    EXPECT_EQ(
        cpp_enum_map.at(cpp_enum::VALUE3),
        c_enum_map.p_values[C_ENUM_VALUE3]
    );

    std::free(c_enum_map.p_values);
    c_enum_map.length = 0;
}

TEST(cpp_to_c_test, hidden_power_cpp_to_c_test)
{
    pkmn::calculations::hidden_power hidden_power_cpp(
                                         pkmn::e_type::NORMAL,
                                         90
                                     );

    struct pkmn_hidden_power hidden_power_c = { PKMN_TYPE_NONE, 0 };

    pkmn::c::hidden_power_cpp_to_c(
        hidden_power_cpp,
        &hidden_power_c
    );

    EXPECT_EQ(PKMN_TYPE_NORMAL, hidden_power_c.type);
    EXPECT_EQ(90, hidden_power_c.base_power);
}

TEST(cpp_to_c_test, natural_gift_cpp_to_c_test)
{
    pkmn::calculations::natural_gift natural_gift_cpp(
                                         pkmn::e_type::NORMAL,
                                         90
                                     );

    struct pkmn_natural_gift natural_gift_c = { PKMN_TYPE_NONE, 0 };

    pkmn::c::natural_gift_cpp_to_c(
        natural_gift_cpp,
        &natural_gift_c
    );

    EXPECT_EQ(PKMN_TYPE_NORMAL, natural_gift_c.type);
    EXPECT_EQ(90, natural_gift_c.base_power);
}

TEST(cpp_to_c_test, item_slot_cpp_to_c_test)
{
    pkmn::item_slot item_slot_cpp(pkmn::e_item::POTION, 50);
    struct pkmn_item_slot item_slot_c;

    pkmn::c::item_slot_cpp_to_c(
        item_slot_cpp,
        &item_slot_c
    );

    EXPECT_EQ(PKMN_ITEM_POTION, item_slot_c.item);
    EXPECT_EQ(50, item_slot_c.amount);
}

TEST(cpp_to_c_test, item_slots_cpp_to_c_test)
{
    pkmn::item_slots_t item_slots_cpp =
    {
        pkmn::item_slot(pkmn::e_item::POTION, 50),
        pkmn::item_slot(pkmn::e_item::BERRY, 28),
        pkmn::item_slot(pkmn::e_item::BERRY_POUCH, 1)
    };

    struct pkmn_item_slots item_slots_c = { NULL, 0 };
    pkmn::c::item_slots_cpp_to_c(
        item_slots_cpp,
        &item_slots_c
    );

    EXPECT_EQ(3, item_slots_c.length);
    EXPECT_EQ(PKMN_ITEM_POTION, item_slots_c.p_item_slots[0].item);
    EXPECT_EQ(50, item_slots_c.p_item_slots[0].amount);
    EXPECT_EQ(PKMN_ITEM_BERRY, item_slots_c.p_item_slots[1].item);
    EXPECT_EQ(28, item_slots_c.p_item_slots[1].amount);
    EXPECT_EQ(PKMN_ITEM_BERRY_POUCH, item_slots_c.p_item_slots[2].item);
    EXPECT_EQ(1, item_slots_c.p_item_slots[2].amount);

    pkmn_item_slots_free(
        &item_slots_c
    );
    EXPECT_EQ(NULL, item_slots_c.p_item_slots);
    EXPECT_EQ(0, item_slots_c.length);
}

TEST(cpp_to_c_test, levelup_move_cpp_to_c_test)
{
    pkmn::database::levelup_move levelup_move_cpp(
        pkmn::e_move::SCRATCH,
        50
    );

    struct pkmn_levelup_move levelup_move_c = { PKMN_MOVE_NONE, 0 };

    pkmn::c::levelup_move_cpp_to_c(
        levelup_move_cpp,
        &levelup_move_c
    );

    EXPECT_EQ(PKMN_MOVE_SCRATCH, levelup_move_c.move);
    EXPECT_EQ(50, levelup_move_c.level);
}

TEST(cpp_to_c_test, levelup_moves_cpp_to_c_test)
{
    pkmn::database::levelup_moves_t levelup_moves_cpp =
    {
        pkmn::database::levelup_move(
            pkmn::e_move::SCRATCH,
            50
        ),
        pkmn::database::levelup_move(
            pkmn::e_move::SYNTHESIS,
            5
        ),
        pkmn::database::levelup_move(
            pkmn::e_move::FRENZY_PLANT,
            37
        )
    };

    struct pkmn_levelup_moves levelup_moves_c = { NULL, 0 };
    pkmn::c::levelup_moves_cpp_to_c(
        levelup_moves_cpp,
        &levelup_moves_c
    );

    EXPECT_EQ(3, levelup_moves_c.length);
    EXPECT_EQ(PKMN_MOVE_SCRATCH, levelup_moves_c.p_levelup_moves[0].move);
    EXPECT_EQ(50, levelup_moves_c.p_levelup_moves[0].level);
    EXPECT_EQ(PKMN_MOVE_SYNTHESIS, levelup_moves_c.p_levelup_moves[1].move);
    EXPECT_EQ(5, levelup_moves_c.p_levelup_moves[1].level);
    EXPECT_EQ(PKMN_MOVE_FRENZY_PLANT, levelup_moves_c.p_levelup_moves[2].move);
    EXPECT_EQ(37, levelup_moves_c.p_levelup_moves[2].level);

    pkmn_levelup_moves_free(
        &levelup_moves_c
    );
    EXPECT_EQ(NULL, levelup_moves_c.p_levelup_moves);
    EXPECT_EQ(0, levelup_moves_c.length);
}

TEST(cpp_to_c_test, move_list_cpp_to_c_test)
{
    pkmn::database::move_list_t move_list_cpp =
    {
        pkmn::database::move_entry(pkmn::e_move::SCRATCH, pkmn::e_game::RED),
        pkmn::database::move_entry(pkmn::e_move::SYNTHESIS, pkmn::e_game::SILVER),
        pkmn::database::move_entry(pkmn::e_move::FRENZY_PLANT, pkmn::e_game::EMERALD)
    };

    struct pkmn_string_list string_list_c = { NULL, 0 };
    pkmn::c::move_list_to_string_list(
        move_list_cpp,
        &string_list_c
    );

    EXPECT_EQ(3, string_list_c.length);
    EXPECT_STREQ("Scratch", string_list_c.pp_strings[0]);
    EXPECT_STREQ("Synthesis", string_list_c.pp_strings[1]);
    EXPECT_STREQ("Frenzy Plant", string_list_c.pp_strings[2]);

    pkmn_string_list_free(
        &string_list_c
    );
    EXPECT_EQ(NULL, string_list_c.pp_strings);
    EXPECT_EQ(0, string_list_c.length);
}

TEST(cpp_to_c_test, move_slot_cpp_to_c_test)
{
    pkmn::move_slot move_slot_cpp(
        pkmn::e_move::TACKLE,
        50
    );

    struct pkmn_move_slot move_slot_c;

    pkmn::c::move_slot_cpp_to_c(
        move_slot_cpp,
        &move_slot_c
    );

    EXPECT_EQ(PKMN_MOVE_TACKLE, move_slot_c.move);
    EXPECT_EQ(50, move_slot_c.pp);
}

TEST(cpp_to_c_test, move_slots_cpp_to_c_test)
{
    pkmn::move_slots_t move_slots_cpp =
    {
        pkmn::move_slot(
            pkmn::e_move::TACKLE,
            50
        ),
        pkmn::move_slot(
            pkmn::e_move::POUND,
            28
        ),
        pkmn::move_slot(
            pkmn::e_move::METRONOME,
            1
        )
    };

    struct pkmn_move_slots move_slots_c = { NULL, 0 };
    pkmn::c::move_slots_cpp_to_c(
        move_slots_cpp,
        &move_slots_c
    );

    EXPECT_EQ(3, move_slots_c.length);
    EXPECT_EQ(PKMN_MOVE_TACKLE, move_slots_c.p_move_slots[0].move);
    EXPECT_EQ(50, move_slots_c.p_move_slots[0].pp);
    EXPECT_EQ(PKMN_MOVE_POUND, move_slots_c.p_move_slots[1].move);
    EXPECT_EQ(28, move_slots_c.p_move_slots[1].pp);
    EXPECT_EQ(PKMN_MOVE_METRONOME, move_slots_c.p_move_slots[2].move);
    EXPECT_EQ(1, move_slots_c.p_move_slots[2].pp);

    pkmn_move_slots_free(
        &move_slots_c
    );
    EXPECT_EQ(NULL, move_slots_c.p_move_slots);
    EXPECT_EQ(0, move_slots_c.length);
}

TEST(cpp_to_c_test, pokemon_list_cpp_to_c)
{
    pkmn::pokemon_list_t pokemon_list_cpp =
    {
        pkmn::pokemon::make(
            pkmn::e_species::CHARMANDER, pkmn::e_game::RED, "", 5
        ),
        pkmn::pokemon::make(
            pkmn::e_species::SQUIRTLE, pkmn::e_game::BLUE, "", 10
        ),
        pkmn::pokemon::make(
            pkmn::e_species::BULBASAUR, pkmn::e_game::YELLOW, "", 15
        )
    };

    enum pkmn_error error = PKMN_ERROR_NONE;
    struct pkmn_pokemon_list pokemon_list_c = { NULL, 0 };
    pkmn::c::pokemon_list_cpp_to_c(
        pokemon_list_cpp,
        &pokemon_list_c
    );
    EXPECT_EQ(3, pokemon_list_c.length);

    for(size_t i = 0; i < 3; ++i)
    {
        int level_c = 0;

        EXPECT_EQ(
            int(pokemon_list_cpp[i]->get_species()),
            pokemon_list_c.p_pokemon[i].species
        );
        EXPECT_EQ(
            int(pokemon_list_cpp[i]->get_game()),
            pokemon_list_c.p_pokemon[i].game
        );

        error = pkmn_pokemon_get_level(
                    &pokemon_list_c.p_pokemon[i],
                    &level_c
                );
        EXPECT_EQ(PKMN_ERROR_NONE, error);
        EXPECT_EQ(
            pokemon_list_cpp[i]->get_level(),
            level_c
        );
    }

    pkmn_pokemon_list_free(
        &pokemon_list_c
    );
    EXPECT_EQ(NULL, pokemon_list_c.p_pokemon);
    EXPECT_EQ(0, pokemon_list_c.length);
}

TEST(cpp_to_c_test, pokemon_box_list_cpp_to_c_test)
{
    pkmn::pokemon_box_list_t pokemon_box_list_cpp =
    {
        pkmn::pokemon_box::make(pkmn::e_game::GOLD),
        pkmn::pokemon_box::make(pkmn::e_game::RUBY),
        pkmn::pokemon_box::make(pkmn::e_game::FIRERED)
    };

    pokemon_box_list_cpp[0]->set_name("ABCD");
    pokemon_box_list_cpp[1]->set_name("EFGH");
    pokemon_box_list_cpp[2]->set_name("IJKL");

    enum pkmn_error error = PKMN_ERROR_NONE;
    struct pkmn_pokemon_box_list pokemon_box_list_c = { NULL, 0 };
    pkmn::c::pokemon_box_list_cpp_to_c(
        pokemon_box_list_cpp,
        &pokemon_box_list_c
    );
    EXPECT_EQ(3, pokemon_box_list_c.length);

    for(size_t index = 0; index < 3; ++index)
    {
        char name_c[BUFFER_LEN] = {0};

        EXPECT_EQ(
            int(pokemon_box_list_cpp[index]->get_game()),
            int(pokemon_box_list_c.p_boxes[index].game)
        );

        error = pkmn_pokemon_box_get_name(
                    &pokemon_box_list_c.p_boxes[index],
                    name_c,
                    sizeof(name_c),
                    nullptr
                );
        EXPECT_EQ(PKMN_ERROR_NONE, error);
        EXPECT_STREQ(
            pokemon_box_list_cpp[index]->get_name().c_str(),
            name_c
        );
    }

    pkmn_pokemon_box_list_free(
        &pokemon_box_list_c
    );
    EXPECT_EQ(NULL, pokemon_box_list_c.p_boxes);
    EXPECT_EQ(0, pokemon_box_list_c.length);
}

TEST(cpp_to_c_test, int_pair_cpp_to_c_test)
{
    std::pair<int, int> int_pair_cpp(6322, 10011);
    struct pkmn_int_pair int_pair_c = {0, 0};

    pkmn::c::int_pair_cpp_to_c(
        int_pair_cpp,
        &int_pair_c
    );

    EXPECT_EQ(6322, int_pair_c.first);
    EXPECT_EQ(10011, int_pair_c.second);
}

TEST(cpp_to_c_test, string_cpp_to_c_test)
{
    std::string string_cpp = "LibPKMN";
    char string_c[50] = {0};
    char too_short_string_buffer[3] = {0};
    size_t string_length = 0;

    // Test not getting buffer length
    pkmn::c::string_cpp_to_c(
        string_cpp,
        string_c,
        sizeof(string_c)-1,
        nullptr
    );
    EXPECT_EQ(string_cpp, string_c);

    // Make sure accurate string length is returned.
    pkmn::c::string_cpp_to_c(
        string_cpp,
        string_c,
        sizeof(string_c)-1,
        &string_length
    );
    EXPECT_EQ(string_cpp, string_c);
    EXPECT_EQ(string_cpp.size(), string_length);

    // Make sure if a too-small buffer is given, the partial string
    // is returned.
    pkmn::c::string_cpp_to_c(
        string_cpp,
        too_short_string_buffer,
        sizeof(too_short_string_buffer)-1,
        &string_length
    );
    EXPECT_STREQ("Li", too_short_string_buffer);
    EXPECT_EQ(string_cpp.size(), string_length);
}

TEST(cpp_to_c_test, string_pair_cpp_to_c_test)
{
    std::pair<std::string, std::string> string_pair_cpp("LibPKMN", "PKSav");
    struct pkmn_string_pair string_pair_c = {NULL, NULL};

    pkmn::c::string_pair_cpp_to_c(
        string_pair_cpp,
        &string_pair_c
    );

    EXPECT_STREQ("LibPKMN", string_pair_c.p_first);
    EXPECT_STREQ("PKSav", string_pair_c.p_second);

    pkmn_string_pair_free(&string_pair_c);
    EXPECT_EQ(NULL, string_pair_c.p_first);
    EXPECT_EQ(NULL, string_pair_c.p_second);
}

TEST(cpp_to_c_test, std_map_keys_to_string_list_test)
{
    static const std::map<std::string, bool> string_bool_map =
    {
        {"key1", true},
        {"key2", false},
        {"key3", false},
        {"key4", true},
    };
    static const std::map<std::string, int> string_int_map =
    {
        {"key5", 1},
        {"key6", 2},
        {"key7", 3},
        {"key8", 4},
        {"key9", 5},
    };

    struct pkmn_string_list string_list_c = {NULL, 0};

    pkmn::c::string_map_keys_to_string_list<bool>(
        string_bool_map,
        &string_list_c
    );

    // To suppress Clang-tidy warning, instead of assering length
    EXPECT_EQ(4ULL, string_list_c.length);
    if(string_list_c.pp_strings)
    {
        EXPECT_STREQ("key1", string_list_c.pp_strings[0]);
        EXPECT_STREQ("key2", string_list_c.pp_strings[1]);
        EXPECT_STREQ("key3", string_list_c.pp_strings[2]);
        EXPECT_STREQ("key4", string_list_c.pp_strings[3]);

        pkmn_string_list_free(&string_list_c);
        EXPECT_EQ(NULL, string_list_c.pp_strings);
        EXPECT_EQ(0, string_list_c.length);
    }

    pkmn::c::string_map_keys_to_string_list<int>(
        string_int_map,
        &string_list_c
    );

    // To suppress Clang-tidy warning, instead of asserting length
    EXPECT_EQ(5ULL, string_list_c.length);
    if(string_list_c.pp_strings)
    {
        EXPECT_STREQ("key5", string_list_c.pp_strings[0]);
        EXPECT_STREQ("key6", string_list_c.pp_strings[1]);
        EXPECT_STREQ("key7", string_list_c.pp_strings[2]);
        EXPECT_STREQ("key8", string_list_c.pp_strings[3]);
        EXPECT_STREQ("key9", string_list_c.pp_strings[4]);

        pkmn_string_list_free(&string_list_c);
        EXPECT_EQ(NULL, string_list_c.pp_strings);
        EXPECT_EQ(0, string_list_c.length);
    }
}

TEST(cpp_to_c_test, string_list_cpp_to_c_test)
{
    std::vector<std::string> string_vector_cpp{"LibPKMN", "PKSav", "TKO"};

    struct pkmn_string_list string_list_c = { NULL, 0 };
    pkmn::c::string_list_cpp_to_c(
        string_vector_cpp,
        &string_list_c
    );

    EXPECT_EQ(3, string_list_c.length);
    EXPECT_STREQ("LibPKMN", string_list_c.pp_strings[0]);
    EXPECT_STREQ("PKSav", string_list_c.pp_strings[1]);
    EXPECT_STREQ("TKO", string_list_c.pp_strings[2]);

    pkmn_string_list_free(
        &string_list_c
    );
    EXPECT_EQ(NULL, string_list_c.pp_strings);
    EXPECT_EQ(0, string_list_c.length);
}

TEST(cpp_to_c_test, time_duration_cpp_to_c_test)
{
    const pkmn::time_duration time_duration_cpp1 = {23, 36, 30, 30};
    struct pkmn_time_duration time_duration_c = {0, 0, 0, 0};

    pkmn::c::time_duration_cpp_to_c(
        time_duration_cpp1,
        &time_duration_c
    );

    EXPECT_EQ(time_duration_cpp1.hours,   time_duration_c.hours);
    EXPECT_EQ(time_duration_cpp1.minutes, time_duration_c.minutes);
    EXPECT_EQ(time_duration_cpp1.seconds, time_duration_c.seconds);
    EXPECT_EQ(time_duration_cpp1.frames,  time_duration_c.frames);

    pkmn::time_duration time_duration_cpp2 = pkmn::c::time_duration_c_to_cpp(
                                                 &time_duration_c
                                             );
    EXPECT_EQ(time_duration_cpp1, time_duration_cpp2);
}

ext: cpp

max_stars_repo_name: ncorgan/libpkmn

max_issues_repo_name: PMArkive/libpkmn

max_forks_repo_name: PMArkive/libpkmn


text:
#include "CorePch.h"
#include <rtp++/network/UdpRtpNetworkInterface.h>
#include <boost/bind.hpp>
#include <boost/asio/placeholders.hpp>
#include <rtp++/RtpTime.h>

//#define DEBUG_RTP
//#define DEBUG_RTP_TS
//#define RTP_DEBUG

namespace rtp_plus_plus
{

UdpRtpNetworkInterface::UdpRtpNetworkInterface(boost::asio::io_service& rIoService, const EndPoint& rtpEp, const EndPoint& rtcpEp, boost::system::error_code& ec)
  :RtpNetworkInterface(std::unique_ptr<RtpPacketiser>(new RtpPacketiser())),
  m_rIoService(rIoService),
  m_bInitialised(false),
  m_bShuttingDown(false),
  m_rtpEp(rtpEp),
  m_rtcpEp(rtcpEp)
#ifdef RTP_DEBUG
  ,m_rtpDump("dump.rtp")
#endif
{
  initialise(ec);
}

UdpRtpNetworkInterface::UdpRtpNetworkInterface(boost::asio::io_service& rIoService, const EndPoint& rtpEp, const EndPoint& rtcpEp,
  std::unique_ptr<boost::asio::ip::udp::socket> pRtpSocket, std::unique_ptr<boost::asio::ip::udp::socket> pRtcpSocket)
  :RtpNetworkInterface(std::unique_ptr<RtpPacketiser>(new RtpPacketiser())),
  m_rIoService(rIoService),
  m_bInitialised(false),
  m_bShuttingDown(false),
  m_rtpEp(rtpEp),
  m_rtcpEp(rtcpEp)
#ifdef RTP_DEBUG
  , m_rtpDump("dump.rtp")
#endif
{
  initialiseExistingSockets(std::move(pRtpSocket), std::move(pRtcpSocket));
}

UdpRtpNetworkInterface::~UdpRtpNetworkInterface()
{
}

void UdpRtpNetworkInterface::initialise(boost::system::error_code& ec)
{
  // unicast case
  VLOG(5) << "Creating RTP socket " << m_rtpEp;
  m_pRtpSocket = UdpSocketWrapper::create(m_rIoService, m_rtpEp.getAddress(), m_rtpEp.getPort(), ec);
  if (ec) return;
  m_pRtpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtpPacket, this, _1, _2, _3, _4));
  m_pRtpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtpPacket, this, _1, _2, _3, _4));
  // This check should be sufficient
  assert (m_rtpEp.getPort() != m_rtcpEp.getPort());
  // add rtcp socket
  VLOG(5) << "Creating RTCP socket " << m_rtcpEp;
  m_pRtcpSocket = UdpSocketWrapper::create(m_rIoService, m_rtcpEp.getAddress(), m_rtcpEp.getPort(), ec);
  if (ec) return;
  m_pRtcpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtcpPacket, this, _1, _2, _3, _4));
  m_pRtcpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtcpPacket, this, _1, _2, _3, _4));

  m_bInitialised = true;
  m_bShuttingDown = false;

  // Multicast HACK: commenting out for now: refactoring code to pass in local endpoint so
  // that this class does not need to know about the RtpSessionParameter
#if 0
  // we need to create the appropriate UDP sockets according to the RTP session description
  // and bind the handlers to the methods that can handle incoming packets
  const EndPoint remoteRtpEp = m_sessionParameters.getRemoteEndPoint(0).first;
  const EndPoint remoteRtcpEp = m_sessionParameters.getRemoteEndPoint(0).second;
  VLOG(1) << "ADDR/RTP/RTCP " << remoteRtpEp.getAddress() << ":" << remoteRtpEp.getPort() << ":" << remoteRtcpEp.getPort();

  // HACK: use the remote endpoint to determine if this is multicast!
  boost::asio::ip::address addr(boost::asio::ip::address::from_string(remoteRtpEp.getAddress()));
  if (addr.is_multicast())
  {
    m_pRtpSocket = UdpSocketWrapper::create(m_rIoService, remoteRtpEp.getAddress(), remoteRtpEp.getPort());
    m_pRtcpSocket = UdpSocketWrapper::create(m_rIoService, remoteRtcpEp.getAddress(), remoteRtcpEp.getPort());
    m_pRtpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtpPacket, this, _1, _2, _3, _4));
    m_pRtpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtpPacket, this, _1, _2, _3, _4));
    m_pRtcpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtcpPacket, this, _1, _2, _3, _4));
    m_pRtcpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtcpPacket, this, _1, _2, _3, _4));
  }
  else
  {
    // unicast case
    // this basic receiver can only handle one endpoint
    const EndPoint rtpEp = m_sessionParameters.getLocalEndPoint(0).first;
    const EndPoint rtcpEp = m_sessionParameters.getLocalEndPoint(0).second;

    m_pRtpSocket = UdpSocketWrapper::create(m_rIoService, rtpEp.getAddress(), rtpEp.getPort());
    m_pRtpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtpPacket, this, _1, _2, _3, _4));
    m_pRtpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtpPacket, this, _1, _2, _3, _4));
    // This check should be sufficient
    assert (rtpEp.getPort() != rtcpEp.getPort());
    // add rtcp socket
    m_pRtcpSocket = UdpSocketWrapper::create(m_rIoService, rtcpEp.getAddress(), rtcpEp.getPort());
    m_pRtcpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtcpPacket, this, _1, _2, _3, _4));
    m_pRtcpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtcpPacket, this, _1, _2, _3, _4));
  }

  m_bInitialised = true;
  m_bShuttingDown = false;
#endif
}

void UdpRtpNetworkInterface::initialiseExistingSockets(std::unique_ptr<boost::asio::ip::udp::socket> pRtpSocket, std::unique_ptr<boost::asio::ip::udp::socket> pRtcpSocket)
{
  // unicast case
  VLOG(5) << "Using existing RTP socket " << m_rtpEp;
  m_pRtpSocket = UdpSocketWrapper::create(m_rIoService, m_rtpEp.getAddress(), m_rtpEp.getPort(), std::move(pRtpSocket));
  m_pRtpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtpPacket, this, _1, _2, _3, _4));
  m_pRtpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtpPacket, this, _1, _2, _3, _4));
  // This check should be sufficient
  assert(m_rtpEp.getPort() != m_rtcpEp.getPort());
  // add rtcp socket
  VLOG(5) << "Using existing RTCP socket " << m_rtcpEp;
  m_pRtcpSocket = UdpSocketWrapper::create(m_rIoService, m_rtcpEp.getAddress(), m_rtcpEp.getPort(), std::move(pRtcpSocket));
  m_pRtcpSocket->onRecv(boost::bind(&UdpRtpNetworkInterface::handleRtcpPacket, this, _1, _2, _3, _4));
  m_pRtcpSocket->onSendComplete(boost::bind(&UdpRtpNetworkInterface::handleSentRtcpPacket, this, _1, _2, _3, _4));

  m_bInitialised = true;
  m_bShuttingDown = false;
}

void UdpRtpNetworkInterface::reset()
{
  boost::system::error_code ec;
  // TODO: how to handle this case after using error code in init?
  // Also, there are two init methods now...
  // Throw exception? Get rid of reset method?
  initialise(ec);
  if (ec)
  {
    assert(false);
  }
}

void UdpRtpNetworkInterface::shutdown()
{
  VLOG(5) << "[" << this << "] Shutting down RTP network interface";
  if (m_bInitialised)
  {
    m_pRtpSocket->close();
    m_pRtcpSocket->close();

    m_bInitialised = false;
  }
}

bool UdpRtpNetworkInterface::recv()
{
  assert(m_pRtpSocket && m_pRtcpSocket);
  m_pRtpSocket->recv();
  m_pRtcpSocket->recv();
  return true;
}

bool UdpRtpNetworkInterface::doSendRtp(Buffer rtpBuffer, const EndPoint& rtpEp)
{
  if (!m_bInitialised)
  {
    // only log first occurence
    LOG_FIRST_N(INFO, 1) << "Shutting down, unable to deliver RTP packets";
    return false;
  }

#ifdef DEBUG_RTP
  LOG_FIRST_N(INFO, 1) << "Sending 1st RTP packet";
#endif

  m_pRtpSocket->send(rtpBuffer, rtpEp);
  return true;
}

bool UdpRtpNetworkInterface::doSendRtcp(Buffer rtcpBuffer, const EndPoint& rtcpEp)
{
  if (!m_bInitialised)
  {
    // only log first occurence
    LOG_FIRST_N(INFO, 1) << "Shutting down, unable to deliver RTCP packets";
    return false;
  }

#ifdef DEBUG_RTP
  LOG_FIRST_N(INFO, 1) << "Sending 1st RTCP packet";
#endif

  m_pRtcpSocket->send(rtcpBuffer, rtcpEp );
  return true;
}

void UdpRtpNetworkInterface::handleRtpPacket(const boost::system::error_code& ec, UdpSocketWrapper::ptr pSource, NetworkPacket networkPacket, const EndPoint& ep)
{
#ifdef DEBUG_INCOMING_RTP
  LOG_EVERY_N(INFO, 100) << "[" << this << "] Read  " << google::COUNTER << "RTP packets";
#endif
  if (!ec)
  {
    processIncomingRtpPacket(networkPacket, ep);
    pSource->recv();
  }
  else
  {
#ifdef _WIN32
    // http://stackoverflow.com/questions/13995830/windows-boost-asio-10061-in-async-receive-from-on-on-async-send-to
    if (ec == boost::asio::error::connection_refused || ec == boost::asio::error::connection_reset)
    {
      VLOG(10) << "WIN32 Error in receive: " << ec.message();
      // just start next read
      pSource->recv();
      return;
    }
#endif
    if (ec != boost::asio::error::operation_aborted)
    {
      LOG(WARNING) << "Error in receive: " << ec.message();
    }
    else
    {
      VLOG(10) << "Shutting down: " << ec.message();
    }
  }
}

void UdpRtpNetworkInterface::handleRtcpPacket(const boost::system::error_code& ec, UdpSocketWrapper::ptr pSource, NetworkPacket networkPacket, const EndPoint& ep)
{
#if 0
  DLOG(INFO) << "[" << this << "]";
#endif
  if (!ec)
  {
#ifdef DEBUG_RTCP
    uint32_t uiNtpTimestampMsw = 0;
    uint32_t uiNtpTimestampLsw = 0;
    getNTPTimeStamp(uiNtpTimestampMsw, uiNtpTimestampLsw);
    DLOG(INFO) << "###### RTCP received: MSW " << hex(uiNtpTimestampMsw) << " LSW:" << hex(uiNtpTimestampLsw);
#endif
    // call base class method
    processIncomingRtcpPacket(networkPacket, ep);
    pSource->recv();
  }
  else
  {
#ifdef _WIN32
    // http://stackoverflow.com/questions/13995830/windows-boost-asio-10061-in-async-receive-from-on-on-async-send-to
    if (ec == boost::asio::error::connection_refused || ec == boost::asio::error::connection_reset)
    {
      VLOG(10) << "WIN32 Error in receive: " << ec.message();
      // just start next read
      pSource->recv();
      return;
    }
#endif

    if ((ec != boost::asio::error::operation_aborted) &&
        (ec != boost::asio::error::connection_refused)) // seems to get triggered on Windows?
    {
      LOG(WARNING) << "Error in receive: " << ec.message();
    }
    else
    {
      VLOG(10) << "Error in receive. Shutting down? " << ec.message();
    }
  }
}

void UdpRtpNetworkInterface::handleSentRtpPacket(const boost::system::error_code& ec, UdpSocketWrapper::ptr pSource, Buffer buffer, const EndPoint& ep)
{
  onRtpSent(buffer, ep);
}

void UdpRtpNetworkInterface::handleSentRtcpPacket(const boost::system::error_code& ec, UdpSocketWrapper::ptr pSource, Buffer buffer, const EndPoint& ep)
{
  onRtcpSent(buffer, ep);
}

}


text:
/*=============================================================================
    Copyright (c) 2001-2010 Hartmut Kaiser

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#include <boost/detail/lightweight_test.hpp>

#include <boost/spirit/include/support_argument.hpp>
#include <boost/spirit/include/qi_char.hpp>
#include <boost/spirit/include/qi_numeric.hpp>
#include <boost/spirit/include/qi_directive.hpp>
#include <boost/cstdint.hpp>
#include "test.hpp"

///////////////////////////////////////////////////////////////////////////////
struct backwards_bool_policies : boost::spirit::qi::bool_policies<>
{
    // we want to interpret a 'true' spelled backwards as 'false'
    template <typename Iterator, typename Attribute>
    static bool
    parse_false(Iterator& first, Iterator const& last, Attribute& attr)
    {
        namespace spirit = boost::spirit;
        namespace qi = boost::spirit::qi;
        if (qi::detail::string_parse("eurt", first, last, qi::unused))
        {
            spirit::traits::assign_to(false, attr);    // result is false
            return true;
        }
        return false;
    }
};

///////////////////////////////////////////////////////////////////////////////
struct test_bool_type
{
    test_bool_type(bool b) : b(b) {}    // provide conversion
    bool b;
};

///////////////////////////////////////////////////////////////////////////////
int main()
{
    using spirit_test::test_attr;
    using spirit_test::test;
    using boost::spirit::qi::bool_;

    {
        BOOST_TEST(test("true", bool_));
        BOOST_TEST(test("false", bool_));
        BOOST_TEST(!test("fasle", bool_));
    }

    {
        using boost::spirit::qi::true_;
        using boost::spirit::qi::false_;

        BOOST_TEST(test("true", true_));
        BOOST_TEST(!test("true", false_));
        BOOST_TEST(test("false", false_));
        BOOST_TEST(!test("false", true_));
    }

    {
        using boost::spirit::qi::true_;
        using boost::spirit::qi::false_;
        using boost::spirit::qi::no_case;

        BOOST_TEST(test("True", no_case[bool_]));
        BOOST_TEST(test("False", no_case[bool_]));
        BOOST_TEST(test("True", no_case[true_]));
        BOOST_TEST(test("False", no_case[false_]));
    }

    {
        bool b = false;
        BOOST_TEST(test_attr("true", bool_, b) && b);
        BOOST_TEST(test_attr("false", bool_, b) && !b);
        BOOST_TEST(!test_attr("fasle", bool_, b));
    }

    {
        typedef boost::spirit::qi::bool_parser<bool, backwards_bool_policies>
            backwards_bool_type;
        backwards_bool_type const backwards_bool = backwards_bool_type();

        BOOST_TEST(test("true", backwards_bool));
        BOOST_TEST(test("eurt", backwards_bool));
        BOOST_TEST(!test("false", backwards_bool));
        BOOST_TEST(!test("fasle", backwards_bool));

        bool b = false;
        BOOST_TEST(test_attr("true", backwards_bool, b) && b);
        BOOST_TEST(test_attr("eurt", backwards_bool, b) && !b);
        BOOST_TEST(!test_attr("false", backwards_bool, b));
        BOOST_TEST(!test_attr("fasle", backwards_bool, b));
    }

    {
        typedef boost::spirit::qi::bool_parser<test_bool_type>
            bool_test_type;
        bool_test_type const test_bool = bool_test_type();

        BOOST_TEST(test("true", test_bool));
        BOOST_TEST(test("false", test_bool));
        BOOST_TEST(!test("fasle", test_bool));

        test_bool_type b = false;
        BOOST_TEST(test_attr("true", test_bool, b) && b.b);
        BOOST_TEST(test_attr("false", test_bool, b) && !b.b);
        BOOST_TEST(!test_attr("fasle", test_bool, b));
    }

    return boost::report_errors();
}

max_issues_repo_name: jonstewart/boost-svn

max_forks_repo_name: jonstewart/boost-svn


text:
#pragma once

#include <algorithm>
#include <boost/asio.hpp>
#include <memory>

#include "handler.hpp"

namespace otus {
  class Session: public std::enable_shared_from_this<Session> {
  public:
    Session(boost::asio::ip::tcp::socket &&socket, Handler &handler):
    socket(std::move(socket)), handler(handler) { }

    void start() { doRead(); }

  private:
    void doRead();
    void doWrite(std::string const &message);

    boost::asio::ip::tcp::socket socket;
    Handler &handler;
    boost::asio::streambuf buffer { };
  };
}


text:
/*
 * Copyright (C) 2014-2015 Open Source Robotics Foundation
 *
 * 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 <boost/filesystem.hpp>

#include "gazebo/test/ServerFixture.hh"

using namespace gazebo;
class PluginTest : public ServerFixture
{
};

/////////////////////////////////////////////////
TEST_F(PluginTest, ModelExceptionConstructor)
{
  Load("worlds/exception_model_plugin_constructor_test.world", true);

  physics::WorldPtr world = physics::get_world("default");
  ASSERT_TRUE(world != NULL);

  gazebo::physics::ModelPtr model = world->GetModel("box");
  ASSERT_TRUE(model != NULL);

  world->Step(100);

  char *home = getenv("HOME");
  ASSERT_TRUE(home);

  boost::filesystem::path path(home);
  path /= "/.gazebo/server-11345/default.log";

  // Open the log file, and read back the string
  std::ifstream ifs(path.string().c_str(), std::ios::in);
  std::string loggedString;

  while (!ifs.eof())
  {
    std::string line;
    std::getline(ifs, line);
    loggedString += line;
  }

  EXPECT_TRUE(loggedString.find(
        "Exception occured in the constructor of plugin with name") !=
      std::string::npos);
}

/////////////////////////////////////////////////
TEST_F(PluginTest, ModelExceptionInit)
{
  Load("worlds/exception_model_plugin_init_test.world", true);

  physics::WorldPtr world = physics::get_world("default");
  ASSERT_TRUE(world != NULL);

  gazebo::physics::ModelPtr model = world->GetModel("box");
  ASSERT_TRUE(model != NULL);

  world->Step(100);

  char *home = getenv("HOME");
  ASSERT_TRUE(home);

  boost::filesystem::path path(home);
  path /= "/.gazebo/server-11345/default.log";

  // Open the log file, and read back the string
  std::ifstream ifs(path.string().c_str(), std::ios::in);
  std::string loggedString;

  while (!ifs.eof())
  {
    std::string line;
    std::getline(ifs, line);
    loggedString += line;
  }

  EXPECT_TRUE(loggedString.find(
        "Exception occured in the Init function of plugin with name") !=
      std::string::npos);
}

/////////////////////////////////////////////////
TEST_F(PluginTest, ModelExceptionLoad)
{
  Load("worlds/exception_model_plugin_load_test.world", true);

  physics::WorldPtr world = physics::get_world("default");
  ASSERT_TRUE(world != NULL);

  gazebo::physics::ModelPtr model = world->GetModel("box");
  ASSERT_TRUE(model != NULL);

  world->Step(100);

  char *home = getenv("HOME");
  ASSERT_TRUE(home);

  boost::filesystem::path path(home);
  path /= "/.gazebo/server-11345/default.log";

  // Open the log file, and read back the string
  std::ifstream ifs(path.string().c_str(), std::ios::in);
  std::string loggedString;

  while (!ifs.eof())
  {
    std::string line;
    std::getline(ifs, line);
    loggedString += line;
  }

  EXPECT_TRUE(loggedString.find(
        "Exception occured in the Load function of plugin with name") !=
      std::string::npos);
}

/////////////////////////////////////////////////
int main(int argc, char **argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

ext: cc

max_stars_repo_name: harderthan/gazebo

max_issues_repo_name: harderthan/gazebo

max_forks_repo_name: harderthan/gazebo


text: #include <boost/compute/algorithm/random_shuffle.hpp>