#ifndef STAN_MATH_FWD_FUN_FMA_HPP
#define STAN_MATH_FWD_FUN_FMA_HPP

#include <stan/math/fwd/meta.hpp>
#include <stan/math/fwd/core.hpp>
#include <stan/math/prim/fun/fma.hpp>

namespace stan {
namespace math {

/**
 * The fused multiply-add operation (C99).
 *
 * This double-based operation delegates to <code>fma</code>.
 *
 * The function is defined by
 *
 * <code>fma(a, b, c) = (a * b) + c</code>.
 *
 *
   \f[
   \mbox{fma}(x, y, z) =
   \begin{cases}
     x\cdot y+z & \mbox{if } -\infty\leq x, y, z \leq \infty \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{fma}(x, y, z)}{\partial x} =
   \begin{cases}
     y & \mbox{if } -\infty\leq x, y, z \leq \infty \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{fma}(x, y, z)}{\partial y} =
   \begin{cases}
     x & \mbox{if } -\infty\leq x, y, z \leq \infty \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{fma}(x, y, z)}{\partial z} =
   \begin{cases}
     1 & \mbox{if } -\infty\leq x, y, z \leq \infty \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN}
   \end{cases}
   \f]
 *
 * @param x1 First value.
 * @param x2 Second value.
 * @param x3 Third value.
 * @return Product of the first two values plus the third.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const fvar<T1>& x1,
                                           const fvar<T2>& x2,
                                           const fvar<T3>& x3) {
  return fvar<return_type_t<T1, T2, T3>>(
      fma(x1.val_, x2.val_, x3.val_),
      x1.d_ * x2.val_ + x2.d_ * x1.val_ + x3.d_);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const T1& x1, const fvar<T2>& x2,
                                           const fvar<T3>& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1, x2.val_, x3.val_),
                                         x2.d_ * x1 + x3.d_);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const fvar<T1>& x1, const T2& x2,
                                           const fvar<T3>& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1.val_, x2, x3.val_),
                                         x1.d_ * x2 + x3.d_);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const fvar<T1>& x1,
                                           const fvar<T2>& x2, const T3& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1.val_, x2.val_, x3),
                                         x1.d_ * x2.val_ + x2.d_ * x1.val_);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const T1& x1, const T2& x2,
                                           const fvar<T3>& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1, x2, x3.val_), x3.d_);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const fvar<T1>& x1, const T2& x2,
                                           const T3& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1.val_, x2, x3), x1.d_ * x2);
}

/**
 * See all-var input signature for details on the function and derivatives.
 */
template <typename T1, typename T2, typename T3,
          require_all_stan_scalar_t<T1, T2, T3>* = nullptr>
inline fvar<return_type_t<T1, T2, T3>> fma(const T1& x1, const fvar<T2>& x2,
                                           const T3& x3) {
  return fvar<return_type_t<T1, T2, T3>>(fma(x1, x2.val_, x3), x2.d_ * x1);
}

}  // namespace math
}  // namespace stan
#endif
