#ifndef TEST_UNIT_MATH_MIX_SCAL_FUN_NAN_UTIL_HPP
#define TEST_UNIT_MATH_MIX_SCAL_FUN_NAN_UTIL_HPP

#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/tools/promotion.hpp>
#include <limits>

template <typename F>
void test_nan_fv1(const F& f, const double& arg1, const bool& throws) {
  using stan::math::var;
  stan::math::fvar<var> arg1_v = arg1;

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val()));

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_fv2(const F& f, const double& arg1, const bool& throws) {
  using stan::math::var;
  stan::math::fvar<var> arg1_v = arg1;

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val()));

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv1(const F& f, const double& arg1, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  stan::math::fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv2(const F& f, const double& arg1, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  stan::math::fvar<fvar<var> > arg1_v(fvar<var>(arg1, 1.0),
                                      fvar<var>(1.0, 1.0));

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv3(const F& f, const double& arg1, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  stan::math::fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv4(const F& f, const double& arg1, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  stan::math::fvar<fvar<var> > arg1_v(fvar<var>(arg1, 1.0),
                                      fvar<var>(1.0, 1.0));

  if (throws) {
    EXPECT_THROW(f(arg1_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_mix(const F& f, const bool& throws) {
  double nan = std::numeric_limits<double>::quiet_NaN();
  test_nan_fv1(f, nan, throws);
  test_nan_fv2(f, nan, throws);
  test_nan_ffv1(f, nan, throws);
  test_nan_ffv2(f, nan, throws);
  test_nan_ffv3(f, nan, throws);
  test_nan_ffv4(f, nan, throws);
}

template <typename F>
void test_nan_fv_fv1(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val()));

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}

template <typename F>
void test_nan_fv_fv2(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val()));

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}

template <typename F>
void test_nan_fv_d1(const F& f, const double& arg1, const double& arg2,
                    const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val()));

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_fv_d2(const F& f, const double& arg1, const double& arg2,
                    const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val()));

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_d_fv1(const F& f, const double& arg1, const double& arg2,
                    const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val()));

    AVEC x = createAVEC(arg2_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_d_fv2(const F& f, const double& arg1, const double& arg2,
                    const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val()));

    AVEC x = createAVEC(arg2_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv_ffv1(const F& f, const double& arg1, const double& arg2,
                       const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}
template <typename F>
void test_nan_ffv_ffv2(const F& f, const double& arg1, const double& arg2,
                       const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}
template <typename F>
void test_nan_ffv_ffv3(const F& f, const double& arg1, const double& arg2,
                       const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}
template <typename F>
void test_nan_ffv_ffv4(const F& f, const double& arg1, const double& arg2,
                       const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
    EXPECT_TRUE(stan::math::is_nan(g[1]));
  }
}

template <typename F>
void test_nan_ffv_d1(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv_d2(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv_d3(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_ffv_d4(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v = arg1;
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.val_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val()));

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}

template <typename F>
void test_nan_d_ffv1(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val()));

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_d_ffv2(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val()));

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_d_ffv3(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val()));

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_d_ffv4(const F& f, const double& arg1, const double& arg2,
                     const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v = arg2;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.val_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v), std::domain_error);
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val()));

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size());
    EXPECT_TRUE(stan::math::is_nan(g[0]));
  }
}
template <typename F>
void test_nan_mix(const F& f, const double& arg1, const double& arg2,
                  const bool& throws) {
  double nan = std::numeric_limits<double>::quiet_NaN();
  Eigen::VectorXd arg1_vec(3);
  Eigen::VectorXd arg2_vec(3);
  arg1_vec << nan, arg1, nan;
  arg2_vec << arg2, nan, nan;
  for (int i = 0; i < arg1_vec.size(); i++) {
    test_nan_fv_fv1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_fv_fv2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_fv_d1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_fv_d2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_fv1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_fv2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_ffv1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_ffv2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_ffv3(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_ffv4(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_d1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_d2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_d3(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_ffv_d4(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_ffv1(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_ffv2(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_ffv3(f, arg1_vec(i), arg2_vec(i), throws);
    test_nan_d_ffv4(f, arg1_vec(i), arg2_vec(i), throws);
  }
}
template <typename F>
void test_nan_fv_fv_fv1(const F& f, const double& arg1, const double& arg2,
                        const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_, arg3_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_fv_fv2(const F& f, const double& arg1, const double& arg2,
                        const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_, arg3_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_d_fv_fv1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_, arg3_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_fv_fv2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_, arg3_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_d_fv1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg3_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_d_fv2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg3_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_fv_d1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_fv_d2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);
  fvar<var> arg2_v(arg2, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_, arg2_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_d_d1(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_fv_d_d2(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg1_v(arg1, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_d_fv_d1(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_fv_d2(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg2_v(arg2, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_d_d_fv1(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_);
    VEC g;
    res.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_d_fv2(const F& f, const double& arg1, const double& arg2,
                      const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<var> arg3_v(arg3, 1.0);

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    stan::math::fvar<var> res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_);
    VEC g;
    res.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_ffv1(const F& f, const double& arg1, const double& arg2,
                           const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_ffv2(const F& f, const double& arg1, const double& arg2,
                           const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_ffv3(const F& f, const double& arg1, const double& arg2,
                           const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_ffv_ffv_ffv4(const F& f, const double& arg1, const double& arg2,
                           const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3_v), std::domain_error)
        << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(3U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[2])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_ffv1(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_ffv2(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_ffv3(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_ffv4(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_ffv1(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_ffv2(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_ffv3(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_ffv4(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg3_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_d1(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_d2(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_d3(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_ffv_d4(const F& f, const double& arg1, const double& arg2,
                         const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_, arg2_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(2U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[1])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_d1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_d2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_d3(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_ffv_d_d4(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg1_v(arg1, 1.0);
  arg1_v.val_.d_ = 1.0;
  arg1_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1_v << " second argument " << arg2
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1_v, arg2, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1_v, arg2, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg1_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_d_ffv_d1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_d2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_d3(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_ffv_d4(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg2_v(arg2, 1.0);
  arg2_v.val_.d_ = 1.0;
  arg2_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2_v
           << " and third argument " << arg3;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2_v, arg3), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2_v, arg3);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg2_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_d_d_ffv1(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_.val_);
    VEC g;
    res.val_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_d_ffv2(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.val_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_.val_);
    VEC g;
    res.d_.val_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_d_ffv3(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.val_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_.val_);
    VEC g;
    res.val_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}
template <typename F>
void test_nan_d_d_ffv4(const F& f, const double& arg1, const double& arg2,
                       const double& arg3, const bool& throws) {
  using stan::math::fvar;
  using stan::math::var;
  fvar<fvar<var> > arg3_v(arg3, 1.0);
  arg3_v.val_.d_ = 1.0;
  arg3_v.d_.d_ = 1.0;

  std::ostringstream fail_msg;
  fail_msg << "Failed for "
           << "first argument " << arg1 << " second argument " << arg2
           << " and third argument " << arg3_v;

  if (throws) {
    EXPECT_THROW(f(arg1, arg2, arg3_v), std::domain_error) << fail_msg.str();
  } else {
    fvar<fvar<var> > res = f(arg1, arg2, arg3_v);
    EXPECT_TRUE(stan::math::is_nan(res.d_.d_.val())) << fail_msg.str();

    AVEC x = createAVEC(arg3_v.val_.val_);
    VEC g;
    res.d_.d_.grad(x, g);

    ASSERT_EQ(1U, g.size()) << fail_msg.str();
    EXPECT_TRUE(stan::math::is_nan(g[0])) << fail_msg.str();
  }
}

template <typename F>
void test_nan_mix(const F& f, const double& arg1, const double& arg2,
                  const double& arg3, const bool& throws) {
  double nan = std::numeric_limits<double>::quiet_NaN();
  Eigen::VectorXd arg1_vec(7);
  Eigen::VectorXd arg2_vec(7);
  Eigen::VectorXd arg3_vec(7);
  arg1_vec << nan, arg1, arg1, nan, nan, arg1, nan;
  arg2_vec << arg2, nan, arg2, nan, arg2, nan, nan;
  arg3_vec << arg3, arg3, nan, arg3, nan, nan, nan;

  for (int i = 0; i < arg1_vec.size(); i++) {
    test_nan_fv_fv_fv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_fv_fv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_fv_fv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_fv_fv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_d_fv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_d_fv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_fv_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_fv_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_fv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_fv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_fv_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_fv_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_d_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_fv_d_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_ffv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_ffv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_ffv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_ffv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_ffv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_ffv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_ffv1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_ffv2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_d1(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_d2(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_ffv3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_ffv4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_ffv3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_ffv4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_ffv3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_ffv4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_d3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_ffv_d4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_ffv3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_d_ffv4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_d3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_d_ffv_d4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_d3(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
    test_nan_ffv_d_d4(f, arg1_vec(i), arg2_vec(i), arg3_vec(i), throws);
  }
}

#endif


#ifndef IO_UTILITIES_HPP
#define IO_UTILITIES_HPP

#include <fstream>
#include <Eigen/Dense>
#include <opencv2/core/core.hpp>

std::string cvTypeToString(int type);

template<class Matrix>
void readEigenMatrixFromFile(const std::string &filename, Matrix& matrix){
	std::ifstream in(filename, std::ifstream::binary);
	typename Matrix::Index rows = 0, cols = 0;
	in.read((char*)(&rows), sizeof(typename Matrix::Index));
	in.read((char*)(&cols), sizeof(typename Matrix::Index));
	matrix.resize(rows, cols);
	in.read((char*)matrix.data(), rows*cols*sizeof(typename Matrix::Scalar));
	in.close();
}

template<class Matrix>
void writeEigenMatrixToFile(const std::string &filename, const Matrix& matrix){
	std::ofstream out(filename, std::ofstream::binary);
	typename Matrix::Index rows = matrix.rows(), cols = matrix.cols();
	out.write((char*)(&rows), sizeof(typename Matrix::Index));
	out.write((char*)(&cols), sizeof(typename Matrix::Index));
	out.write((char*)matrix.data(), rows*cols*sizeof(typename Matrix::Scalar));
	out.close();
}

template<typename ScalarT>
void readVectorFromFile(const std::string &filename, std::vector<ScalarT> &vec) {
	Eigen::Matrix<ScalarT, Eigen::Dynamic, 1> vec_e;
	readEigenMatrixFromFile(filename, vec_e);
	vec.resize(vec_e.size());
	Eigen::Matrix<ScalarT, Eigen::Dynamic, 1>::Map(&vec[0], vec_e.size()) = vec_e;
}

template<typename ScalarT>
void writeVectorToFile(const std::string &filename, const std::vector<ScalarT> &vec) {
	Eigen::Matrix<ScalarT, Eigen::Dynamic, 1> vec_e = Eigen::Matrix<ScalarT, Eigen::Dynamic, 1>::Map(&vec[0], vec.size());
	writeEigenMatrixToFile(filename, vec_e);
}

void readSIFTModelFromFile(const std::string &fname, Eigen::MatrixXf &loc, std::vector<float> &descr);

void writeSIFTModelToFile(const std::string &fname, const Eigen::MatrixXf &loc, const std::vector<float> &descr);

void readStereoRigParametersFromXMLFile(const std::string &fname, cv::Mat &K0, cv::Mat &d0, cv::Size &im0_size, cv::Mat &K1, cv::Mat &d1, cv::Size &im1_size, cv::Mat &R, cv::Mat &t);

#endif /* IO_UTILITIES_HPP */


/*!
@file
Defines `boost::hana::HashMap`.

@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_HANA_HASH_MAP_HPP
#define BOOST_HANA_HASH_MAP_HPP

#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/foldable/mcd.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/logical/logical.hpp>
#include <boost/hana/maybe.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/searchable/mcd.hpp>

namespace boost { namespace hana {
    struct HashMap;

    namespace hash_map_detail {
        template <typename Hash, typename Pairs>
        struct bucket;

        BOOST_HANA_CONSTEXPR_LAMBDA auto make_bucket = [](auto h, auto pairs) {
            return bucket<decltype(h), decltype(pairs)>{h, pairs};
        };

        template <typename Hash, typename Pairs>
        struct bucket {
            Hash hash;
            Pairs pairs; // a list of (key, value) pairs

            template <typename H1, typename P1, typename H2, typename P2>
            friend constexpr auto operator==(bucket<H1, P1> b1, bucket<H2, P2> b2)
            { return b1.hash == b2.hash && b1.pairs == b2.pairs; }

            template <typename Key, typename Value>
            constexpr auto insert(Key k, Value v) const {
                auto iskey = [=](auto pair) { return first(pair) == k; };
                return eval_if(any(pairs, iskey),
                    [=](auto _) {
                        auto new_pairs = _(replace)(iskey, pair(k, v), pairs);
                        return make_bucket(hash, new_pairs);
                    },
                    [=](auto _) {
                        return make_bucket(hash, _(cons)(pair(k, v), pairs));
                    }
                );
            }

            template <typename Key>
            constexpr auto lookup(Key key) const {
                return fmap(
                    second,
                    find(pairs, [=](auto p) { return first(p) == key; })
                );
            }
        };

        template <typename Buckets>
        struct map {
            Buckets buckets; // a list of buckets
            using hana_datatype = HashMap;
        };
    }

    //! @todo Create a `Hashable` type class.
    BOOST_HANA_CONSTEXPR_LAMBDA auto hash = [](auto x) {
        return x;
    };

    BOOST_HANA_CONSTEXPR_LAMBDA auto insert = [](auto key, auto value, auto map) {
        auto h = hash(key);
        // This should be O(1), but for now it'll do.
        auto bucket = find(map.buckets, [=](auto b) { return b.hash == h; });

        auto insert_in_bucket = [=](auto bucket) {
            auto new_buckets = replace(_ == bucket, bucket.insert(key, value), map.buckets);
            hash_map_detail::map<decltype(new_buckets)> new_map{new_buckets};
            return new_map;
        };

        auto insert_new_bucket = [=] {
            auto new_bucket = hash_map_detail::make_bucket(h, list()).insert(key, value);
            auto buckets = cons(new_bucket, map.buckets);
            hash_map_detail::map<decltype(buckets)> new_map{buckets};
            return new_map;
        };

        // we should not need to evaluate insert_new_bucket.
        return maybe(insert_new_bucket(), insert_in_bucket, bucket);
    };

    BOOST_HANA_CONSTEXPR_LAMBDA auto hash_map = [](auto ...pairs) {
        hash_map_detail::map<decltype(list())> empty_map{list()};
        auto ins = [](auto map, auto pair) { return insert(first(pair), second(pair), map); };
        return foldl(list(pairs...), empty_map, ins);
    };

    template <>
    struct Searchable::instance<HashMap> : Searchable::mcd {
        template <typename Map, typename Pred>
        static constexpr auto find_impl(Map map, Pred pred) {
            return; // not implemented
        }

        template <typename Map, typename Pred>
        static constexpr auto any_impl(Map map, Pred pred) {
            return; // not implemented
        }

        template <typename Map, typename Key>
        static constexpr auto lookup_impl(Map map, Key key) {
            auto h = hash(key);
            auto bucket = find(map.buckets, [=](auto b) { return b.hash == h; });
            return maybe(nothing,
                [=](auto bucket) { return bucket.lookup(key); }
            , bucket);
        }
    };

    template <>
    struct Foldable::instance<HashMap> : Foldable::mcd {
        template <typename Map, typename State, typename F>
        static constexpr auto foldr_impl(Map m, State s, F f) {
            auto f_ = [=](auto bucket, auto s) {
                return foldr(fmap(second, bucket.pairs), s, f);
            };
            return foldr(m.buckets, s, f_);
        }

        template <typename Map, typename State, typename F>
        static constexpr auto foldl_impl(Map m, State s, F f) {
            auto f_ = [=](auto s, auto bucket) {
                return foldl(fmap(second, bucket.pairs), s, f);
            };
            return foldl(m.buckets, s, f_);
        }
    };
}} // end namespace boost::hana

#endif // !BOOST_HANA_HASH_MAP_HPP


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file n_queens_components.hpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2014-01-02
 */
#ifndef PAAL_N_QUEENS_COMPONENTS_HPP
#define PAAL_N_QUEENS_COMPONENTS_HPP

#include "paal/data_structures/subset_iterator.hpp"

#include <boost/iterator/function_input_iterator.hpp>
#include <boost/range/adaptor/transformed.hpp>

namespace paal {
namespace local_search {

/**
 * @brief class describing Move
 */
struct Move {
    /**
     * @brief constructor
     *
     * @param from
     * @param to
     */
    Move(int from, int to) : m_from(from), m_to(to) {}

    /**
     * @brief getter for m_from
     *
     * @return
     */
    int get_from() const { return m_from; }

    /**
     * @brief getter for m_to
     *
     * @return
     */
    int get_to() const { return m_to; }

  private:
    int m_from;
    int m_to;
};

/**
 * @brief Functor creating Move
 */
struct make_move {
    /**
     * @brief operator()
     *
     * @param from
     * @param to
     *
     * @return
     */
    Move operator()(int from, int to) const { return Move(from, to); }
};

/**
 * @brief n_queens_commit functor
 */
struct n_queens_commit {
    template <typename Solution>
    /**
     * @brief Operator swaps elements of the solution range
     *
     * @param sol
     * @param move
     */
        bool operator()(Solution &sol, Move move) const {
        sol.swap_queens(move.get_from(), move.get_to());
        return true;
    }
};

namespace detail {

struct tuple_to_move {
    using result_type = Move;
    result_type operator()(std::tuple<int, int> t) const {
        return Move(std::get<0>(t), std::get<1>(t));
    }
};
} //!detail

/**
 * @brief n_queensget_moves functor
 */
class n_queensget_moves {

    /**
     * @brief Functor needed for type computation
     *
     * @tparam Solution
     */
    template <typename Solution> struct types_eval {
        using SolutionIter = decltype(std::declval<Solution>().begin());
        using Subset =
            data_structures::subsets_iterator<2, SolutionIter, make_move>;
        using IterPair = std::pair<Subset, Subset>;
        using Range = boost::iterator_range<Subset>;
    };

  public:
    /**
     * @brief operator() returns all the elements
     *
     * @tparam Solution
     * @param solution
     *
     * @return
     */
    template <typename Solution>
    auto operator()(
        const Solution &solution) const->typename types_eval<Solution>::Range {
        return data_structures::make_subsets_iterator_range<2>(
                solution.begin(), solution.end(), make_move{});
    }
};

/**
 * @brief n_queens_gain functor
 */
struct n_queens_gain {
    /**
     * @brief computes difference in cost
     *
     * @tparam Solution
     * @param solution
     * @param move
     *
     * @return
     */
    template <typename Solution>
    int operator()(const Solution &solution, Move move) const {
        int x1 = move.get_from();
        int y1 = solution.get_y(x1);
        int x2 = move.get_to();
        int y2 = solution.get_y(x2);

        return -solution.get_num_attacing(x1, y2) -
               solution.get_num_attacing(x2, y1) +
               solution.get_num_attacing(x1, y1) - 2 +
               solution.get_num_attacing(x2, y2) - 2 -
               2 * (std::abs(x1 - x2) == std::abs(y1 - y2));
    }
};
} //!local_search
} //!paal

#endif // PAAL_N_QUEENS_COMPONENTS_HPP