// -*- C++ -*-

#if !defined(__ext_array_h__)
#define __ext_array_h__

#include "arrayStd.h"

namespace ext {
//-------------------------------------------------------------------------
/*! \defgroup extArrayMake Convenience Constructor Functions.

  These functions are useful for constructing std::tr1::array's.
  \verbatim
  typedef std::tr1::array<double, 3> Point;
  Point x;
  ...
  // Convert to an array with a different value type.
  std::tr1::array<int, 3> a = ext::convert_array<int>(x);
  // Make an array filled with a specified value.
  x = ext::filled_array<Point>(0.);
  // Copy from a different kind of array.
  double y[3] = {2, 3, 5};
  x = ext::copy_array(y);
  // Make an array by specifying the elements.
  std::tr1::array<double, 1> x1;
  x1 = ext::make_array(2);
  std::tr1::array<double, 2> x2;
  x2 = ext::make_array(2, 3);
  std::tr1::array<double, 3> x3;
  x3 = ext::make_array(2, 3, 5);
  std::tr1::array<double, 4> x4;
  x4 = ext::make_array(2, 3, 5, 7); \endverbatim
*/
//@{


//! Convert to an array with different value type.
template<typename _Target, typename _Source, std::size_t _N>
inline
std::tr1::array<_Target, _N>
convert_array(const std::tr1::array<_Source, _N>& x) {
   std::tr1::array<_Target, _N> result;
   for (std::size_t i = 0; i != _N; ++i) {
      result[i] = static_cast<_Target>(x[i]);
   }
   return result;
}

//! Return an array filled with the specified value.
template<typename _Array>
inline
_Array
filled_array(const typename _Array::value_type& value) {
   _Array x;
   for (std::size_t i = 0; i != x.size(); ++i) {
      x[i] = value;
   }
   return x;
}

//! Copy from the input iterator to make an array.
template<typename _Array, typename _InputIterator>
inline
_Array
copy_array(_InputIterator input) {
   _Array x;
   for (std::size_t i = 0; i != x.size(); ++i) {
      x[i] = *input++;
   }
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 1>
make_array(const _T& x0) {
   std::tr1::array<_T, 1> x = {{x0}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 2>
make_array(const _T& x0, const _T& x1) {
   std::tr1::array<_T, 2> x = {{x0, x1}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 3>
make_array(const _T& x0, const _T& x1, const _T& x2) {
   std::tr1::array<_T, 3> x = {{x0, x1, x2}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 4>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3) {
   std::tr1::array<_T, 4> x = {{x0, x1, x2, x3}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 5>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4) {
   std::tr1::array<_T, 5> x = {{x0, x1, x2, x3, x4}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 6>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4, const _T& x5) {
   std::tr1::array<_T, 6> x = {{x0, x1, x2, x3, x4, x5}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 7>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4, const _T& x5, const _T& x6) {
   std::tr1::array<_T, 7> x = {{x0, x1, x2, x3, x4, x5, x6}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 8>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4, const _T& x5, const _T& x6, const _T& x7) {
   std::tr1::array<_T, 8> x = {{x0, x1, x2, x3, x4, x5, x6, x7}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 9>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4, const _T& x5, const _T& x6, const _T& x7,
const _T& x8) {
   std::tr1::array<_T, 9> x = {{x0, x1, x2, x3, x4, x5, x6, x7, x8}};
   return x;
}

//! Return an array with the specified components.
template<typename _T>
inline
std::tr1::array<_T, 10>
make_array(const _T& x0, const _T& x1, const _T& x2, const _T& x3,
const _T& x4, const _T& x5, const _T& x6, const _T& x7,
const _T& x8, const _T& x9) {
   std::tr1::array<_T, 10> x = {{x0, x1, x2, x3, x4, x5, x6, x7, x8, x9}};
   return x;
}

//@}
//-------------------------------------------------------------------------
/*! \defgroup extArraySimd SIMD Functions
  I provide these functions because in some implementation std::tr1::array
  is 16-byte aligned, while in others it is not. Note that these functions are
  defined in the ext namespace so that they are not confused with the SSE 
  intrinsics.
*/
//@{

#if 0
//! Store for 3-D points.
inline
void
_mm_store_ps(std::tr1::array<float, 3>* p, __m128 a) {
   // CONTINUE: Implement specialization for 16-byte aligned structure.
#if 0
   // If the structure is aligned, we can use the aligned store.
   ::_mm_store_ps(&(*p)[0], a);
#endif
   else {
      // Otherwise, use the slower method. Note that we can't just use the
      // unaligned store because it would write the fourth element.
      (*p)[0] = reinterpret_cast<const float*>(&a)[0];
      (*p)[1] = reinterpret_cast<const float*>(&a)[1];
      (*p)[2] = reinterpret_cast<const float*>(&a)[2];
   }
}

//! Load for 3-D points.
inline
__m128
_mm_load_ps(const std::tr1::array<float, 3>* p) {
   if (sizeof(std::tr1::array<float, 3>) == 4 * sizeof(float)) {
      // If the structure is aligned, we can use the aligned load.
      return ::_mm_load_ps(&(*p)[0]);
   }
   else {
      // Otherwise, use the slower set.
      return _mm_set_ps(0, (*p)[2], (*p)[1], (*p)[0]);
   }
}
#endif

//@}

} // namespace ext


#endif
