/*
 *
 * Copyright (c) Kresimir Fresl 2002
 *
 * 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)
 *
 * Author acknowledges the support of the Faculty of Civil Engineering,
 * University of Zagreb, Croatia.
 *
 */

#ifndef BOOST_NUMERIC_BINDINGS_CBLAS_LEVEL_1_HPP
#define BOOST_NUMERIC_BINDINGS_CBLAS_LEVEL_1_HPP

#include <cassert>

#include <boost/numeric/bindings/traits/type_traits.hpp>
#include <boost/numeric/bindings/traits/vector_traits.hpp>
#include <boost/numeric/bindings/atlas/cblas1_overloads.hpp>

#ifndef BOOST_NUMERIC_BINDINGS_NO_TYPE_CHECK
#  include <boost/type_traits/same_traits.hpp>
#  include <boost/static_assert.hpp>
#endif

namespace boost { namespace numeric { namespace bindings {

  namespace atlas {

    // x_i <- alpha for all i
    template <typename T, typename Vct>
    inline
    void set (T const& alpha, Vct& x) {
      detail::set (traits::vector_size (x), alpha,
                   traits::vector_storage (x), traits::vector_stride (x));
    }

    // y <- x
    template <typename VctX, typename VctY>
    inline
    void copy (VctX const& x, VctY& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::copy (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
                    traits::vector_storage (y), traits::vector_stride (y));
    }

    // x <-> y
    template <typename VctX, typename VctY>
    inline
    void swap (VctX& x, VctY& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::swap (traits::vector_size (x),
                    traits::vector_storage (x), traits::vector_stride (x),
                    traits::vector_storage (y), traits::vector_stride (y));
    }

    // x <- alpha * x
    template <typename T, typename Vct>
    inline
    void scal (T const& alpha, Vct& x) {
#ifndef BOOST_NUMERIC_BINDINGS_NO_TYPE_CHECK
      typedef traits::vector_traits<Vct> vtraits;
      BOOST_STATIC_ASSERT(
       (boost::is_same<T, typename vtraits::value_type>::value
        ||
        boost::is_same<T,
          typename traits::type_traits<typename vtraits::value_type>::real_type
        >::value
        ));
#endif
      detail::scal (traits::vector_size (x), alpha,
                    traits::vector_storage (x), traits::vector_stride (x));
    }

    // y <- alpha * x + y
    template <typename T, typename VctX, typename VctY>
    inline
    void axpy (T const& alpha, VctX const& x, VctY& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::axpy (traits::vector_size (x), alpha,
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
                    traits::vector_storage (y), traits::vector_stride (y));
    }

    // y <- x + y
    template <typename VctX, typename VctY>
    inline
    void xpy (VctX const& x, VctY& y) {
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
      typedef typename traits::vector_traits<VctX>::value_type val_t;
#else
      typedef typename VctX::value_type val_t;
#endif
      axpy ((val_t) 1, x, y);
    }

    // y <- alpha * x + beta * y
    template <typename T, typename VctX, typename VctY>
    inline
    void axpby (T const& alpha, VctX const& x,
                T const& beta, VctY& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::axpby (traits::vector_size (x), alpha,
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                     traits::vector_storage (x),
#else
                     traits::vector_storage_const (x),
#endif
                     traits::vector_stride (x),
                     beta,
                     traits::vector_storage (y), traits::vector_stride (y));
    }

    ///////////////////////////////////////////

    // dot <- x^T * y
    // .. real & complex types
    template <typename VctX, typename VctY>
    inline
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
    typename traits::vector_traits<VctX>::value_type
#else
    typename VctX::value_type
#endif
    dot (VctX const& x, VctY const& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      return detail::dot (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                          traits::vector_storage (x),
#else
                          traits::vector_storage_const (x),
#endif
                          traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                          traits::vector_storage (y),
#else
                          traits::vector_storage_const (y),
#endif
                          traits::vector_stride (y));
    }

    // dot <- x^T * y
    // .. float only -- with double accumulation
    template <typename VctX, typename VctY>
    inline
    double dsdot (VctX const& x, VctY const& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      return cblas_dsdot (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                          traits::vector_storage (x),
#else
                          traits::vector_storage_const (x),
#endif
                          traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                          traits::vector_storage (y),
#else
                          traits::vector_storage_const (y),
#endif
                          traits::vector_stride (y));
    }

    // apdot <- alpha + x^T * y
    // .. float only -- computation uses double precision
    template <typename VctX, typename VctY>
    inline
    float sdsdot (float const alpha, VctX const& x, VctY const& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      return cblas_sdsdot (traits::vector_size (x), alpha,
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                           traits::vector_storage (x),
#else
                           traits::vector_storage_const (x),
#endif
                           traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                           traits::vector_storage (y),
#else
                           traits::vector_storage_const (y),
#endif
                           traits::vector_stride (y));
    }

    // dotu <- x^T * y
    // .. complex types only
    // .. function
    template <typename VctX, typename VctY>
    inline
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
    typename traits::vector_traits<VctX>::value_type
#else
    typename VctX::value_type
#endif
    dotu (VctX const& x, VctY const& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
      typename traits::vector_traits<VctX>::value_type val;
#else
      typename VctX::value_type val;
#endif
      detail::dotu (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (y),
#else
                    traits::vector_storage_const (y),
#endif
                    traits::vector_stride (y),
                    &val);
      return val;
    }
    // .. procedure
    template <typename VctX, typename VctY>
    inline
    void dotu (VctX const& x, VctY const& y,
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
               typename traits::vector_traits<VctX>::value_type& val
#else
               typename VctX::value_type& val
#endif
    ) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::dotu (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (y),
#else
                    traits::vector_storage_const (y),
#endif
                    traits::vector_stride (y),
                    &val);
    }

    // dotc <- x^H * y
    // .. complex types only
    // .. function
    template <typename VctX, typename VctY>
    inline
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
    typename traits::vector_traits<VctX>::value_type
#else
    typename VctX::value_type
#endif
    dotc (VctX const& x, VctY const& y) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
      typename traits::vector_traits<VctX>::value_type val;
#else
      typename VctX::value_type val;
#endif
      detail::dotc (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (y),
#else
                    traits::vector_storage_const (y),
#endif
                    traits::vector_stride (y),
                    &val);
      return val;
    }
    // .. procedure
    template <typename VctX, typename VctY>
    inline
    void dotc (VctX const& x, VctY const& y,
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
               typename traits::vector_traits<VctX>::value_type& val
#else
               typename VctX::value_type& val
#endif
    ) {
      assert (traits::vector_size (y) >= traits::vector_size (x));
      detail::dotc (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (x),
#else
                    traits::vector_storage_const (x),
#endif
                    traits::vector_stride (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                    traits::vector_storage (y),
#else
                    traits::vector_storage_const (y),
#endif
                    traits::vector_stride (y),
                    &val);
    }

    // nrm2 <- ||x||_2
    template <typename Vct>
    inline
    typename traits::type_traits<
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
      typename traits::vector_traits<Vct>::value_type
#else
      typename Vct::value_type
#endif
    >::real_type
    nrm2 (Vct const& x) {
      return detail::nrm2 (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                           traits::vector_storage (x),
#else
                           traits::vector_storage_const (x),
#endif
                           traits::vector_stride (x));
    }

    // asum <- ||re (x)|| + ||im (x)||
    template <typename Vct>
    inline
    typename traits::type_traits<
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
      typename traits::vector_traits<Vct>::value_type
#else
      typename Vct::value_type
#endif
    >::real_type
    asum (Vct const& x) {
      return detail::asum (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                           traits::vector_storage (x),
#else
                           traits::vector_storage_const (x),
#endif
                           traits::vector_stride (x));
    }

    // iamax <- 1st i: max (|re (x_i)| + |im (x_i)|)
    template <typename Vct>
    inline
    CBLAS_INDEX iamax (Vct const& x) {
      return detail::iamax (traits::vector_size (x),
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
                            traits::vector_storage (x),
#else
                            traits::vector_storage_const (x),
#endif
                            traits::vector_stride (x));
    }

    // TO DO: plane rotations

  } // namespace atlas

}}}

#endif // BOOST_NUMERIC_BINDINGS_CBLAS_LEVEL_1_HPP
