#ifndef __KOKKOSBATCHED_APPLY_HOUSEHOLDER_SERIAL_INTERNAL_HPP__
#define __KOKKOSBATCHED_APPLY_HOUSEHOLDER_SERIAL_INTERNAL_HPP__

/// \author Kyungjoo Kim (kyukim@sandia.gov)

#include "KokkosBatched_Util.hpp"

namespace KokkosBatched {

///
/// Serial Internal Impl
/// ====================
///
/// this impl follows the flame interface of householder transformation
///
struct SerialApplyLeftHouseholderInternal {
  template <typename ValueType>
  KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n,
                                           const ValueType* tau,
                                           /* */ ValueType* u2, const int u2s,
                                           /* */ ValueType* a1t, const int a1ts,
                                           /* */ ValueType* A2, const int as0,
                                           const int as1,
                                           /* */ ValueType* w1t) {
    typedef ValueType value_type;

    /// u2  m x 1
    /// a1t 1 x n
    /// A2  m x n

    // apply a single householder transform H from the left to a row vector a1t
    // and a matrix A2
    const value_type inv_tau = value_type(1) / (*tau);

    // compute the followings:
    // a1t -=    inv(tau)(a1t + u2'A2)
    // A2  -= u2 inv(tau)(a1t + u2'A2)

    // w1t = a1t + u2'A2 = A2^T conj(u2)
    // w1t /= tau
    for (int j = 0; j < n; ++j) {
      value_type tmp = a1t[j * a1ts];
      for (int i = 0; i < m; ++i)
        tmp += Kokkos::Details::ArithTraits<value_type>::conj(u2[i * u2s]) *
               A2[i * as0 + j * as1];
      w1t[j] = tmp * inv_tau;  // /= (*tau);
    }

    // a1t -= w1t    (axpy)
    for (int j = 0; j < n; ++j) a1t[j * a1ts] -= w1t[j];

    // A2  -= u2 w1t (ger)
    for (int j = 0; j < n; ++j)
      for (int i = 0; i < m; ++i) A2[i * as0 + j * as1] -= u2[i * u2s] * w1t[j];

    return 0;
  }
};

struct SerialApplyRightHouseholderInternal {
  template <typename ValueType>
  KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n,
                                           const ValueType* tau,
                                           /* */ ValueType* u2, const int u2s,
                                           /* */ ValueType* a1, const int a1s,
                                           /* */ ValueType* A2, const int as0,
                                           const int as1,
                                           /* */ ValueType* w1) {
    typedef ValueType value_type;
    /// u2 n x 1
    /// a1 m x 1
    /// A2 m x n

    // apply a single householder transform H from the left to a row vector a1t
    // and a matrix A2
    const value_type inv_tau = value_type(1) / (*tau);

    // compute the followings:
    // a1 -= inv(tau)(a1 + A2 u2)
    // A2 -= inv(tau)(a1 + A2 u2) u2'

    // w1 = a1 + A2 u2
    // w1 /= tau
    for (int i = 0; i < m; ++i) {
      value_type tmp = a1[i * a1s];
      for (int j = 0; j < n; ++j) tmp += A2[i * as0 + j * as1] * u2[j * u2s];
      w1[i] = tmp * inv_tau;  // \= (*tau);
    }

    // a1 -= w1 (axpy)
    for (int i = 0; i < m; ++i) a1[i * a1s] -= w1[i];

    // A2 -= w1 * u2' (ger with conjugate)
    for (int j = 0; j < n; ++j)
      for (int i = 0; i < m; ++i)
        A2[i * as0 + j * as1] -=
            w1[i] * Kokkos::Details::ArithTraits<ValueType>::conj(u2[j * u2s]);

    return 0;
  }
};

}  // end namespace KokkosBatched

#endif
