/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include "AngleTerm.h"
#include "../MMExceptions.h"
#include <Utils/Constants.h>
#include <Utils/Math/AtomicSecondDerivativeCollection.h>
#include <Utils/Math/AutomaticDifferentiation/AutomaticDifferentiationHelpers.h>
#include <Utils/Math/AutomaticDifferentiation/VectorDerivatives3D.h>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>

namespace Scine {

using namespace Utils::AutomaticDifferentiation;
namespace MolecularMechanics {

AngleTerm::AngleTerm(AtomIndex firstAtom, AtomIndex secondAtom, AtomIndex thirdAtom, const Angle& angle, const AngleType& typeOfAngle)
  : firstAtom_(firstAtom), secondAtom_(secondAtom), thirdAtom_(thirdAtom), angle_(angle), typeOfAngle_(typeOfAngle) {
}

AngleTerm::~AngleTerm() = default;

double AngleTerm::evaluateAngleTerm(const Utils::PositionCollection& positions,
                                    Utils::AtomicSecondDerivativeCollection& derivatives) const {
  if (this->disabled_)
    return 0.0;
  if (!angle_.hasParameters()) // Check this only if this term is not disabled
    throw MMAngleParametersNotAvailableException(typeOfAngle_.a1, typeOfAngle_.a2, typeOfAngle_.a3);

  Eigen::Vector3d a(positions.row(firstAtom_) - positions.row(secondAtom_));
  Eigen::Vector3d b(positions.row(thirdAtom_) - positions.row(secondAtom_));

  double angle = acos((a.dot(b) / (a.norm() * b.norm())));
  Second1D result = angle_.getInteraction(angle);
  double energy = result.value();

  Second3D angleContributionAtom1;
  Second3D angleContributionAtom2;
  Second3D angleContributionAtom3;
  if (angle < Utils::Constants::pi - singularityCriterion_ && angle > singularityCriterion_) {
    calculateDerivativesWithNormalFormula(result, a, b, angleContributionAtom1, angleContributionAtom2, angleContributionAtom3);
  }
  else {
    calculateDerivativesForCriticalAngles(result, a, b, angleContributionAtom1, angleContributionAtom2, angleContributionAtom3);
    energy = 0;
  }

  derivatives[firstAtom_] += angleContributionAtom1;
  derivatives[secondAtom_] += angleContributionAtom2;
  derivatives[thirdAtom_] += angleContributionAtom3;

  return energy;
}

Second3D AngleTerm::threeDimDer(const Second1D& energy, const Second3D& alpha) {
  return {energy.value(),
          energy.first() * alpha.dx(),
          energy.first() * alpha.dy(),
          energy.first() * alpha.dz(),
          energy.second() * alpha.dx() * alpha.dx() + energy.first() * alpha.XX(),
          energy.second() * alpha.dy() * alpha.dy() + energy.first() * alpha.YY(),
          energy.second() * alpha.dz() * alpha.dz() + energy.first() * alpha.ZZ(),
          energy.second() * alpha.dx() * alpha.dy() + energy.first() * alpha.XY(),
          energy.second() * alpha.dx() * alpha.dz() + energy.first() * alpha.XZ(),
          energy.second() * alpha.dy() * alpha.dz() + energy.first() * alpha.YZ()};
}

void AngleTerm::calculateDerivativesWithNormalFormula(const Second1D& angleDerivative, const Eigen::Vector3d& a,
                                                      const Eigen::Vector3d& b, Second3D& angleContributionAtom1,
                                                      Second3D& angleContributionAtom2, Second3D& angleContributionAtom3) {
  // NB: suffix digit indicates with respect to which atom the derivative is taken
  auto a1 = VectorDerivatives3D::spatialVectorHessian3D(a);
  auto a2 = VectorDerivatives3D::spatialVectorHessian3DWithInverseDerivative(a);
  auto b3 = VectorDerivatives3D::spatialVectorHessian3D(b);
  auto b2 = VectorDerivatives3D::spatialVectorHessian3DWithInverseDerivative(b);

  Second3D alpha1 = arccos(a1.dot(b) / (a1.norm() * b.norm()));
  Second3D alpha2 = arccos(a2.dot(b2) / (a2.norm() * b2.norm())); // NB: maybe the second derivatives for atom 2 can be
                                                                  // obtained from atom 1 and 2 in the end and this is
                                                                  // not needed
  Second3D alpha3 = arccos(b3.dot(a) / (a.norm() * b3.norm()));

  angleContributionAtom1 = threeDimDer(angleDerivative, alpha1);
  angleContributionAtom2 = threeDimDer(angleDerivative, alpha2);
  angleContributionAtom3 = threeDimDer(angleDerivative, alpha3);
}

void AngleTerm::calculateDerivativesForCriticalAngles(const Second1D& angleDerivative, const Eigen::Vector3d& a,
                                                      const Eigen::Vector3d& b, Second3D& angleContributionAtom1,
                                                      Second3D& angleContributionAtom2, Second3D& angleContributionAtom3) {
  // Explanation of formulas: see Alain's derivation
  Eigen::Vector3d orientation = a.normalized();

  double inverseDistanceSum = 1.0 / a.norm() + 1.0 / b.norm();
  double d2thetad1 = 1.0 / a.squaredNorm();
  double d2thetad3 = 1.0 / b.squaredNorm();
  double d2thetad2 = inverseDistanceSum * inverseDistanceSum;

  double d2dxx1 = d2thetad1 * angleDerivative.second(); // Second derivative of the Energy with respect to x in local
                                                        // coordinate system for atom 1
  double d2dxx2 = d2thetad2 * angleDerivative.second();
  double d2dxx3 = d2thetad3 * angleDerivative.second();

  double x = orientation.x();
  double y = orientation.y();
  double z = orientation.z();

  // Get the local coordinate system: generate perpendicular unit x and y vectors
  Eigen::Vector3d yV(0, z, -y);
  if (std::abs(x) > std::abs(y))
    yV = Eigen::Vector3d(-z, 0, x);
  yV.normalize();
  Eigen::Vector3d xV = yV.cross(orientation);

  // Derivatives of local coordinates with respect to global coordinates
  double dxdx = xV.x();
  double dxdy = xV.y();
  double dxdz = xV.z();
  double dydx = yV.x();
  double dydy = yV.y();
  double dydz = yV.z();

  angleContributionAtom1 = Second3D(0, 0, 0, 0, d2dxx1 * (dxdx * dxdx + dydx * dydx), d2dxx1 * (dxdy * dxdy + dydy * dydy),
                                    d2dxx1 * (dxdz * dxdz + dydz * dydz), d2dxx1 * (dxdx * dxdy + dydx * dydy),
                                    d2dxx1 * (dxdx * dxdz + dydx * dydz), d2dxx1 * (dxdy * dxdz + dydy * dydz));
  angleContributionAtom2 = Second3D(0, 0, 0, 0, d2dxx2 * (dxdx * dxdx + dydx * dydx), d2dxx2 * (dxdy * dxdy + dydy * dydy),
                                    d2dxx2 * (dxdz * dxdz + dydz * dydz), d2dxx2 * (dxdx * dxdy + dydx * dydy),
                                    d2dxx2 * (dxdx * dxdz + dydx * dydz), d2dxx2 * (dxdy * dxdz + dydy * dydz));
  angleContributionAtom3 = Second3D(0, 0, 0, 0, d2dxx3 * (dxdx * dxdx + dydx * dydx), d2dxx3 * (dxdy * dxdy + dydy * dydy),
                                    d2dxx3 * (dxdz * dxdz + dydz * dydz), d2dxx3 * (dxdx * dxdy + dydx * dydy),
                                    d2dxx3 * (dxdx * dxdz + dydx * dydz), d2dxx3 * (dxdy * dxdz + dydy * dydz));
}

AngleType AngleTerm::getTypeOfAngle() const {
  return typeOfAngle_;
}

int AngleTerm::getFirstAtom() const {
  return firstAtom_;
}

int AngleTerm::getSecondAtom() const {
  return secondAtom_;
}

int AngleTerm::getThirdAtom() const {
  return thirdAtom_;
}

} // namespace MolecularMechanics
} // namespace Scine


#ifndef __EIGENMULTIVARIATENORMAL_HPP
#define __EIGENMULTIVARIATENORMAL_HPP

#include <Eigen/Dense>
#include <math.h>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>

template<typename _Scalar, int _size>
class EigenMultivariateNormal
{
    boost::mt19937 rng;    // The uniform pseudo-random algorithm
    boost::normal_distribution<_Scalar> norm;  // The gaussian combinator
    boost::variate_generator<boost::mt19937&,boost::normal_distribution<_Scalar> >
       randN; // The 0-mean unit-variance normal generator

    Eigen::Matrix<_Scalar,_size,_size> rot;
    Eigen::Matrix<_Scalar,_size,1> scl;

    Eigen::Matrix<_Scalar,_size,1> mean;

public:
    EigenMultivariateNormal(const Eigen::Matrix<_Scalar,_size,1>& meanVec,
        const Eigen::Matrix<_Scalar,_size,_size>& covarMat)
        : randN(rng,norm)
    {
        setCovar(covarMat);
        setMean(meanVec);
    }

    void setCovar(const Eigen::Matrix<_Scalar,_size,_size>& covarMat)
    {
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix<_Scalar,_size,_size> >
           eigenSolver(covarMat);
        rot = eigenSolver.eigenvectors();
        scl = eigenSolver.eigenvalues();
        for (int ii=0;ii<_size;++ii) {
            scl(ii,0) = sqrt(scl(ii,0));
        }
    }

    void setMean(const Eigen::Matrix<_Scalar,_size,1>& meanVec)
    {
        mean = meanVec;
    }

    void nextSample(Eigen::Matrix<_Scalar,_size,1>& sampleVec)
    {
        for (int ii=0;ii<_size;++ii) {
            sampleVec(ii,0) = randN()*scl(ii,0);
        }
        sampleVec = rot*sampleVec + mean;
    }

};

#endif


#if !defined F_DECOMPOSER_HPP
#define F_DECOMPOSER_HPP

#include <Eigen/Core>
#include <Eigen/LU>

namespace LocalStress {
  // NOTE:
  // drij = ri - rj;
#define DECL_DECOMPOSE_FORCE_FUNC(N)                                    \
  template <typename T, int32_t num_body = N>                           \
  auto decomposeForce(const std::array<Vec<T>, N>& F,                   \
                      const std::array<Vec<T>, N*(N-1)/2>& dr) -> remove_reference_t<decltype(dr)> \

  // in  F = {F0, F1, F2}, dr = {dr01, dr12, dr20};
  // out dF = {dF01, dF12, dF20};
  DECL_DECOMPOSE_FORCE_FUNC(3) {
    using dr_pair_t = remove_const_t<remove_reference_t<decltype(dr)>>;
    const dr_pair_t dr_u {normalize(dr[0]), normalize(dr[1]), normalize(dr[2])};
    const auto dFa = F[0] * (dr_u[0] - dr_u[2]) / (1.0 - dr_u[0] * dr_u[2]);
    const auto dFb = F[1] * (dr_u[1] - dr_u[0]) / (1.0 - dr_u[1] * dr_u[0]);
    const auto dFc = F[2] * (dr_u[2] - dr_u[1]) / (1.0 - dr_u[2] * dr_u[1]);
    const auto dF01 = 0.5 * (dFa + dFb - dFc);
    const auto dF12 = 0.5 * (dFb + dFc - dFa);
    const auto dF02 = 0.5 * (dFc + dFa - dFb);
    return {dF01 * dr_u[0], dF12 * dr_u[1], dF02 * dr_u[2]};
  }

#define VEC_TO_MAT(M, i, j, v)                  \
  do {                                          \
    for (int32_t axis = 0; axis < D; axis++) {  \
      M(D * i + axis, j) = v[axis];             \
    }                                           \
  } while (0)

  // in  F = {F0, F1, F2, F3}, dr = {dr01, dr02, dr03, dr12, dr13, dr23}
  // out dF = {dF01, dF02, dF03, dF12, dF13, dF23}
  DECL_DECOMPOSE_FORCE_FUNC(4) {
    constexpr int32_t nrows = num_body * D;
    constexpr int32_t ncols = num_body * (num_body - 1) / 2;

    Eigen::Matrix<T, nrows, ncols> dr_mat;
    dr_mat.setZero();

    // F0
    VEC_TO_MAT(dr_mat, 0, 0, dr[0]);
    VEC_TO_MAT(dr_mat, 0, 1, dr[1]);
    VEC_TO_MAT(dr_mat, 0, 2, dr[2]);

    // F1
    VEC_TO_MAT(dr_mat, 1, 0, -dr[0]);
    VEC_TO_MAT(dr_mat, 1, 3, dr[3]);
    VEC_TO_MAT(dr_mat, 1, 4, dr[4]);

    // F2
    VEC_TO_MAT(dr_mat, 2, 1, -dr[1]);
    VEC_TO_MAT(dr_mat, 2, 3, -dr[3]);
    VEC_TO_MAT(dr_mat, 2, 5, dr[5]);

    // F3
    VEC_TO_MAT(dr_mat, 3, 2, -dr[2]);
    VEC_TO_MAT(dr_mat, 3, 4, -dr[4]);
    VEC_TO_MAT(dr_mat, 3, 5, -dr[5]);

    Eigen::VectorXd F_vec(nrows);
    std::copy_n(&F[0].x, nrows, F_vec.data());

    Eigen::VectorXd cf_dF = dr_mat.fullPivLu().solve(F_vec);

    return {cf_dF[0] * dr[0], cf_dF[1] * dr[1], cf_dF[2] * dr[2],
            cf_dF[3] * dr[3], cf_dF[4] * dr[4], cf_dF[5] * dr[5]};
  }

#undef VEC_TO_MAT
#undef DECL_DECOMPOSE_FORCE_FUNC
}
#endif


#include <NTL/GF2E.h>

#include <NTL/new.h>

NTL_START_IMPL

GF2EInfoT::GF2EInfoT(const GF2X& NewP)
{
   build(p, NewP);

   if (p.size == 1) {
      if (deg(p) <= NTL_BITS_PER_LONG/2)
         KarCross = 4;
      else
         KarCross = 8;
   }
   else if (p.size == 2)
      KarCross = 8;
   else if (p.size <= 5)
      KarCross = 4;
   else if (p.size == 6)
      KarCross = 3;
   else
      KarCross = 2;

   if (p.size <= 1) {
      if (deg(p) <= NTL_BITS_PER_LONG/2)
         ModCross = 20;
      else
         ModCross = 40;
   }
   else if (p.size <= 2)
      ModCross = 75;
   else if (p.size <= 4)
      ModCross = 50;
   else
      ModCross = 25;

   if (p.size == 1) {
      if (deg(p) <= NTL_BITS_PER_LONG/2)
         DivCross = 100;
      else
         DivCross = 200;
   }
   else if (p.size == 2)
      DivCross = 400;
   else if (p.size <= 4)
      DivCross = 200;
   else if (p.size == 5)
      DivCross = 150;
   else if (p.size <= 13)
      DivCross = 100;
   else
      DivCross = 75;

   _card_exp = p.n;
}

const ZZ& GF2E::cardinality()
{
   if (!GF2EInfo) LogicError("GF2E::cardinality: undefined modulus");

   do { // NOTE: thread safe lazy init
      Lazy<ZZ>::Builder builder(GF2EInfo->_card);
      if (!builder()) break;
      UniquePtr<ZZ> p;
      p.make();
      power(*p, 2, GF2EInfo->_card_exp);
      builder.move(p);
   } while (0);

   return *GF2EInfo->_card;
}

NTL_THREAD_LOCAL
SmartPtr<GF2EInfoT> GF2EInfo = 0;

void GF2E::init(const GF2X& p)
{
   GF2EContext c(p);
   c.restore();
}

void GF2EContext::save()
{
   ptr = GF2EInfo;
}

void GF2EContext::restore() const
{
   GF2EInfo = ptr;
}

GF2EBak::~GF2EBak()
{
   if (MustRestore) c.restore();
}

void GF2EBak::save()
{
   c.save();
   MustRestore = true;
}

void GF2EBak::restore()
{
   c.restore();
   MustRestore = false;
}

const GF2E& GF2E::zero()
{
   NTL_THREAD_LOCAL static GF2E z(INIT_NO_ALLOC);
   return z;
}

istream& operator>>(istream& s, GF2E& x)
{
   GF2X y;

   NTL_INPUT_CHECK_RET(s, s >> y);
   conv(x, y);

   return s;
}

void div(GF2E& x, const GF2E& a, const GF2E& b)
{
   GF2E t;

   inv(t, b);
   mul(x, a, t);
}

void div(GF2E& x, GF2 a, const GF2E& b)
{
   inv(x, b);
   mul(x, x, a);
}

void div(GF2E& x, long a, const GF2E& b)
{
   inv(x, b);
   mul(x, x, a);
}

void inv(GF2E& x, const GF2E& a)
{
   InvMod(x._GF2E__rep, a._GF2E__rep, GF2E::modulus());
}

NTL_END_IMPL


/*
Copyright (c) 2018 Inverse Palindrome
Apophis - SteeringBehaviors.cpp
InversePalindrome.com
*/

#include "Constants.hpp"
#include "SteeringBehaviors.hpp"

#include <cocos/base/ccRandom.h>

#include <boost/math/constants/constants.hpp>

#include <cmath>

b2Vec2 SteeringBehaviors::seek(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, const b2Vec2& bodyVelocity, float maxSpeed)
{
    return desiredVelocity(bodyPosition, targetPosition, maxSpeed) - bodyVelocity;
}

b2Vec2 SteeringBehaviors::flee(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, const b2Vec2& bodyVelocity, float maxSpeed)
{
    return desiredVelocity(targetPosition, bodyPosition, maxSpeed) - bodyVelocity;
}

b2Vec2 SteeringBehaviors::pursue(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, const b2Vec2& bodyVelocity, const b2Vec2& targetVelocity, float maxSpeed)
{
    const auto predictionFrames = (targetPosition - bodyPosition).Length() / maxSpeed;

    return seek(bodyPosition, targetPosition + predictionFrames * targetVelocity, bodyVelocity, maxSpeed);
}

b2Vec2 SteeringBehaviors::evade(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, const b2Vec2& bodyVelocity, const b2Vec2& targetVelocity, float maxSpeed)
{
    const auto predictionFrames = (targetPosition - bodyPosition).Length() / maxSpeed;

    return flee(bodyPosition, targetPosition + predictionFrames * targetVelocity, bodyVelocity, maxSpeed);
}

b2Vec2 SteeringBehaviors::arrive(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, const b2Vec2& bodyVelocity, float slowRadius, float maxSpeed)
{
    if (const auto radius = (targetPosition - bodyPosition).Length(); radius < slowRadius)
    {
        return seek(bodyPosition, targetPosition, bodyVelocity, maxSpeed * radius / slowRadius);
    }

    return seek(bodyPosition, targetPosition, bodyVelocity, maxSpeed);
}

b2Vec2 SteeringBehaviors::wander(const b2Vec2& bodyPosition, const b2Vec2& bodyVelocity, float wanderDistance, float wanderRadius, float wanderRate, float& wanderAngle, float maxSpeed)
{
    auto wanderCenter = bodyVelocity;
    wanderCenter.Normalize();
    wanderCenter *= wanderDistance;
    wanderCenter += { wanderRadius* std::cos(wanderAngle), wanderRadius* std::sin(wanderAngle) };

    wanderAngle += cocos2d::rand_minus1_1() * wanderRate;

    return seek(bodyPosition, bodyPosition + wanderCenter, bodyVelocity, maxSpeed);
}

b2Vec2 SteeringBehaviors::orbit(const b2Vec2& satellitePosition, const b2Vec2& primaryPosition, const b2Vec2& bodyVelocity, float maxSpeed)
{
    const auto radius = primaryPosition - satellitePosition;

    auto steeringForce = radius.Skew();
    steeringForce.Normalize();
    steeringForce *= maxSpeed;

    return steeringForce - bodyVelocity;
}

b2Vec2 SteeringBehaviors::alignForce(const b2Vec2& agentPosition, const std::vector<b2Vec2>& neighborVelocities, float alignmentForce)
{
    b2Vec2 steeringForce(0.f, 0.f);

    for (const auto& neighborVelocity : neighborVelocities)
    {
        steeringForce += neighborVelocity;
    }

    if (!neighborVelocities.empty())
    {
        steeringForce *= 1.f / neighborVelocities.size();
        steeringForce.Normalize();
        steeringForce *= alignmentForce;
    }

    return steeringForce;
}

b2Vec2 SteeringBehaviors::cohesionForce(const b2Vec2& agentPosition, const std::vector<b2Vec2>& neighborPositions, float cohesionForce)
{
    b2Vec2 steeringForce(0.f, 0.f);

    for (const auto& neighborPosition : neighborPositions)
    {
        steeringForce += neighborPosition;
    }

    if (!neighborPositions.empty())
    {
        steeringForce *= 1.f / neighborPositions.size();
        steeringForce -= agentPosition;
        steeringForce.Normalize();
        steeringForce *= cohesionForce;
    }

    return steeringForce;
}

b2Vec2 SteeringBehaviors::separateForce(const b2Vec2& agentPosition, const std::vector<b2Vec2>& neighborPositions, float separationForce)
{
    b2Vec2 steeringForce(0.f, 0.f);

    for (const auto& neighborPosition : neighborPositions)
    {
        steeringForce += neighborPosition - agentPosition;
    }

    if (!neighborPositions.empty())
    {
        steeringForce *= -1.f / neighborPositions.size();
        steeringForce.Normalize();
        steeringForce *= separationForce;
    }

    return steeringForce;
}

b2Vec2 SteeringBehaviors::desiredVelocity(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, float maxSpeed)
{
    auto desiredVelocity = targetPosition - bodyPosition;
    desiredVelocity.Normalize();
    desiredVelocity *= maxSpeed;

    return desiredVelocity;
}

float SteeringBehaviors::face(float desiredAngle, float bodyAngle, float bodyAngularVelocity, float bodyInertia)
{
    const auto nextAngle = bodyAngle + bodyAngularVelocity / Constants::FPS;
    const auto totalRotation = std::remainderf(desiredAngle - nextAngle, 2 * boost::math::constants::pi<float>());

    return bodyInertia * totalRotation * Constants::FPS;
}

float SteeringBehaviors::face(const b2Vec2& bodyPosition, const b2Vec2& targetPosition, float bodyAngle, float bodyAngularVelocity, float bodyInertia)
{
    const auto desiredAngle = std::atan2f(targetPosition.y - bodyPosition.y, targetPosition.x - bodyPosition.x);

    return face(desiredAngle, bodyAngle, bodyAngularVelocity, bodyInertia);
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_ARITHMETIC_FUNCTIONS_SCALAR_HYPOT_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_SCALAR_HYPOT_HPP_INCLUDED

#include <boost/simd/arithmetic/functions/hypot.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/sdk/config.hpp>
#include <boost/simd/include/functions/scalar/abs.hpp>
#include <boost/simd/include/functions/scalar/exponent.hpp>
#include <boost/simd/include/functions/scalar/ldexp.hpp>
#include <boost/simd/include/functions/scalar/sqr.hpp>
#include <boost/simd/include/functions/scalar/sqrt.hpp>
#include <boost/simd/include/functions/scalar/max.hpp>
#include <boost/simd/include/functions/scalar/min.hpp>
#include <boost/simd/include/constants/maxexponentm1.hpp>
#include <boost/simd/include/constants/minexponent.hpp>
#include <boost/simd/include/constants/zero.hpp>

#ifndef BOOST_SIMD_NO_INVALIDS
#include <boost/simd/include/functions/scalar/is_inf.hpp>
#include <boost/simd/include/functions/scalar/is_nan.hpp>
#include <boost/simd/include/constants/inf.hpp>
#endif

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( hypot_, tag::cpu_
                                    , (A0)
                                    , ((scalar_<floating_<A0> >))
                                      ((scalar_<floating_<A0> >))
                                    )
  {
    typedef A0 result_type;

    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      typedef typename dispatch::meta::as_integer<result_type>::type   iA0;
      #ifndef BOOST_SIMD_NO_INVALIDS
      if (is_nan(a0) && is_inf(a1)) return Inf<A0>();
      if (is_inf(a0) && is_nan(a1)) return Inf<A0>();
      #endif
      A0 r =  boost::simd::abs(a0);
      A0 i =  boost::simd::abs(a1);
      iA0 e =  exponent(boost::simd::max(i, r));
      e = boost::simd::min(boost::simd::max(e,Minexponent<A0>()),Maxexponentm1<A0>());
      return ldexp(sqrt(sqr(ldexp(r, -e))+sqr(ldexp(i, -e))), e);
    }
  };
} } }

#endif


#include <queue>
#include <fstream>
#include <iostream>

#include <boost/algorithm/string.hpp>

#include "../headers/Phylogeny.h"

typedef boost::variate_generator<boost::mt19937 &, boost::exponential_distribution<>> exp_distribution;

Phylogeny::Phylogeny(double alpha, double beta)
{
    this->alpha = alpha;
    this->beta = beta;
}
std::vector<std::vector<double>> Phylogeny::generateProbMatrix(double time)
{
    double s = (1 - std::exp(-4 * beta * time)) / 4;
    double u = (1 + std::exp(-4 * beta * time) - 2 * std::exp(-2 * (alpha + beta) * time)) / 4;
    double r = 1 - 2 * s - u;

    std::vector<std::vector<double>> retArray = {
        {r, s, u, s},
        {s, r, s, u},
        {u, s, r, s},
        {s, u, s, r}};

    return retArray;
}
std::vector<treeVertex> Phylogeny::phylogenesy(std::vector<treeVertex> &tree, int epochs, double timeGeneratorMean)
{
    boost::mt19937 generator(5u);
    exp_distribution distribution(generator, boost::exponential_distribution<>(timeGeneratorMean));

    int actualEpoch = 0;
    std::queue<int> vertexQueue;

    for (treeVertex &vertex : tree)
    {
        if (vertex.left == 0 && vertex.right == 0) //only push leaves
            vertexQueue.push(vertex.id);
    }
    while (!vertexQueue.empty())
    {
        int vertex = vertexQueue.front();
        vertexQueue.pop();

        double time = tree[vertex].timeDepth + distribution();

        ProteinSequence nextSpecie = mutate(tree[vertex].sequence, time, generator);

        if (nextSpecie.baseVec2String() == tree[vertex].sequence.baseVec2String())
            continue;
        treeVertex dummyVertex(tree[vertex].sequence);
        tree[vertex].left = dummyVertex.id = tree.size();

        dummyVertex.root = tree[vertex].id;
        dummyVertex.depth = tree[vertex].depth + 1;
        dummyVertex.timeDepth = time;

        tree.push_back(dummyVertex);

        treeVertex nextVertex(nextSpecie);

        nextVertex.root = tree[vertex].id;
        nextVertex.sequence = nextSpecie;
        nextVertex.depth = tree[vertex].depth + 1;
        nextVertex.timeDepth = time;

        tree[vertex].right = nextVertex.id = tree.size();

        tree.push_back(nextVertex);
        actualEpoch++;
        if (actualEpoch < epochs)
        {
            vertexQueue.push(dummyVertex.id);
            vertexQueue.push(nextVertex.id);
        }
    }
    return tree;
}
//TODO: mutatis mutandis
ProteinSequence Phylogeny::mutate(ProteinSequence &initialSequence, double &time, boost::mt19937 &generator)
{
    ProteinSequence mutatedSequence;

    std::vector<std::vector<double>> probMatrix = generateProbMatrix(time);

    for (base &prot : initialSequence)
    {
        int protCode = static_cast<int>(prot);
        boost::random::discrete_distribution<int, double> mutationprobability(probMatrix[protCode]);
        int nextProtein = mutationprobability(generator);
        mutatedSequence.pushBack(static_cast<base>(nextProtein));
    }

    return mutatedSequence;
}

void Phylogeny::writeBranchesIntoFile(std::string filename, std::vector<treeVertex> &tree)
{
    std::ofstream targetFile;
    std::vector<treeVertex> leaves;

    for (treeVertex &vertex : tree)
    {
        if (vertex.right == 0 && vertex.left == 0)
            leaves.push_back(vertex);
    }

    targetFile.open(filename, std::ios::out);

    if (targetFile.is_open())
    {

        for (treeVertex &vertex : leaves)
        {
            targetFile << this->getReversedOrderOfAncestors(tree, vertex) << "\n";
        }
    }
}

std::string Phylogeny::getReversedOrderOfAncestors(std::vector<treeVertex> &tree, treeVertex &vertex)
{
    std::vector<std::string> sequences;

    int nextVertex = vertex.id;
    ProteinSequence sequence;
    std::vector<std::string> stringVector;
    while (nextVertex >= 0)
    {
        sequence = tree[nextVertex].sequence;

        stringVector.push_back(sequence.baseVec2String());

        nextVertex = tree[nextVertex].root;
    }

    std::string output;

    for (int i = stringVector.size() - 1; i >= 0; i--)
    {
        output += stringVector[i] + ",";
    }
    output += std::to_string(vertex.depth);
    return output;
}
void Phylogeny::printTree(std::vector<treeVertex> &tree)
{
    treeVertex maxDepthVertex = *std::max_element(tree.begin(), tree.end(), [](treeVertex &a, treeVertex &b) { return a.depth < b.depth; });
    for (int depth = 0; depth <= maxDepthVertex.depth; depth++)
    {

        for (treeVertex &vertex : tree)
        {
            if (vertex.depth == depth)
            {
                std::cout << vertex.sequence.baseVec2String() << " (d=" << vertex.timeDepth << "|id=" << vertex.id << "|r=" << vertex.root << ")   ";
            }
        }
        std::cout << '\n'
                  << '\n';
    }
}

std::vector<treeVertex> Phylogeny::reversePhylogeny(const std::string &filename)
{
    std::ifstream inputStream;
    inputStream.open(filename);
    std::vector<treeVertex> tree;

    if (inputStream.is_open())
    {
        std::vector<std::vector<std::string>> readTree;
        // read to map
        std::string line;
        std::vector<std::string> lineSplitted;
        int maxLength = 0;
        do
        {
            std::getline(inputStream, line);
            boost::split(lineSplitted, line, boost::is_any_of(","));
            if (lineSplitted.back() != "")
            {
                int length = std::stoi(lineSplitted.back()) + 1;
                if (length > maxLength)
                    maxLength = length;
            }
            lineSplitted.pop_back();
            readTree.push_back(lineSplitted);
            lineSplitted.clear();

        } while (!inputStream.eof());
        readTree.pop_back();
        // set root

        treeVertex rootVertex;
        rootVertex.sequence = ProteinSequence(readTree[0][0]);
        rootVertex.id = 0;
        rootVertex.root = -1;
        rootVertex.depth = 0;

        tree.push_back(rootVertex);

        for (int i = 1; i < maxLength; i++)
        {
            for (std::vector<std::string> &stringVec : readTree)
            {

                for (int epoch = 1; epoch <= i && epoch < stringVec.size(); epoch++)
                {
                    std::string sequence = stringVec[epoch];
                    std::string ancestor = stringVec[epoch - 1];
                    std::vector<int> possibleAncestors;
                    for (int i = 0; i < tree.size(); i++)
                    {
                        if (tree[i].sequence.baseVec2String() == ancestor && tree[i].depth == epoch - 1)
                            possibleAncestors.push_back(i);
                    }
                    for (int vertex : possibleAncestors) // possible immediate ancestors, now we need to find way to the root
                    {
                        bool goodWay = true;
                        int iter = 1;
                        int nextVertex = vertex;
                        while (goodWay && tree[nextVertex].root != -1 && epoch > iter)
                        {
                            if (tree[nextVertex].sequence.baseVec2String() != stringVec[epoch - iter])
                            {
                                goodWay = false;
                                break;
                            }
                            iter++;
                            nextVertex = tree[nextVertex].root;
                        }
                        if (!goodWay)
                            continue;
                        else
                        {
                            treeVertex newVertex;
                            newVertex.sequence = ProteinSequence(sequence);
                            newVertex.id = tree.size();
                            newVertex.root = tree[vertex].id;
                            newVertex.depth = epoch;

                            ancestor = tree[vertex].sequence.baseVec2String();
                            if (sequence == ancestor) //left child (no left child present)
                            {
                                if (tree[vertex].left == 0)
                                {
                                    tree.push_back(newVertex);
                                    tree[vertex].left = newVertex.id;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            else if (tree[vertex].right == 0) //right child
                            {
                                tree.push_back(newVertex);
                                tree[vertex].right = newVertex.id;
                            }

                            break;
                        }
                    }
                }
            }
        }
    }
    return tree;
}


#define BOOST_TEST_MODULE "test_3spn2_bond_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/forcefield/3SPN2/ThreeSPN2BondPotential.hpp>

BOOST_AUTO_TEST_CASE(potential_3spn2_bond_double)
{
    using real_type = double;
    constexpr std::size_t N = 1000;
    constexpr real_type   h = 1e-6;

    const real_type k  = 1.0;
    const real_type r0 = 5.0;

    mjolnir::ThreeSPN2BondPotential<real_type> potential(k, r0);

    const real_type x_min = 0.5 * r0;
    const real_type x_max = 1.5 * r0;
    const real_type dx = (x_max - x_min) / N;

    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x    = x_min + i * dx;
        const real_type pot1 = potential.potential(x + h);
        const real_type pot2 = potential.potential(x - h);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = potential.derivative(x);

        if(std::abs(pot1 / pot2 - 1.0) < h)
        {
            // pot1 and pot2 are almost the same, thus dpot ~ 0.0.
            // to avoid numerical error in dpot, here it checks `deri ~ 0.0`.
            BOOST_TEST(deri == 0.0, boost::test_tools::tolerance(h));
        }
        else
        {
            BOOST_TEST(dpot == deri, boost::test_tools::tolerance(h));
        }
    }
}

BOOST_AUTO_TEST_CASE(potential_3spn2_bond_float)
{
    using real_type = float;
    constexpr std::size_t N = 100;
    constexpr real_type   h = 1e-3f;

    const real_type k  = 1.0f;
    const real_type r0 = 5.0f;

    mjolnir::ThreeSPN2BondPotential<real_type> potential(k, r0);

    const real_type x_min = 0.5 * r0;
    const real_type x_max = 1.5 * r0;
    const real_type dx = (x_max - x_min) / N;

    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x    = x_min + i * dx;
        const real_type pot1 = potential.potential(x + h);
        const real_type pot2 = potential.potential(x - h);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = potential.derivative(x);

        if(std::abs(pot1 / pot2 - 1.0f) < h)
        {
            // pot1 and pot2 are almost the same, thus dpot ~ 0.0.
            // to avoid numerical error in dpot, here it checks `deri ~ 0.0`.
            BOOST_TEST(deri == 0.0f, boost::test_tools::tolerance(h));
        }
        else
        {
            BOOST_TEST(dpot == deri, boost::test_tools::tolerance(h));
        }
    }
}


//  Copyright John Maddock 2006.
//  Copyright Paul A. Bristow 2007, 2010.
//  Use, modification and distribution are subject to 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)

#ifndef BOOST_LIBS_MATH_TEST_INSTANTIATE_HPP
#define BOOST_LIBS_MATH_TEST_INSTANTIATE_HPP

#ifndef BOOST_MATH_ASSERT_UNDEFINED_POLICY
#  define BOOST_MATH_ASSERT_UNDEFINED_POLICY false
#endif

#include <boost/math/distributions.hpp>

#include <boost/math/special_functions.hpp>
#include <boost/math/concepts/distributions.hpp>
#include <boost/concept_archetype.hpp>

#ifndef BOOST_MATH_INSTANTIATE_MINIMUM

typedef boost::math::policies::policy<boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false> > test_policy;

namespace test{

BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(test_policy)

}

namespace dist_test{

BOOST_MATH_DECLARE_DISTRIBUTIONS(double, test_policy)

}
#endif

#if !defined(TEST_GROUP_1) && !defined(TEST_GROUP_2) && !defined(TEST_GROUP_3) \
   && !defined(TEST_GROUP_4) && !defined(TEST_GROUP_5) && !defined(TEST_GROUP_6) \
   && !defined(TEST_GROUP_7) && !defined(TEST_GROUP_8) && !defined(TEST_GROUP_9)
#  define TEST_GROUP_1
#  define TEST_GROUP_2
#  define TEST_GROUP_3
#  define TEST_GROUP_4
#  define TEST_GROUP_5
#  define TEST_GROUP_6
#  define TEST_GROUP_7
#  define TEST_GROUP_8
#  define TEST_GROUP_9
#endif

template <class RealType>
void instantiate(RealType)
{
   using namespace boost;
   using namespace boost::math;
   using namespace boost::math::concepts;
#ifdef TEST_GROUP_1
   function_requires<DistributionConcept<arcsine_distribution<RealType> > >();
   function_requires<DistributionConcept<bernoulli_distribution<RealType> > >();
   function_requires<DistributionConcept<beta_distribution<RealType> > >();
   function_requires<DistributionConcept<binomial_distribution<RealType> > >();
   function_requires<DistributionConcept<cauchy_distribution<RealType> > >();
   function_requires<DistributionConcept<chi_squared_distribution<RealType> > >();
   function_requires<DistributionConcept<exponential_distribution<RealType> > >();
   function_requires<DistributionConcept<extreme_value_distribution<RealType> > >();
   function_requires<DistributionConcept<fisher_f_distribution<RealType> > >();
   function_requires<DistributionConcept<gamma_distribution<RealType> > >();
   function_requires<DistributionConcept<geometric_distribution<RealType> > >();
   function_requires<DistributionConcept<hypergeometric_distribution<RealType> > >();
   function_requires<DistributionConcept<hyperexponential_distribution<RealType> > >();
   function_requires<DistributionConcept<inverse_chi_squared_distribution<RealType> > >();
   function_requires<DistributionConcept<inverse_gamma_distribution<RealType> > >();
   function_requires<DistributionConcept<inverse_gaussian_distribution<RealType> > >();
   function_requires<DistributionConcept<laplace_distribution<RealType> > >();
   function_requires<DistributionConcept<logistic_distribution<RealType> > >();
   function_requires<DistributionConcept<lognormal_distribution<RealType> > >();
   function_requires<DistributionConcept<negative_binomial_distribution<RealType> > >();
   function_requires<DistributionConcept<non_central_chi_squared_distribution<RealType> > >();
   function_requires<DistributionConcept<non_central_beta_distribution<RealType> > >();
   function_requires<DistributionConcept<non_central_f_distribution<RealType> > >();
   function_requires<DistributionConcept<non_central_t_distribution<RealType> > >();
   function_requires<DistributionConcept<normal_distribution<RealType> > >();
   function_requires<DistributionConcept<pareto_distribution<RealType> > >();
   function_requires<DistributionConcept<poisson_distribution<RealType> > >();
   function_requires<DistributionConcept<rayleigh_distribution<RealType> > >();
   function_requires<DistributionConcept<students_t_distribution<RealType> > >();
   function_requires<DistributionConcept<skew_normal_distribution<RealType> > >();
   function_requires<DistributionConcept<triangular_distribution<RealType> > >();
   function_requires<DistributionConcept<uniform_distribution<RealType> > >();
   function_requires<DistributionConcept<weibull_distribution<RealType> > >();
#endif
#ifndef BOOST_MATH_INSTANTIATE_MINIMUM
#ifdef TEST_GROUP_2
   function_requires<DistributionConcept<arcsine_distribution<RealType> > >();
   function_requires<DistributionConcept<bernoulli_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<beta_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<binomial_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<cauchy_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<chi_squared_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<exponential_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<extreme_value_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<fisher_f_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<gamma_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<geometric_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<hypergeometric_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<inverse_chi_squared_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<inverse_gamma_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<inverse_gaussian_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<laplace_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<logistic_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<lognormal_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<negative_binomial_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<non_central_chi_squared_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<non_central_beta_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<non_central_f_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<non_central_t_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<normal_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<pareto_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<poisson_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<rayleigh_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<skew_normal_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<students_t_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<triangular_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<uniform_distribution<RealType, test_policy> > >();
   function_requires<DistributionConcept<weibull_distribution<RealType, test_policy> > >();
#endif
#ifdef TEST_GROUP_3
   function_requires<DistributionConcept<dist_test::arcsine > >();
   function_requires<DistributionConcept<dist_test::bernoulli > >();
   function_requires<DistributionConcept<dist_test::beta > >();
   function_requires<DistributionConcept<dist_test::binomial > >();
   function_requires<DistributionConcept<dist_test::cauchy > >();
   function_requires<DistributionConcept<dist_test::chi_squared > >();
   function_requires<DistributionConcept<dist_test::exponential > >();
   function_requires<DistributionConcept<dist_test::extreme_value > >();
   function_requires<DistributionConcept<dist_test::fisher_f > >();
   function_requires<DistributionConcept<dist_test::gamma > >();
   function_requires<DistributionConcept<dist_test::geometric > >();
   function_requires<DistributionConcept<dist_test::hypergeometric > >();
   function_requires<DistributionConcept<dist_test::inverse_chi_squared > >();
   function_requires<DistributionConcept<dist_test::inverse_gamma > >();
   function_requires<DistributionConcept<dist_test::inverse_gaussian > >();
   function_requires<DistributionConcept<dist_test::laplace > >();
   function_requires<DistributionConcept<dist_test::logistic > >();
   function_requires<DistributionConcept<dist_test::lognormal > >();
   function_requires<DistributionConcept<dist_test::negative_binomial > >();
   function_requires<DistributionConcept<dist_test::non_central_chi_squared > >();
   function_requires<DistributionConcept<dist_test::non_central_beta > >();
   function_requires<DistributionConcept<dist_test::non_central_f > >();
   function_requires<DistributionConcept<dist_test::non_central_t > >();
   function_requires<DistributionConcept<dist_test::normal > >();
   function_requires<DistributionConcept<dist_test::pareto > >();
   function_requires<DistributionConcept<dist_test::poisson > >();
   function_requires<DistributionConcept<dist_test::rayleigh > >();
   function_requires<DistributionConcept<dist_test::students_t > >();
   function_requires<DistributionConcept<dist_test::triangular > >();
   function_requires<DistributionConcept<dist_test::uniform > >();
   function_requires<DistributionConcept<dist_test::weibull > >();
   function_requires<DistributionConcept<dist_test::hypergeometric > >();
#endif
#endif
   int i = 1;
   // Deal with unused variable warnings:
   (void)i;
   RealType v1(0.5), v2(0.5), v3(0.5);
   boost::detail::dummy_constructor dc;
   boost::output_iterator_archetype<RealType> oi(dc);
#ifdef TEST_GROUP_4
   boost::math::tgamma(v1);
   boost::math::tgamma1pm1(v1);
   boost::math::lgamma(v1);
   boost::math::lgamma(v1, &i);
   boost::math::digamma(v1);
   boost::math::trigamma(v1);
   boost::math::polygamma(i, v1);
   boost::math::tgamma_ratio(v1, v2);
   boost::math::tgamma_delta_ratio(v1, v2);
   boost::math::factorial<RealType>(i);
   boost::math::unchecked_factorial<RealType>(i);
   i = boost::math::max_factorial<RealType>::value;
   boost::math::double_factorial<RealType>(i);
   boost::math::rising_factorial(v1, i);
   boost::math::falling_factorial(v1, i);
   boost::math::tgamma(v1, v2);
   boost::math::tgamma_lower(v1, v2);
   boost::math::gamma_p(v1, v2);
   boost::math::gamma_q(v1, v2);
   boost::math::gamma_p_inv(v1, v2);
   boost::math::gamma_q_inv(v1, v2);
   boost::math::gamma_p_inva(v1, v2);
   boost::math::gamma_q_inva(v1, v2);
   boost::math::erf(v1);
   boost::math::erfc(v1);
   boost::math::erf_inv(v1);
   boost::math::erfc_inv(v1);
   boost::math::beta(v1, v2);
   boost::math::beta(v1, v2, v3);
   boost::math::betac(v1, v2, v3);
   boost::math::ibeta(v1, v2, v3);
   boost::math::ibetac(v1, v2, v3);
   boost::math::ibeta_inv(v1, v2, v3);
   boost::math::ibetac_inv(v1, v2, v3);
   boost::math::ibeta_inva(v1, v2, v3);
   boost::math::ibetac_inva(v1, v2, v3);
   boost::math::ibeta_invb(v1, v2, v3);
   boost::math::ibetac_invb(v1, v2, v3);
   boost::math::gamma_p_derivative(v2, v3);
   boost::math::ibeta_derivative(v1, v2, v3);
   boost::math::binomial_coefficient<RealType>(i, i);
   (boost::math::fpclassify)(v1);
   (boost::math::isfinite)(v1);
   (boost::math::isnormal)(v1);
   (boost::math::isnan)(v1);
   (boost::math::isinf)(v1);
   (boost::math::signbit)(v1);
   (boost::math::copysign)(v1, v2);
   (boost::math::changesign)(v1);
   (boost::math::sign)(v1);
   boost::math::log1p(v1);
   boost::math::expm1(v1);
   boost::math::cbrt(v1);
   boost::math::sqrt1pm1(v1);
   boost::math::powm1(v1, v2);
   boost::math::legendre_p(1, v1);
   boost::math::legendre_p(1, 0, v1);
   boost::math::legendre_q(1, v1);
   boost::math::legendre_p_prime(1, v1);
   boost::math::legendre_next(2, v1, v2, v3);
   boost::math::legendre_next(2, 2, v1, v2, v3);
   boost::math::laguerre(1, v1);
   boost::math::laguerre(2, 1, v1);
   boost::math::laguerre(2u, 1u, v1);
   boost::math::laguerre_next(2, v1, v2, v3);
   boost::math::laguerre_next(2, 1, v1, v2, v3);
   boost::math::hermite(1, v1);
   boost::math::hermite_next(2, v1, v2, v3);
   boost::math::spherical_harmonic_r(2, 1, v1, v2);
   boost::math::spherical_harmonic_i(2, 1, v1, v2);
   boost::math::ellint_1(v1);
   boost::math::ellint_1(v1, v2);
   boost::math::ellint_2(v1);
   boost::math::ellint_2(v1, v2);
   boost::math::ellint_3(v1, v2);
   boost::math::ellint_3(v1, v2, v3);
   boost::math::ellint_d(v1);
   boost::math::ellint_d(v1, v2);
   boost::math::jacobi_zeta(v1, v2);
   boost::math::heuman_lambda(v1, v2);
   boost::math::ellint_rc(v1, v2);
   boost::math::ellint_rd(v1, v2, v3);
   boost::math::ellint_rf(v1, v2, v3);
   boost::math::ellint_rg(v1, v2, v3);
   boost::math::ellint_rj(v1, v2, v3, v1);
   boost::math::jacobi_elliptic(v1, v2, &v1, &v2);
   boost::math::jacobi_cd(v1, v2);
   boost::math::jacobi_cn(v1, v2);
   boost::math::jacobi_cs(v1, v2);
   boost::math::jacobi_dc(v1, v2);
   boost::math::jacobi_dn(v1, v2);
   boost::math::jacobi_ds(v1, v2);
   boost::math::jacobi_nc(v1, v2);
   boost::math::jacobi_nd(v1, v2);
   boost::math::jacobi_ns(v1, v2);
   boost::math::jacobi_sc(v1, v2);
   boost::math::jacobi_sd(v1, v2);
   boost::math::jacobi_sn(v1, v2);
   boost::math::hypot(v1, v2);
   boost::math::sinc_pi(v1);
   boost::math::sinhc_pi(v1);
   boost::math::asinh(v1);
   boost::math::acosh(v1);
   boost::math::atanh(v1);
   boost::math::sin_pi(v1);
   boost::math::cos_pi(v1);
   boost::math::cyl_neumann(v1, v2);
   boost::math::cyl_neumann(i, v2);
   boost::math::cyl_bessel_j(v1, v2);
   boost::math::cyl_bessel_j(i, v2);
   boost::math::cyl_bessel_i(v1, v2);
   boost::math::cyl_bessel_i(i, v2);
   boost::math::cyl_bessel_k(v1, v2);
   boost::math::cyl_bessel_k(i, v2);
   boost::math::sph_bessel(i, v2);
   boost::math::sph_bessel(i, 1);
   boost::math::sph_neumann(i, v2);
   boost::math::sph_neumann(i, i);
   boost::math::cyl_neumann_prime(v1, v2);
   boost::math::cyl_neumann_prime(i, v2);
   boost::math::cyl_bessel_j_prime(v1, v2);
   boost::math::cyl_bessel_j_prime(i, v2);
   boost::math::cyl_bessel_i_prime(v1, v2);
   boost::math::cyl_bessel_i_prime(i, v2);
   boost::math::cyl_bessel_k_prime(v1, v2);
   boost::math::cyl_bessel_k_prime(i, v2);
   boost::math::sph_bessel_prime(i, v2);
   boost::math::sph_bessel_prime(i, 1);
   boost::math::sph_neumann_prime(i, v2);
   boost::math::sph_neumann_prime(i, i);
   boost::math::cyl_bessel_j_zero(v1, i);
   boost::math::cyl_bessel_j_zero(v1, i, i, oi);
   boost::math::cyl_neumann_zero(v1, i);
   boost::math::cyl_neumann_zero(v1, i, i, oi);
#ifdef TEST_COMPLEX
   boost::math::cyl_hankel_1(v1, v2);
   boost::math::cyl_hankel_1(i, v2);
   boost::math::cyl_hankel_2(v1, v2);
   boost::math::cyl_hankel_2(i, v2);
   boost::math::sph_hankel_1(v1, v2);
   boost::math::sph_hankel_1(i, v2);
   boost::math::sph_hankel_2(v1, v2);
   boost::math::sph_hankel_2(i, v2);
#endif
   boost::math::airy_ai(v1);
   boost::math::airy_bi(v1);
   boost::math::airy_ai_prime(v1);
   boost::math::airy_bi_prime(v1);

   boost::math::airy_ai_zero<RealType>(i);
   boost::math::airy_bi_zero<RealType>(i);
   boost::math::airy_ai_zero<RealType>(i, i, oi);
   boost::math::airy_bi_zero<RealType>(i, i, oi);

   boost::math::expint(v1);
   boost::math::expint(i);
   boost::math::expint(i, v2);
   boost::math::expint(i, i);
   boost::math::zeta(v1);
   boost::math::zeta(i);
   boost::math::owens_t(v1, v2);
   boost::math::trunc(v1);
   boost::math::itrunc(v1);
   boost::math::ltrunc(v1);
   boost::math::round(v1);
   boost::math::iround(v1);
   boost::math::lround(v1);
   boost::math::modf(v1, &v1);
   boost::math::modf(v1, &i);
   long l;
   boost::math::modf(v1, &l);
#ifdef BOOST_HAS_LONG_LONG
   boost::math::lltrunc(v1);
   boost::math::llround(v1);
   boost::long_long_type ll;
   boost::math::modf(v1, &ll);
#endif
   boost::math::pow<2>(v1);
   boost::math::nextafter(v1, v1);
   boost::math::float_next(v1);
   boost::math::float_prior(v1);
   boost::math::float_distance(v1, v1);
   boost::math::ulp(v1);
   boost::math::relative_difference(v1, v2);
   boost::math::epsilon_difference(v1, v2);

   boost::math::unchecked_bernoulli_b2n<RealType>(i);
   boost::math::bernoulli_b2n<RealType>(i);
   boost::math::bernoulli_b2n<RealType>(i, i, &v1);
   boost::math::tangent_t2n<RealType>(i);
   boost::math::tangent_t2n<RealType>(i, i, &v1);

#endif
#ifdef TEST_GROUP_9
   //
   // Over again, but arguments may be expression templates:
   //
   boost::math::tgamma(v1 + 0);
   boost::math::tgamma1pm1(v1 + 0);
   boost::math::lgamma(v1 * 1);
   boost::math::lgamma(v1 * 1, &i);
   boost::math::digamma(v1 * 1);
   boost::math::trigamma(v1 * 1);
   boost::math::polygamma(i, v1 * 1);
   boost::math::tgamma_ratio(v1 * 1, v2 + 0);
   boost::math::tgamma_delta_ratio(v1 * 1, v2 + 0);
   boost::math::factorial<RealType>(i);
   boost::math::unchecked_factorial<RealType>(i);
   i = boost::math::max_factorial<RealType>::value;
   boost::math::double_factorial<RealType>(i);
   boost::math::rising_factorial(v1 * 1, i);
   boost::math::falling_factorial(v1 * 1, i);
   boost::math::tgamma(v1 * 1, v2 + 0);
   boost::math::tgamma_lower(v1 * 1, v2 - 0);
   boost::math::gamma_p(v1 * 1, v2 + 0);
   boost::math::gamma_q(v1 * 1, v2 + 0);
   boost::math::gamma_p_inv(v1 * 1, v2 + 0);
   boost::math::gamma_q_inv(v1 * 1, v2 + 0);
   boost::math::gamma_p_inva(v1 * 1, v2 + 0);
   boost::math::gamma_q_inva(v1 * 1, v2 + 0);
   boost::math::erf(v1 * 1);
   boost::math::erfc(v1 * 1);
   boost::math::erf_inv(v1 * 1);
   boost::math::erfc_inv(v1 * 1);
   boost::math::beta(v1 * 1, v2 + 0);
   boost::math::beta(v1 * 1, v2 + 0, v3 / 1);
   boost::math::betac(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibeta(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibetac(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibeta_inv(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibetac_inv(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibeta_inva(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibetac_inva(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibeta_invb(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ibetac_invb(v1 * 1, v2 + 0, v3 / 1);
   boost::math::gamma_p_derivative(v2 * 1, v3 + 0);
   boost::math::ibeta_derivative(v1 * 1, v2 + 0, v3 / 1);
   (boost::math::fpclassify)(v1 * 1);
   (boost::math::isfinite)(v1 * 1);
   (boost::math::isnormal)(v1 * 1);
   (boost::math::isnan)(v1 * 1);
   (boost::math::isinf)(v1 * 1);
   (boost::math::signbit)(v1 * 1);
   (boost::math::copysign)(v1 * 1, v2 + 0);
   (boost::math::changesign)(v1 * 1);
   (boost::math::sign)(v1 * 1);
   boost::math::log1p(v1 * 1);
   boost::math::expm1(v1 * 1);
   boost::math::cbrt(v1 * 1);
   boost::math::sqrt1pm1(v1 * 1);
   boost::math::powm1(v1 * 1, v2 + 0);
   boost::math::legendre_p(1, v1 * 1);
   boost::math::legendre_p(1, 0, v1 * 1);
   boost::math::legendre_p_prime(1, v1 * 1);
   boost::math::legendre_q(1, v1 * 1);
   boost::math::legendre_next(2, v1 * 1, v2 + 0, v3 / 1);
   boost::math::legendre_next(2, 2, v1 * 1, v2 + 0, v3 / 1);
   boost::math::laguerre(1, v1 * 1);
   boost::math::laguerre(2, 1, v1 * 1);
   boost::math::laguerre(2u, 1u, v1 * 1);
   boost::math::laguerre_next(2, v1 * 1, v2 + 0, v3 / 1);
   boost::math::laguerre_next(2, 1, v1 * 1, v2 + 0, v3 / 1);
   boost::math::hermite(1, v1 * 1);
   boost::math::hermite_next(2, v1 * 1, v2 + 0, v3 / 1);
   boost::math::spherical_harmonic_r(2, 1, v1 * 1, v2 + 0);
   boost::math::spherical_harmonic_i(2, 1, v1 * 1, v2 + 0);
   boost::math::ellint_1(v1 * 1);
   boost::math::ellint_1(v1 * 1, v2 + 0);
   boost::math::ellint_2(v1 * 1);
   boost::math::ellint_2(v1 * 1, v2 + 0);
   boost::math::ellint_3(v1 * 1, v2 + 0);
   boost::math::ellint_3(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ellint_rc(v1 * 1, v2 + 0);
   boost::math::ellint_rd(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ellint_rf(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ellint_rg(v1 * 1, v2 + 0, v3 / 1);
   boost::math::ellint_rj(v1 * 1, v2 + 0, v3 / 1, v1 * 1);
   boost::math::ellint_d(v1 * 1);
   boost::math::ellint_d(v1 * 1, v2 + 0);
   boost::math::jacobi_zeta(v1 * 1, v2 + 0);
   boost::math::heuman_lambda(v1 * 1, v2 + 0);
   boost::math::jacobi_elliptic(v1 * 1, v2 + 0, &v1, &v2);
   boost::math::jacobi_cd(v1 * 1, v2 + 0);
   boost::math::jacobi_cn(v1 * 1, v2 + 0);
   boost::math::jacobi_cs(v1 * 1, v2 + 0);
   boost::math::jacobi_dc(v1 * 1, v2 + 0);
   boost::math::jacobi_dn(v1 * 1, v2 + 0);
   boost::math::jacobi_ds(v1 * 1, v2 + 0);
   boost::math::jacobi_nc(v1 * 1, v2 + 0);
   boost::math::jacobi_nd(v1 * 1, v2 + 0);
   boost::math::jacobi_ns(v1 * 1, v2 + 0);
   boost::math::jacobi_sc(v1 * 1, v2 + 0);
   boost::math::jacobi_sd(v1 * 1, v2 + 0);
   boost::math::jacobi_sn(v1 * 1, v2 + 0);
   boost::math::hypot(v1 * 1, v2 + 0);
   boost::math::sinc_pi(v1 * 1);
   boost::math::sinhc_pi(v1 * 1);
   boost::math::asinh(v1 * 1);
   boost::math::acosh(v1 * 1);
   boost::math::atanh(v1 * 1);
   boost::math::sin_pi(v1 * 1);
   boost::math::cos_pi(v1 * 1);
   boost::math::cyl_neumann(v1 * 1, v2 + 0);
   boost::math::cyl_neumann(i, v2 * 1);
   boost::math::cyl_bessel_j(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_j(i, v2 * 1);
   boost::math::cyl_bessel_i(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_i(i, v2 * 1);
   boost::math::cyl_bessel_k(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_k(i, v2 * 1);
   boost::math::sph_bessel(i, v2 * 1);
   boost::math::sph_bessel(i, 1);
   boost::math::sph_neumann(i, v2 * 1);
   boost::math::sph_neumann(i, i);
   boost::math::cyl_neumann_prime(v1 * 1, v2 + 0);
   boost::math::cyl_neumann_prime(i, v2 * 1);
   boost::math::cyl_bessel_j_prime(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_j_prime(i, v2 * 1);
   boost::math::cyl_bessel_i_prime(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_i_prime(i, v2 * 1);
   boost::math::cyl_bessel_k_prime(v1 * 1, v2 + 0);
   boost::math::cyl_bessel_k_prime(i, v2 * 1);
   boost::math::sph_bessel_prime(i, v2 * 1);
   boost::math::sph_bessel_prime(i, 1);
   boost::math::sph_neumann_prime(i, v2 * 1);
   boost::math::sph_neumann_prime(i, i);
   boost::math::cyl_bessel_j_zero(v1 * 1, i);
   boost::math::cyl_bessel_j_zero(v1 * 1, i, i, oi);
   boost::math::cyl_neumann_zero(v1 * 1, i);
   boost::math::cyl_neumann_zero(v1 * 1, i, i, oi);
#ifdef TEST_COMPLEX
   boost::math::cyl_hankel_1(v1, v2);
   boost::math::cyl_hankel_1(i, v2);
   boost::math::cyl_hankel_2(v1, v2);
   boost::math::cyl_hankel_2(i, v2);
   boost::math::sph_hankel_1(v1, v2);
   boost::math::sph_hankel_1(i, v2);
   boost::math::sph_hankel_2(v1, v2);
   boost::math::sph_hankel_2(i, v2);
#endif
   boost::math::airy_ai(v1 * 1);
   boost::math::airy_bi(v1 * 1);
   boost::math::airy_ai_prime(v1 * 1);
   boost::math::airy_bi_prime(v1 * 1);
   boost::math::expint(v1 * 1);
   boost::math::expint(i);
   boost::math::expint(i, v2 * 1);
   boost::math::expint(i, i);
   boost::math::zeta(v1 * 1);
   boost::math::zeta(i);
   boost::math::owens_t(v1 * 1, v2 + 0);
   boost::math::trunc(v1 * 1);
   boost::math::itrunc(v1 * 1);
   boost::math::ltrunc(v1 * 1);
   boost::math::round(v1 * 1);
   boost::math::iround(v1 * 1);
   boost::math::lround(v1 * 1);
   //boost::math::modf(v1 * 1, &v1);
   //boost::math::modf(v1 * 1, &i);
   //long l;
   //boost::math::modf(v1 * 1, &l);
#ifdef BOOST_HAS_LONG_LONG
   boost::math::lltrunc(v1 * 1);
   boost::math::llround(v1 * 1);
   //boost::long_long_type ll;
   //boost::math::modf(v1 * 1, &ll);
#endif
   boost::math::pow<2>(v1 * 1);
   boost::math::nextafter(v1 * 1, v1 + 0);
   boost::math::float_next(v1 * 1);
   boost::math::float_prior(v1 * 1);
   boost::math::float_distance(v1 * 1, v1 * 1);
   boost::math::ulp(v1 * 1);
   boost::math::relative_difference(v1 * 1, v2 * 1);
   boost::math::epsilon_difference(v1 * 1, v2 * 1);
#endif
#ifndef BOOST_MATH_INSTANTIATE_MINIMUM
#ifdef TEST_GROUP_5
   //
   // All over again, with a policy this time:
   //
   test_policy pol;
   boost::math::tgamma(v1, pol);
   boost::math::tgamma1pm1(v1, pol);
   boost::math::lgamma(v1, pol);
   boost::math::lgamma(v1, &i, pol);
   boost::math::digamma(v1, pol);
   boost::math::trigamma(v1, pol);
   boost::math::polygamma(i, v1, pol);
   boost::math::tgamma_ratio(v1, v2, pol);
   boost::math::tgamma_delta_ratio(v1, v2, pol);
   boost::math::factorial<RealType>(i, pol);
   boost::math::unchecked_factorial<RealType>(i);
   i = boost::math::max_factorial<RealType>::value;
   boost::math::double_factorial<RealType>(i, pol);
   boost::math::rising_factorial(v1, i, pol);
   boost::math::falling_factorial(v1, i, pol);
   boost::math::tgamma(v1, v2, pol);
   boost::math::tgamma_lower(v1, v2, pol);
   boost::math::gamma_p(v1, v2, pol);
   boost::math::gamma_q(v1, v2, pol);
   boost::math::gamma_p_inv(v1, v2, pol);
   boost::math::gamma_q_inv(v1, v2, pol);
   boost::math::gamma_p_inva(v1, v2, pol);
   boost::math::gamma_q_inva(v1, v2, pol);
   boost::math::erf(v1, pol);
   boost::math::erfc(v1, pol);
   boost::math::erf_inv(v1, pol);
   boost::math::erfc_inv(v1, pol);
   boost::math::beta(v1, v2, pol);
   boost::math::beta(v1, v2, v3, pol);
   boost::math::betac(v1, v2, v3, pol);
   boost::math::ibeta(v1, v2, v3, pol);
   boost::math::ibetac(v1, v2, v3, pol);
   boost::math::ibeta_inv(v1, v2, v3, pol);
   boost::math::ibetac_inv(v1, v2, v3, pol);
   boost::math::ibeta_inva(v1, v2, v3, pol);
   boost::math::ibetac_inva(v1, v2, v3, pol);
   boost::math::ibeta_invb(v1, v2, v3, pol);
   boost::math::ibetac_invb(v1, v2, v3, pol);
   boost::math::gamma_p_derivative(v2, v3, pol);
   boost::math::ibeta_derivative(v1, v2, v3, pol);
   boost::math::binomial_coefficient<RealType>(i, i, pol);
   boost::math::log1p(v1, pol);
   boost::math::expm1(v1, pol);
   boost::math::cbrt(v1, pol);
   boost::math::sqrt1pm1(v1, pol);
   boost::math::powm1(v1, v2, pol);
   boost::math::legendre_p(1, v1, pol);
   boost::math::legendre_p(1, 0, v1, pol);
   boost::math::legendre_p_prime(1, v1 * 1, pol);
   boost::math::legendre_q(1, v1, pol);
   boost::math::legendre_next(2, v1, v2, v3);
   boost::math::legendre_next(2, 2, v1, v2, v3);
   boost::math::laguerre(1, v1, pol);
   boost::math::laguerre(2, 1, v1, pol);
   boost::math::laguerre_next(2, v1, v2, v3);
   boost::math::laguerre_next(2, 1, v1, v2, v3);
   boost::math::hermite(1, v1, pol);
   boost::math::hermite_next(2, v1, v2, v3);
   boost::math::spherical_harmonic_r(2, 1, v1, v2, pol);
   boost::math::spherical_harmonic_i(2, 1, v1, v2, pol);
   boost::math::ellint_1(v1, pol);
   boost::math::ellint_1(v1, v2, pol);
   boost::math::ellint_2(v1, pol);
   boost::math::ellint_2(v1, v2, pol);
   boost::math::ellint_3(v1, v2, pol);
   boost::math::ellint_3(v1, v2, v3, pol);
   boost::math::ellint_d(v1, pol);
   boost::math::ellint_d(v1, v2, pol);
   boost::math::jacobi_zeta(v1, v2, pol);
   boost::math::heuman_lambda(v1, v2, pol);
   boost::math::ellint_rc(v1, v2, pol);
   boost::math::ellint_rd(v1, v2, v3, pol);
   boost::math::ellint_rf(v1, v2, v3, pol);
   boost::math::ellint_rg(v1, v2, v3, pol);
   boost::math::ellint_rj(v1, v2, v3, v1, pol);
   boost::math::jacobi_elliptic(v1, v2, &v1, &v2, pol);
   boost::math::jacobi_cd(v1, v2, pol);
   boost::math::jacobi_cn(v1, v2, pol);
   boost::math::jacobi_cs(v1, v2, pol);
   boost::math::jacobi_dc(v1, v2, pol);
   boost::math::jacobi_dn(v1, v2, pol);
   boost::math::jacobi_ds(v1, v2, pol);
   boost::math::jacobi_nc(v1, v2, pol);
   boost::math::jacobi_nd(v1, v2, pol);
   boost::math::jacobi_ns(v1, v2, pol);
   boost::math::jacobi_sc(v1, v2, pol);
   boost::math::jacobi_sd(v1, v2, pol);
   boost::math::jacobi_sn(v1, v2, pol);
   boost::math::hypot(v1, v2, pol);
   boost::math::sinc_pi(v1, pol);
   boost::math::sinhc_pi(v1, pol);
   boost::math::asinh(v1, pol);
   boost::math::acosh(v1, pol);
   boost::math::atanh(v1, pol);
   boost::math::sin_pi(v1, pol);
   boost::math::cos_pi(v1, pol);
   boost::math::cyl_neumann(v1, v2, pol);
   boost::math::cyl_neumann(i, v2, pol);
   boost::math::cyl_bessel_j(v1, v2, pol);
   boost::math::cyl_bessel_j(i, v2, pol);
   boost::math::cyl_bessel_i(v1, v2, pol);
   boost::math::cyl_bessel_i(i, v2, pol);
   boost::math::cyl_bessel_k(v1, v2, pol);
   boost::math::cyl_bessel_k(i, v2, pol);
   boost::math::sph_bessel(i, v2, pol);
   boost::math::sph_bessel(i, 1, pol);
   boost::math::sph_neumann(i, v2, pol);
   boost::math::sph_neumann(i, i, pol);
   boost::math::cyl_neumann_prime(v1, v2, pol);
   boost::math::cyl_neumann_prime(i, v2, pol);
   boost::math::cyl_bessel_j_prime(v1, v2, pol);
   boost::math::cyl_bessel_j_prime(i, v2, pol);
   boost::math::cyl_bessel_i_prime(v1, v2, pol);
   boost::math::cyl_bessel_i_prime(i, v2, pol);
   boost::math::cyl_bessel_k_prime(v1, v2, pol);
   boost::math::cyl_bessel_k_prime(i, v2, pol);
   boost::math::sph_bessel_prime(i, v2, pol);
   boost::math::sph_bessel_prime(i, 1, pol);
   boost::math::sph_neumann_prime(i, v2, pol);
   boost::math::sph_neumann_prime(i, i, pol);
   boost::math::cyl_bessel_j_zero(v1, i, pol);
   boost::math::cyl_bessel_j_zero(v1, i, i, oi, pol);
   boost::math::cyl_neumann_zero(v1, i, pol);
   boost::math::cyl_neumann_zero(v1, i, i, oi, pol);
#ifdef TEST_COMPLEX
   boost::math::cyl_hankel_1(v1, v2, pol);
   boost::math::cyl_hankel_1(i, v2, pol);
   boost::math::cyl_hankel_2(v1, v2, pol);
   boost::math::cyl_hankel_2(i, v2, pol);
   boost::math::sph_hankel_1(v1, v2, pol);
   boost::math::sph_hankel_1(i, v2, pol);
   boost::math::sph_hankel_2(v1, v2, pol);
   boost::math::sph_hankel_2(i, v2, pol);
#endif
   boost::math::airy_ai(v1, pol);
   boost::math::airy_bi(v1, pol);
   boost::math::airy_ai_prime(v1, pol);
   boost::math::airy_bi_prime(v1, pol);

   boost::math::airy_ai_zero<RealType>(i, pol);
   boost::math::airy_bi_zero<RealType>(i, pol);
   boost::math::airy_ai_zero<RealType>(i, i, oi, pol);
   boost::math::airy_bi_zero<RealType>(i, i, oi, pol);

   boost::math::expint(v1, pol);
   boost::math::expint(i, pol);
   boost::math::expint(i, v2, pol);
   boost::math::expint(i, i, pol);
   boost::math::zeta(v1, pol);
   boost::math::zeta(i, pol);
   boost::math::owens_t(v1, v2, pol);
   //
   // These next functions are intended to be found via ADL:
   //
   BOOST_MATH_STD_USING
   trunc(v1, pol);
   itrunc(v1, pol);
   ltrunc(v1, pol);
   round(v1, pol);
   iround(v1, pol);
   lround(v1, pol);
   modf(v1, &v1, pol);
   modf(v1, &i, pol);
   modf(v1, &l, pol);
#ifdef BOOST_HAS_LONG_LONG
   using boost::math::lltrunc;
   using boost::math::llround;
   lltrunc(v1, pol);
   llround(v1, pol);
   modf(v1, &ll, pol);
#endif
   boost::math::pow<2>(v1, pol);
   boost::math::nextafter(v1, v1, pol);
   boost::math::float_next(v1, pol);
   boost::math::float_prior(v1, pol);
   boost::math::float_distance(v1, v1, pol);
   boost::math::ulp(v1, pol);

   boost::math::bernoulli_b2n<RealType>(i, pol);
   boost::math::bernoulli_b2n<RealType>(i, i, &v1, pol);
   boost::math::tangent_t2n<RealType>(i, pol);
   boost::math::tangent_t2n<RealType>(i, i, &v1, pol);
#endif
#ifdef TEST_GROUP_6
   //
   // All over again with the versions in test::
   //
   test::tgamma(v1);
   test::tgamma1pm1(v1);
   test::lgamma(v1);
   test::lgamma(v1, &i);
   test::digamma(v1);
   test::trigamma(v1);
   test::polygamma(i, v1);
   test::tgamma_ratio(v1, v2);
   test::tgamma_delta_ratio(v1, v2);
   test::factorial<RealType>(i);
   test::unchecked_factorial<RealType>(i);
   i = test::max_factorial<RealType>::value;
   test::double_factorial<RealType>(i);
   test::rising_factorial(v1, i);
   test::falling_factorial(v1, i);
   test::tgamma(v1, v2);
   test::tgamma_lower(v1, v2);
   test::gamma_p(v1, v2);
   test::gamma_q(v1, v2);
   test::gamma_p_inv(v1, v2);
   test::gamma_q_inv(v1, v2);
   test::gamma_p_inva(v1, v2);
   test::gamma_q_inva(v1, v2);
   test::erf(v1);
   test::erfc(v1);
   test::erf_inv(v1);
   test::erfc_inv(v1);
   test::beta(v1, v2);
   test::beta(v1, v2, v3);
   test::betac(v1, v2, v3);
   test::ibeta(v1, v2, v3);
   test::ibetac(v1, v2, v3);
   test::ibeta_inv(v1, v2, v3);
   test::ibetac_inv(v1, v2, v3);
   test::ibeta_inva(v1, v2, v3);
   test::ibetac_inva(v1, v2, v3);
   test::ibeta_invb(v1, v2, v3);
   test::ibetac_invb(v1, v2, v3);
   test::gamma_p_derivative(v2, v3);
   test::ibeta_derivative(v1, v2, v3);
   test::binomial_coefficient<RealType>(i, i);
   (test::fpclassify)(v1);
   (test::isfinite)(v1);
   (test::isnormal)(v1);
   (test::isnan)(v1);
   (test::isinf)(v1);
   (test::signbit)(v1);
   (test::copysign)(v1, v2);
   (test::changesign)(v1);
   (test::sign)(v1);
   test::log1p(v1);
   test::expm1(v1);
   test::cbrt(v1);
   test::sqrt1pm1(v1);
   test::powm1(v1, v2);
   test::legendre_p(1, v1);
   test::legendre_p(1, 0, v1);
   test::legendre_p_prime(1, v1 * 1);
   test::legendre_q(1, v1);
   test::legendre_next(2, v1, v2, v3);
   test::legendre_next(2, 2, v1, v2, v3);
   test::laguerre(1, v1);
   test::laguerre(2, 1, v1);
   test::laguerre_next(2, v1, v2, v3);
   test::laguerre_next(2, 1, v1, v2, v3);
   test::hermite(1, v1);
   test::hermite_next(2, v1, v2, v3);
   test::spherical_harmonic_r(2, 1, v1, v2);
   test::spherical_harmonic_i(2, 1, v1, v2);
   test::ellint_1(v1);
   test::ellint_1(v1, v2);
   test::ellint_2(v1);
   test::ellint_2(v1, v2);
   test::ellint_3(v1, v2);
   test::ellint_3(v1, v2, v3);
   test::ellint_d(v1);
   test::ellint_d(v1, v2);
   test::jacobi_zeta(v1, v2);
   test::heuman_lambda(v1, v2);
   test::ellint_rc(v1, v2);
   test::ellint_rd(v1, v2, v3);
   test::ellint_rf(v1, v2, v3);
   test::ellint_rg(v1, v2, v3);
   test::ellint_rj(v1, v2, v3, v1);
   test::jacobi_elliptic(v1, v2, &v1, &v2);
   test::jacobi_cd(v1, v2);
   test::jacobi_cn(v1, v2);
   test::jacobi_cs(v1, v2);
   test::jacobi_dc(v1, v2);
   test::jacobi_dn(v1, v2);
   test::jacobi_ds(v1, v2);
   test::jacobi_nc(v1, v2);
   test::jacobi_nd(v1, v2);
   test::jacobi_ns(v1, v2);
   test::jacobi_sc(v1, v2);
   test::jacobi_sd(v1, v2);
   test::jacobi_sn(v1, v2);
   test::hypot(v1, v2);
   test::sinc_pi(v1);
   test::sinhc_pi(v1);
   test::asinh(v1);
   test::acosh(v1);
   test::atanh(v1);
   test::sin_pi(v1);
   test::cos_pi(v1);
   test::cyl_neumann(v1, v2);
   test::cyl_neumann(i, v2);
   test::cyl_bessel_j(v1, v2);
   test::cyl_bessel_j(i, v2);
   test::cyl_bessel_i(v1, v2);
   test::cyl_bessel_i(i, v2);
   test::cyl_bessel_k(v1, v2);
   test::cyl_bessel_k(i, v2);
   test::sph_bessel(i, v2);
   test::sph_bessel(i, 1);
   test::sph_neumann(i, v2);
   test::sph_neumann(i, i);
   test::cyl_neumann_prime(v1, v2);
   test::cyl_neumann_prime(i, v2);
   test::cyl_bessel_j_prime(v1, v2);
   test::cyl_bessel_j_prime(i, v2);
   test::cyl_bessel_i_prime(v1, v2);
   test::cyl_bessel_i_prime(i, v2);
   test::cyl_bessel_k_prime(v1, v2);
   test::cyl_bessel_k_prime(i, v2);
   test::sph_bessel_prime(i, v2);
   test::sph_bessel_prime(i, 1);
   test::sph_neumann_prime(i, v2);
   test::sph_neumann_prime(i, i);
   test::cyl_bessel_j_zero(v1, i);
   test::cyl_bessel_j_zero(v1, i, i, oi);
   test::cyl_neumann_zero(v1, i);
   test::cyl_neumann_zero(v1, i, i, oi);
#ifdef TEST_COMPLEX
   test::cyl_hankel_1(v1, v2);
   test::cyl_hankel_1(i, v2);
   test::cyl_hankel_2(v1, v2);
   test::cyl_hankel_2(i, v2);
   test::sph_hankel_1(v1, v2);
   test::sph_hankel_1(i, v2);
   test::sph_hankel_2(v1, v2);
   test::sph_hankel_2(i, v2);
#endif
   test::airy_ai(i);
   test::airy_bi(i);
   test::airy_ai_prime(i);
   test::airy_bi_prime(i);

   test::airy_ai_zero<RealType>(i);
   test::airy_bi_zero<RealType>(i);
   test::airy_ai_zero<RealType>(i, i, oi);
   test::airy_bi_zero<RealType>(i, i, oi);

   test::expint(v1);
   test::expint(i);
   test::expint(i, v2);
   test::expint(i, i);
   test::zeta(v1);
   test::zeta(i);
   test::owens_t(v1, v2);
   test::trunc(v1);
   test::itrunc(v1);
   test::ltrunc(v1);
   test::round(v1);
   test::iround(v1);
   test::lround(v1);
   test::modf(v1, &v1);
   test::modf(v1, &i);
   test::modf(v1, &l);
#ifdef BOOST_HAS_LONG_LONG
   test::lltrunc(v1);
   test::llround(v1);
   test::modf(v1, &ll);
#endif
   test::pow<2>(v1);
   test::nextafter(v1, v1);
   test::float_next(v1);
   test::float_prior(v1);
   test::float_distance(v1, v1);
   test::ulp(v1);
#endif
#endif
}

template <class RealType>
void instantiate_mixed(RealType)
{
   using namespace boost;
   using namespace boost::math;
#ifndef BOOST_MATH_INSTANTIATE_MINIMUM
   int i = 1;
   (void)i;
   long l = 1;
   (void)l;
   short s = 1;
   (void)s;
   float fr = 0.5F;
   (void)fr;
   double dr = 0.5;
   (void)dr;
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   long double lr = 0.5L;
   (void)lr;
#else
   double lr = 0.5L;
   (void)lr;
#endif
#ifdef TEST_GROUP_7
   boost::math::tgamma(i);
   boost::math::tgamma1pm1(i);
   boost::math::lgamma(i);
   boost::math::lgamma(i, &i);
   boost::math::digamma(i);
   boost::math::trigamma(i);
   boost::math::polygamma(i, i);
   boost::math::tgamma_ratio(i, l);
   boost::math::tgamma_ratio(fr, lr);
   boost::math::tgamma_delta_ratio(i, s);
   boost::math::tgamma_delta_ratio(fr, lr);
   boost::math::rising_factorial(s, i);
   boost::math::falling_factorial(s, i);
   boost::math::tgamma(i, l);
   boost::math::tgamma(fr, lr);
   boost::math::tgamma_lower(i, s);
   boost::math::tgamma_lower(fr, lr);
   boost::math::gamma_p(i, s);
   boost::math::gamma_p(fr, lr);
   boost::math::gamma_q(i, s);
   boost::math::gamma_q(fr, lr);
   boost::math::gamma_p_inv(i, fr);
   boost::math::gamma_q_inv(s, fr);
   boost::math::gamma_p_inva(i, lr);
   boost::math::gamma_q_inva(i, lr);
   boost::math::erf(i);
   boost::math::erfc(i);
   boost::math::erf_inv(i);
   boost::math::erfc_inv(i);
   boost::math::beta(i, s);
   boost::math::beta(fr, lr);
   boost::math::beta(i, s, l);
   boost::math::beta(fr, dr, lr);
   boost::math::betac(l, i, s);
   boost::math::betac(fr, dr, lr);
   boost::math::ibeta(l, i, s);
   boost::math::ibeta(fr, dr, lr);
   boost::math::ibetac(l, i, s);
   boost::math::ibetac(fr, dr, lr);
   boost::math::ibeta_inv(l, s, i);
   boost::math::ibeta_inv(fr, dr, lr);
   boost::math::ibetac_inv(l, i, s);
   boost::math::ibetac_inv(fr, dr, lr);
   boost::math::ibeta_inva(l, i, s);
   boost::math::ibeta_inva(fr, dr, lr);
   boost::math::ibetac_inva(l, i, s);
   boost::math::ibetac_inva(fr, dr, lr);
   boost::math::ibeta_invb(l, i, s);
   boost::math::ibeta_invb(fr, dr, lr);
   boost::math::ibetac_invb(l, i, s);
   boost::math::ibetac_invb(fr, dr, lr);
   boost::math::gamma_p_derivative(i, l);
   boost::math::gamma_p_derivative(fr, lr);
   boost::math::ibeta_derivative(l, i, s);
   boost::math::ibeta_derivative(fr, dr, lr);
   (boost::math::fpclassify)(i);
   (boost::math::isfinite)(s);
   (boost::math::isnormal)(l);
   (boost::math::isnan)(i);
   (boost::math::isinf)(l);
   boost::math::log1p(i);
   boost::math::expm1(s);
   boost::math::cbrt(l);
   boost::math::sqrt1pm1(s);
   boost::math::powm1(i, s);
   boost::math::powm1(fr, lr);
   //boost::math::legendre_p(1, i);
   boost::math::legendre_p(1, 0, s);
   boost::math::legendre_q(1, i);
   boost::math::laguerre(1, i);
   boost::math::laguerre(2, 1, i);
   boost::math::laguerre(2u, 1u, s);
   boost::math::hermite(1, s);
   boost::math::spherical_harmonic_r(2, 1, s, i);
   boost::math::spherical_harmonic_i(2, 1, fr, lr);
   boost::math::ellint_1(i);
   boost::math::ellint_1(i, s);
   boost::math::ellint_1(fr, lr);
   boost::math::ellint_2(i);
   boost::math::ellint_2(i, l);
   boost::math::ellint_2(fr, lr);
   boost::math::ellint_3(i, l);
   boost::math::ellint_3(fr, lr);
   boost::math::ellint_3(s, l, i);
   boost::math::ellint_3(fr, dr, lr);
   boost::math::ellint_d(i);
   boost::math::ellint_d(i, l);
   boost::math::ellint_d(fr, lr);
   boost::math::jacobi_zeta(i, l);
   boost::math::jacobi_zeta(fr, lr);
   boost::math::heuman_lambda(i, l);
   boost::math::heuman_lambda(fr, lr);
   boost::math::ellint_rc(i, s);
   boost::math::ellint_rc(fr, lr);
   boost::math::ellint_rd(s, i, l);
   boost::math::ellint_rd(fr, lr, dr);
   boost::math::ellint_rf(s, l, i);
   boost::math::ellint_rf(fr, dr, lr);
   boost::math::ellint_rg(s, l, i);
   boost::math::ellint_rg(fr, dr, lr);
   boost::math::ellint_rj(i, i, s, l);
   boost::math::ellint_rj(i, fr, dr, lr);
   boost::math::jacobi_cd(i, fr);
   boost::math::jacobi_cn(i, fr);
   boost::math::jacobi_cs(i, fr);
   boost::math::jacobi_dc(i, fr);
   boost::math::jacobi_dn(i, fr);
   boost::math::jacobi_ds(i, fr);
   boost::math::jacobi_nc(i, fr);
   boost::math::jacobi_nd(i, fr);
   boost::math::jacobi_ns(i, fr);
   boost::math::jacobi_sc(i, fr);
   boost::math::jacobi_sd(i, fr);
   boost::math::jacobi_sn(i, fr);
   boost::math::hypot(i, s);
   boost::math::hypot(fr, lr);
   boost::math::sinc_pi(i);
   boost::math::sinhc_pi(i);
   boost::math::asinh(s);
   boost::math::acosh(l);
   boost::math::atanh(l);
   boost::math::sin_pi(s);
   boost::math::cos_pi(s);
   boost::math::cyl_neumann(fr, dr);
   boost::math::cyl_neumann(i, s);
   boost::math::cyl_bessel_j(fr, lr);
   boost::math::cyl_bessel_j(i, s);
   boost::math::cyl_bessel_i(fr, lr);
   boost::math::cyl_bessel_i(i, s);
   boost::math::cyl_bessel_k(fr, lr);
   boost::math::cyl_bessel_k(i, s);
   boost::math::sph_bessel(i, fr);
   boost::math::sph_bessel(i, 1);
   boost::math::sph_neumann(i, lr);
   boost::math::sph_neumann(i, i);
   boost::math::cyl_neumann_prime(fr, dr);
   boost::math::cyl_neumann_prime(i, s);
   boost::math::cyl_bessel_j_prime(fr, lr);
   boost::math::cyl_bessel_j_prime(i, s);
   boost::math::cyl_bessel_i_prime(fr, lr);
   boost::math::cyl_bessel_i_prime(i, s);
   boost::math::cyl_bessel_k_prime(fr, lr);
   boost::math::cyl_bessel_k_prime(i, s);
   boost::math::sph_bessel_prime(i, fr);
   boost::math::sph_bessel_prime(i, 1);
   boost::math::sph_neumann_prime(i, lr);
   boost::math::sph_neumann_prime(i, i);
   boost::math::owens_t(fr, dr);
   boost::math::owens_t(i, s);

   boost::math::policies::policy<> pol;

   boost::math::tgamma(i, pol);
   boost::math::tgamma1pm1(i, pol);
   boost::math::lgamma(i, pol);
   boost::math::lgamma(i, &i, pol);
   boost::math::digamma(i, pol);
   boost::math::trigamma(i, pol);
   boost::math::polygamma(i, i, pol);
   boost::math::tgamma_ratio(i, l, pol);
   boost::math::tgamma_ratio(fr, lr, pol);
   boost::math::tgamma_delta_ratio(i, s, pol);
   boost::math::tgamma_delta_ratio(fr, lr, pol);
   boost::math::rising_factorial(s, i, pol);
   boost::math::falling_factorial(s, i, pol);
   boost::math::tgamma(i, l, pol);
   boost::math::tgamma(fr, lr, pol);
   boost::math::tgamma_lower(i, s, pol);
   boost::math::tgamma_lower(fr, lr, pol);
   boost::math::gamma_p(i, s, pol);
   boost::math::gamma_p(fr, lr, pol);
   boost::math::gamma_q(i, s, pol);
   boost::math::gamma_q(fr, lr, pol);
   boost::math::gamma_p_inv(i, fr, pol);
   boost::math::gamma_q_inv(s, fr, pol);
   boost::math::gamma_p_inva(i, lr, pol);
   boost::math::gamma_q_inva(i, lr, pol);
   boost::math::erf(i, pol);
   boost::math::erfc(i, pol);
   boost::math::erf_inv(i, pol);
   boost::math::erfc_inv(i, pol);
   boost::math::beta(i, s, pol);
   boost::math::beta(fr, lr, pol);
   boost::math::beta(i, s, l, pol);
   boost::math::beta(fr, dr, lr, pol);
   boost::math::betac(l, i, s, pol);
   boost::math::betac(fr, dr, lr, pol);
   boost::math::ibeta(l, i, s, pol);
   boost::math::ibeta(fr, dr, lr, pol);
   boost::math::ibetac(l, i, s, pol);
   boost::math::ibetac(fr, dr, lr, pol);
   boost::math::ibeta_inv(l, s, i, pol);
   boost::math::ibeta_inv(fr, dr, lr, pol);
   boost::math::ibetac_inv(l, i, s, pol);
   boost::math::ibetac_inv(fr, dr, lr, pol);
   boost::math::ibeta_inva(l, i, s, pol);
   boost::math::ibeta_inva(fr, dr, lr, pol);
   boost::math::ibetac_inva(l, i, s, pol);
   boost::math::ibetac_inva(fr, dr, lr, pol);
   boost::math::ibeta_invb(l, i, s, pol);
   boost::math::ibeta_invb(fr, dr, lr, pol);
   boost::math::ibetac_invb(l, i, s, pol);
   boost::math::ibetac_invb(fr, dr, lr, pol);
   boost::math::gamma_p_derivative(i, l, pol);
   boost::math::gamma_p_derivative(fr, lr, pol);
   boost::math::ibeta_derivative(l, i, s, pol);
   boost::math::ibeta_derivative(fr, dr, lr, pol);
   boost::math::log1p(i, pol);
   boost::math::expm1(s, pol);
   boost::math::cbrt(l, pol);
   boost::math::sqrt1pm1(s, pol);
   boost::math::powm1(i, s, pol);
   boost::math::powm1(fr, lr, pol);
   //boost::math::legendre_p(1, i, pol);
   boost::math::legendre_p(1, 0, s, pol);
   boost::math::legendre_q(1, i, pol);
   boost::math::laguerre(1, i, pol);
   boost::math::laguerre(2, 1, i, pol);
   boost::math::laguerre(2u, 1u, s, pol);
   boost::math::hermite(1, s, pol);
   boost::math::spherical_harmonic_r(2, 1, s, i, pol);
   boost::math::spherical_harmonic_i(2, 1, fr, lr, pol);
   boost::math::ellint_1(i, pol);
   boost::math::ellint_1(i, s, pol);
   boost::math::ellint_1(fr, lr, pol);
   boost::math::ellint_2(i, pol);
   boost::math::ellint_2(i, l, pol);
   boost::math::ellint_2(fr, lr, pol);
   boost::math::ellint_3(i, l, pol);
   boost::math::ellint_3(fr, lr, pol);
   boost::math::ellint_3(s, l, i, pol);
   boost::math::ellint_3(fr, dr, lr, pol);
   boost::math::ellint_d(i, pol);
   boost::math::ellint_d(i, l, pol);
   boost::math::ellint_d(fr, lr, pol);
   boost::math::jacobi_zeta(i, l, pol);
   boost::math::jacobi_zeta(fr, lr, pol);
   boost::math::heuman_lambda(i, l, pol);
   boost::math::heuman_lambda(fr, lr, pol);
   boost::math::ellint_rc(i, s, pol);
   boost::math::ellint_rc(fr, lr, pol);
   boost::math::ellint_rd(s, i, l, pol);
   boost::math::ellint_rd(fr, lr, dr, pol);
   boost::math::ellint_rf(s, l, i, pol);
   boost::math::ellint_rf(fr, dr, lr, pol);
   boost::math::ellint_rg(s, l, i, pol);
   boost::math::ellint_rg(fr, dr, lr, pol);
   boost::math::ellint_rj(i, i, s, l, pol);
   boost::math::ellint_rj(i, fr, dr, lr, pol);
   boost::math::jacobi_cd(i, fr, pol);
   boost::math::jacobi_cn(i, fr, pol);
   boost::math::jacobi_cs(i, fr, pol);
   boost::math::jacobi_dc(i, fr, pol);
   boost::math::jacobi_dn(i, fr, pol);
   boost::math::jacobi_ds(i, fr, pol);
   boost::math::jacobi_nc(i, fr, pol);
   boost::math::jacobi_nd(i, fr, pol);
   boost::math::jacobi_ns(i, fr, pol);
   boost::math::jacobi_sc(i, fr, pol);
   boost::math::jacobi_sd(i, fr, pol);
   boost::math::jacobi_sn(i, fr, pol);
   boost::math::hypot(i, s, pol);
   boost::math::hypot(fr, lr, pol);
   boost::math::sinc_pi(i, pol);
   boost::math::sinhc_pi(i, pol);
   boost::math::asinh(s, pol);
   boost::math::acosh(l, pol);
   boost::math::atanh(l, pol);
   boost::math::sin_pi(s, pol);
   boost::math::cos_pi(s, pol);
   boost::math::cyl_neumann(fr, dr, pol);
   boost::math::cyl_neumann(i, s, pol);
   boost::math::cyl_bessel_j(fr, lr, pol);
   boost::math::cyl_bessel_j(i, s, pol);
   boost::math::cyl_bessel_i(fr, lr, pol);
   boost::math::cyl_bessel_i(i, s, pol);
   boost::math::cyl_bessel_k(fr, lr, pol);
   boost::math::cyl_bessel_k(i, s, pol);
   boost::math::sph_bessel(i, fr, pol);
   boost::math::sph_bessel(i, 1, pol);
   boost::math::sph_neumann(i, lr, pol);
   boost::math::sph_neumann(i, i, pol);
   boost::math::cyl_neumann_prime(fr, dr, pol);
   boost::math::cyl_neumann_prime(i, s, pol);
   boost::math::cyl_bessel_j_prime(fr, lr, pol);
   boost::math::cyl_bessel_j_prime(i, s, pol);
   boost::math::cyl_bessel_i_prime(fr, lr, pol);
   boost::math::cyl_bessel_i_prime(i, s, pol);
   boost::math::cyl_bessel_k_prime(fr, lr, pol);
   boost::math::cyl_bessel_k_prime(i, s, pol);
   boost::math::sph_bessel_prime(i, fr, pol);
   boost::math::sph_bessel_prime(i, 1, pol);
   boost::math::sph_neumann_prime(i, lr, pol);
   boost::math::sph_neumann_prime(i, i, pol);
   boost::math::owens_t(fr, dr, pol);
   boost::math::owens_t(i, s, pol);
#endif
#ifdef TEST_GROUP_8
   test::tgamma(i);
   test::tgamma1pm1(i);
   test::lgamma(i);
   test::lgamma(i, &i);
   test::digamma(i);
   test::trigamma(i);
   test::polygamma(i, i);
   test::tgamma_ratio(i, l);
   test::tgamma_ratio(fr, lr);
   test::tgamma_delta_ratio(i, s);
   test::tgamma_delta_ratio(fr, lr);
   test::rising_factorial(s, i);
   test::falling_factorial(s, i);
   test::tgamma(i, l);
   test::tgamma(fr, lr);
   test::tgamma_lower(i, s);
   test::tgamma_lower(fr, lr);
   test::gamma_p(i, s);
   test::gamma_p(fr, lr);
   test::gamma_q(i, s);
   test::gamma_q(fr, lr);
   test::gamma_p_inv(i, fr);
   test::gamma_q_inv(s, fr);
   test::gamma_p_inva(i, lr);
   test::gamma_q_inva(i, lr);
   test::erf(i);
   test::erfc(i);
   test::erf_inv(i);
   test::erfc_inv(i);
   test::beta(i, s);
   test::beta(fr, lr);
   test::beta(i, s, l);
   test::beta(fr, dr, lr);
   test::betac(l, i, s);
   test::betac(fr, dr, lr);
   test::ibeta(l, i, s);
   test::ibeta(fr, dr, lr);
   test::ibetac(l, i, s);
   test::ibetac(fr, dr, lr);
   test::ibeta_inv(l, s, i);
   test::ibeta_inv(fr, dr, lr);
   test::ibetac_inv(l, i, s);
   test::ibetac_inv(fr, dr, lr);
   test::ibeta_inva(l, i, s);
   test::ibeta_inva(fr, dr, lr);
   test::ibetac_inva(l, i, s);
   test::ibetac_inva(fr, dr, lr);
   test::ibeta_invb(l, i, s);
   test::ibeta_invb(fr, dr, lr);
   test::ibetac_invb(l, i, s);
   test::ibetac_invb(fr, dr, lr);
   test::gamma_p_derivative(i, l);
   test::gamma_p_derivative(fr, lr);
   test::ibeta_derivative(l, i, s);
   test::ibeta_derivative(fr, dr, lr);
   (test::fpclassify)(i);
   (test::isfinite)(s);
   (test::isnormal)(l);
   (test::isnan)(i);
   (test::isinf)(l);
   test::log1p(i);
   test::expm1(s);
   test::cbrt(l);
   test::sqrt1pm1(s);
   test::powm1(i, s);
   test::powm1(fr, lr);
   //test::legendre_p(1, i);
   test::legendre_p(1, 0, s);
   test::legendre_q(1, i);
   test::laguerre(1, i);
   test::laguerre(2, 1, i);
   test::laguerre(2u, 1u, s);
   test::hermite(1, s);
   test::spherical_harmonic_r(2, 1, s, i);
   test::spherical_harmonic_i(2, 1, fr, lr);
   test::ellint_1(i);
   test::ellint_1(i, s);
   test::ellint_1(fr, lr);
   test::ellint_2(i);
   test::ellint_2(i, l);
   test::ellint_2(fr, lr);
   test::ellint_3(i, l);
   test::ellint_3(fr, lr);
   test::ellint_3(s, l, i);
   test::ellint_3(fr, dr, lr);
   test::ellint_d(i);
   test::ellint_d(i, l);
   test::ellint_d(fr, lr);
   test::jacobi_zeta(i, l);
   test::jacobi_zeta(fr, lr);
   test::heuman_lambda(i, l);
   test::heuman_lambda(fr, lr);
   test::ellint_rc(i, s);
   test::ellint_rc(fr, lr);
   test::ellint_rd(s, i, l);
   test::ellint_rd(fr, lr, dr);
   test::ellint_rf(s, l, i);
   test::ellint_rf(fr, dr, lr);
   test::ellint_rg(s, l, i);
   test::ellint_rg(fr, dr, lr);
   test::ellint_rj(i, i, s, l);
   test::ellint_rj(i, fr, dr, lr);
   test::hypot(i, s);
   test::hypot(fr, lr);
   test::sinc_pi(i);
   test::sinhc_pi(i);
   test::asinh(s);
   test::acosh(l);
   test::atanh(l);
   test::sin_pi(s);
   test::cos_pi(s);
   test::cyl_neumann(fr, dr);
   test::cyl_neumann(i, s);
   test::cyl_bessel_j(fr, lr);
   test::cyl_bessel_j(i, s);
   test::cyl_bessel_i(fr, lr);
   test::cyl_bessel_i(i, s);
   test::cyl_bessel_k(fr, lr);
   test::cyl_bessel_k(i, s);
   test::sph_bessel(i, fr);
   test::sph_bessel(i, 1);
   test::sph_neumann(i, lr);
   test::sph_neumann(i, i);
   test::cyl_neumann_prime(fr, dr);
   test::cyl_neumann_prime(i, s);
   test::cyl_bessel_j_prime(fr, lr);
   test::cyl_bessel_j_prime(i, s);
   test::cyl_bessel_i_prime(fr, lr);
   test::cyl_bessel_i_prime(i, s);
   test::cyl_bessel_k_prime(fr, lr);
   test::cyl_bessel_k_prime(i, s);
   test::sph_bessel_prime(i, fr);
   test::sph_bessel_prime(i, 1);
   test::sph_neumann_prime(i, lr);
   test::sph_neumann_prime(i, i);
   test::airy_ai(i);
   test::airy_bi(i);
   test::airy_ai_prime(i);
   test::airy_bi_prime(i);
   test::owens_t(fr, dr);
   test::owens_t(i, s);
#endif
#endif
}

#endif // BOOST_LIBS_MATH_TEST_INSTANTIATE_HPP


/*=============================================================================
    Copyright (c) 2001-2007 Joel de Guzman
    Copyright (c) 2001-2009 Hartmut Kaiser

    Use, modification and distribution is subject to 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 <climits>
#include <boost/math/concepts/real_concept.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/spirit/include/qi_char.hpp>
#include <boost/spirit/include/qi_numeric.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#include <boost/spirit/home/support/detail/math/signbit.hpp>

#include "test.hpp"
using namespace spirit_test;

///////////////////////////////////////////////////////////////////////////////
//  These policies can be used to parse thousand separated
//  numbers with at most 2 decimal digits after the decimal
//  point. e.g. 123,456,789.01
///////////////////////////////////////////////////////////////////////////////
template <typename T>
struct ts_real_policies : boost::spirit::qi::ureal_policies<T>
{
    //  2 decimal places Max
    template <typename Iterator, typename Attribute>
    static bool
    parse_frac_n(Iterator& first, Iterator const& last, Attribute& attr)
    {
        return boost::spirit::qi::
            extract_uint<T, 10, 1, 2, true>::call(first, last, attr);
    }

    //  No exponent
    template <typename Iterator>
    static bool
    parse_exp(Iterator&, Iterator const&)
    {
        return false;
    }

    //  No exponent
    template <typename Iterator, typename Attribute>
    static bool
    parse_exp_n(Iterator&, Iterator const&, Attribute&)
    {
        return false;
    }

    //  Thousands separated numbers
    template <typename Iterator, typename Attribute>
    static bool
    parse_n(Iterator& first, Iterator const& last, Attribute& attr)
    {
        using namespace boost::spirit::qi;
        using namespace boost::spirit;

        uint_spec<unsigned, 10, 1, 3> uint3;
        uint_spec<unsigned, 10, 3, 3> uint3_3;

        T result = 0;
        if (parse(first, last, uint3, result))
        {
            bool hit = false;
            T n;
            Iterator save = first;

            while (parse(first, last, ',') && parse(first, last, uint3_3, n))
            {
                result = result * 1000 + n;
                save = first;
                hit = true;
            }

            first = save;
            if (hit)
                attr = result;
            return hit;
        }
        return false;
    }
};

template <typename T>
struct no_trailing_dot_policy : boost::spirit::qi::real_policies<T>
{
    static bool const allow_trailing_dot = false;
};

template <typename T>
struct no_leading_dot_policy : boost::spirit::qi::real_policies<T>
{
    static bool const allow_leading_dot = false;
};

template <typename T>
bool
compare(T n, double expected)
{
    double const eps = 0.00001;
    T delta = n - expected;
    return (delta >= -eps) && (delta <= eps);
}

int
main()
{
    ///////////////////////////////////////////////////////////////////////////////
    //  thousand separated numbers
    ///////////////////////////////////////////////////////////////////////////////
    {
        using boost::spirit::qi::uint_spec;
        using boost::spirit::qi::parse;

        uint_spec<unsigned, 10, 1, 3> uint3;
        uint_spec<unsigned, 10, 3, 3> uint3_3;

    #define r (uint3 >> *(',' >> uint3_3))

        BOOST_TEST(test("1,234,567,890", r));
        BOOST_TEST(test("12,345,678,900", r));
        BOOST_TEST(test("123,456,789,000", r));
        BOOST_TEST(!test("1000,234,567,890", r));
        BOOST_TEST(!test("1,234,56,890", r));
        BOOST_TEST(!test("1,66", r));
    }

    ///////////////////////////////////////////////////////////////////////////////
    //  unsigned real number tests
    ///////////////////////////////////////////////////////////////////////////////
    {
        using boost::spirit::qi::real_spec;
        using boost::spirit::qi::parse;
        using boost::spirit::qi::ureal_policies;

        real_spec<double, ureal_policies<double> > udouble;
        double  d;

        BOOST_TEST(test("1234", udouble));
        BOOST_TEST(test_attr("1234", udouble, d) && compare(d, 1234));

        BOOST_TEST(test("1.2e3", udouble));
        BOOST_TEST(test_attr("1.2e3", udouble, d) && compare(d, 1.2e3));

        BOOST_TEST(test("1.2e-3", udouble));
        BOOST_TEST(test_attr("1.2e-3", udouble, d) && compare(d, 1.2e-3));

        BOOST_TEST(test("1.e2", udouble));
        BOOST_TEST(test_attr("1.e2", udouble, d) && compare(d, 1.e2));

        BOOST_TEST(test("1.", udouble));
        BOOST_TEST(test_attr("1.", udouble, d) && compare(d, 1.));

        BOOST_TEST(test(".2e3", udouble));
        BOOST_TEST(test_attr(".2e3", udouble, d) && compare(d, .2e3));

        BOOST_TEST(test("2e3", udouble));
        BOOST_TEST(test_attr("2e3", udouble, d) && compare(d, 2e3));

        BOOST_TEST(test("2", udouble));
        BOOST_TEST(test_attr("2", udouble, d) && compare(d, 2));

        using boost::math::fpclassify;
        BOOST_TEST(test("inf", udouble));
        BOOST_TEST(test("infinity", udouble));
        BOOST_TEST(test("INF", udouble));
        BOOST_TEST(test("INFINITY", udouble));
        BOOST_TEST(test_attr("inf", udouble, d) && FP_INFINITE == fpclassify(d));
        BOOST_TEST(test_attr("INF", udouble, d) && FP_INFINITE == fpclassify(d));
        BOOST_TEST(test_attr("infinity", udouble, d) && FP_INFINITE == fpclassify(d));
        BOOST_TEST(test_attr("INFINITY", udouble, d) && FP_INFINITE == fpclassify(d));

        BOOST_TEST(test("nan", udouble));
        BOOST_TEST(test_attr("nan", udouble, d) && FP_NAN == fpclassify(d));
        BOOST_TEST(test("NAN", udouble));
        BOOST_TEST(test_attr("NAN", udouble, d) && FP_NAN == fpclassify(d));

        BOOST_TEST(test("nan(...)", udouble));
        BOOST_TEST(test_attr("nan(...)", udouble, d) && FP_NAN == fpclassify(d));
        BOOST_TEST(test("NAN(...)", udouble));
        BOOST_TEST(test_attr("NAN(...)", udouble, d) && FP_NAN == fpclassify(d));

        BOOST_TEST(!test("e3", udouble));
        BOOST_TEST(!test_attr("e3", udouble, d));

        BOOST_TEST(!test("-1.2e3", udouble));
        BOOST_TEST(!test_attr("-1.2e3", udouble, d));

        BOOST_TEST(!test("+1.2e3", udouble));
        BOOST_TEST(!test_attr("+1.2e3", udouble, d));

        BOOST_TEST(!test("1.2e", udouble));
        BOOST_TEST(!test_attr("1.2e", udouble, d));

        BOOST_TEST(!test("-.3", udouble));
        BOOST_TEST(!test_attr("-.3", udouble, d));
    }

///////////////////////////////////////////////////////////////////////////////
//  signed real number tests
///////////////////////////////////////////////////////////////////////////////
    {
        using boost::spirit::double_;
        using boost::spirit::qi::parse;
        double  d;

        BOOST_TEST(test("-1234", double_));
        BOOST_TEST(test_attr("-1234", double_, d) && compare(d, -1234));

        BOOST_TEST(test("-1.2e3", double_));
        BOOST_TEST(test_attr("-1.2e3", double_, d) && compare(d, -1.2e3));

        BOOST_TEST(test("+1.2e3", double_));
        BOOST_TEST(test_attr("+1.2e3", double_, d) && compare(d, 1.2e3));

        BOOST_TEST(test("-0.1", double_));
        BOOST_TEST(test_attr("-0.1", double_, d) && compare(d, -0.1));

        BOOST_TEST(test("-1.2e-3", double_));
        BOOST_TEST(test_attr("-1.2e-3", double_, d) && compare(d, -1.2e-3));

        BOOST_TEST(test("-1.e2", double_));
        BOOST_TEST(test_attr("-1.e2", double_, d) && compare(d, -1.e2));

        BOOST_TEST(test("-.2e3", double_));
        BOOST_TEST(test_attr("-.2e3", double_, d) && compare(d, -.2e3));

        BOOST_TEST(test("-2e3", double_));
        BOOST_TEST(test_attr("-2e3", double_, d) && compare(d, -2e3));

        BOOST_TEST(!test("-e3", double_));
        BOOST_TEST(!test_attr("-e3", double_, d));

        BOOST_TEST(!test("-1.2e", double_));
        BOOST_TEST(!test_attr("-1.2e", double_, d));

        using boost::spirit::math::fpclassify;
        using boost::spirit::math::signbit;
        BOOST_TEST(test("-inf", double_));
        BOOST_TEST(test("-infinity", double_));
        BOOST_TEST(test_attr("-inf", double_, d) &&
            FP_INFINITE == fpclassify(d) && signbit(d));
        BOOST_TEST(test_attr("-infinity", double_, d) &&
            FP_INFINITE == fpclassify(d) && signbit(d));
        BOOST_TEST(test("-INF", double_));
        BOOST_TEST(test("-INFINITY", double_));
        BOOST_TEST(test_attr("-INF", double_, d) &&
            FP_INFINITE == fpclassify(d) && signbit(d));
        BOOST_TEST(test_attr("-INFINITY", double_, d) &&
            FP_INFINITE == fpclassify(d) && signbit(d));

        BOOST_TEST(test("-nan", double_));
        BOOST_TEST(test_attr("-nan", double_, d) &&
            FP_NAN == fpclassify(d) && signbit(d));
        BOOST_TEST(test("-NAN", double_));
        BOOST_TEST(test_attr("-NAN", double_, d) &&
            FP_NAN == fpclassify(d) && signbit(d));

        BOOST_TEST(test("-nan(...)", double_));
        BOOST_TEST(test_attr("-nan(...)", double_, d) &&
            FP_NAN == fpclassify(d) && signbit(d));
        BOOST_TEST(test("-NAN(...)", double_));
        BOOST_TEST(test_attr("-NAN(...)", double_, d) &&
            FP_NAN == fpclassify(d) && signbit(d));
    }

    ///////////////////////////////////////////////////////////////////////////////
    //  strict real number tests
    ///////////////////////////////////////////////////////////////////////////////
    {
        using boost::spirit::qi::real_spec;
        using boost::spirit::qi::parse;
        using boost::spirit::qi::strict_ureal_policies;
        using boost::spirit::qi::strict_real_policies;

        real_spec<double, strict_ureal_policies<double> > strict_udouble;
        real_spec<double, strict_real_policies<double> > strict_double;
        double  d;

        BOOST_TEST(!test("1234", strict_udouble));
        BOOST_TEST(!test_attr("1234", strict_udouble, d));

        BOOST_TEST(test("1.2", strict_udouble));
        BOOST_TEST(test_attr("1.2", strict_udouble, d) && compare(d, 1.2));

        BOOST_TEST(!test("-1234", strict_double));
        BOOST_TEST(!test_attr("-1234", strict_double, d));

        BOOST_TEST(test("123.", strict_double));
        BOOST_TEST(test_attr("123.", strict_double, d) && compare(d, 123));

        BOOST_TEST(test("3.E6", strict_double));
        BOOST_TEST(test_attr("3.E6", strict_double, d) && compare(d, 3e6));

        real_spec<double, no_trailing_dot_policy<double> > notrdot_real;
        real_spec<double, no_leading_dot_policy<double> > nolddot_real;

        BOOST_TEST(!test("1234.", notrdot_real));          //  Bad trailing dot
        BOOST_TEST(!test(".1234", nolddot_real));          //  Bad leading dot
    }

    ///////////////////////////////////////////////////////////////////////////
    //  Special thousands separated numbers
    ///////////////////////////////////////////////////////////////////////////
    {
        using boost::spirit::qi::real_spec;
        using boost::spirit::qi::parse;
        real_spec<double, ts_real_policies<double> > ts_real;
        double  d;

        BOOST_TEST(test("123,456,789.01", ts_real));
        BOOST_TEST(test_attr("123,456,789.01", ts_real, d) && compare(d, 123456789.01));

        BOOST_TEST(test("12,345,678.90", ts_real));
        BOOST_TEST(test_attr("12,345,678.90", ts_real, d) && compare(d, 12345678.90));

        BOOST_TEST(test("1,234,567.89", ts_real));
        BOOST_TEST(test_attr("1,234,567.89", ts_real, d) && compare(d, 1234567.89));

        BOOST_TEST(!test("1234,567,890", ts_real));
        BOOST_TEST(!test("1,234,5678,9", ts_real));
        BOOST_TEST(!test("1,234,567.89e6", ts_real));
        BOOST_TEST(!test("1,66", ts_real));
    }

    ///////////////////////////////////////////////////////////////////////////
    //  Custom data type
    ///////////////////////////////////////////////////////////////////////////
    {
        using boost::math::concepts::real_concept;
        using boost::spirit::qi::real_spec;
        using boost::spirit::qi::real_policies;
        using boost::spirit::qi::parse;

        real_spec<real_concept, real_policies<real_concept> > custom_real;
        real_concept d;

        BOOST_TEST(test("-1234", custom_real));
        BOOST_TEST(test_attr("-1234", custom_real, d) && compare(d, -1234));

        BOOST_TEST(test("-1.2e3", custom_real));
        BOOST_TEST(test_attr("-1.2e3", custom_real, d) && compare(d, -1.2e3));

        BOOST_TEST(test("+1.2e3", custom_real));
        BOOST_TEST(test_attr("+1.2e3", custom_real, d) && compare(d, 1.2e3));

        BOOST_TEST(test("-0.1", custom_real));
        BOOST_TEST(test_attr("-0.1", custom_real, d) && compare(d, -0.1));

        BOOST_TEST(test("-1.2e-3", custom_real));
        BOOST_TEST(test_attr("-1.2e-3", custom_real, d) && compare(d, -1.2e-3));

        BOOST_TEST(test("-1.e2", custom_real));
        BOOST_TEST(test_attr("-1.e2", custom_real, d) && compare(d, -1.e2));

        BOOST_TEST(test("-.2e3", custom_real));
        BOOST_TEST(test_attr("-.2e3", custom_real, d) && compare(d, -.2e3));

        BOOST_TEST(test("-2e3", custom_real));
        BOOST_TEST(test_attr("-2e3", custom_real, d) && compare(d, -2e3));

        BOOST_TEST(!test("-e3", custom_real));
        BOOST_TEST(!test_attr("-e3", custom_real, d));

        BOOST_TEST(!test("-1.2e", custom_real));
        BOOST_TEST(!test_attr("-1.2e", custom_real, d));
    }

    return boost::report_errors();
}