//
//  Copyright (c) 2002-2003
//  Toon Knapen, Kresimir Fresl, Joerg Walter
//
// 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)
//

#ifndef BOOST_NUMERIC_BINDINGS_TRAITS_SYMM_HERM_RAW_HPP
#define BOOST_NUMERIC_BINDINGS_TRAITS_SYMM_HERM_RAW_HPP

#include <boost/numeric/bindings/traits/matrix_raw.hpp> 
#ifndef BOOST_UBLAS_HAVE_BINDINGS
#  include <boost/numeric/ublas/symmetric.hpp> 
#  include <boost/numeric/ublas/hermitian.hpp> 
#endif 

namespace boost { namespace numeric { namespace bindings { namespace traits {

  namespace ublas = boost::numeric::ublas; 

  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  int leading_dimension (const ublas::symmetric_adaptor<M, F> &m) {
    return bindings::traits::leading_dimension (m.data());
  }

  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  int leading_dimension (const ublas::hermitian_adaptor<M, F> &m) {
    return bindings::traits::leading_dimension (m.data());
  }



  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  int matrix_storage_size (const ublas::symmetric_adaptor<M, F> &m) {
    return matrix_storage_size (m.data()); 
  }

  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  int matrix_storage_size (const ublas::hermitian_adaptor<M, F> &m) {
    return matrix_storage_size (m.data()); 
  }

  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  int matrix_storage_size (const ublas::symmetric_matrix<T,F1,F2,A> &m) {
    return (int) ((m.size1() * (m.size1() + 1)) / 2); 
  }

  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  int matrix_storage_size (const ublas::hermitian_matrix<T,F1,F2,A> &m) {
    return (int) ((m.size1() * (m.size1() + 1)) / 2); 
  }



#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::symmetric_matrix<T,F1,F2,A>::const_pointer 
  matrix_storage (const ublas::symmetric_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }
#endif
  // We need data_const() mostly due to MSVC 6.0.
  // But how shall we write portable code otherwise?
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::symmetric_matrix<T,F1,F2,A>::const_pointer 
  matrix_storage_const (const ublas::symmetric_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::symmetric_matrix<T,F1,F2,A>::pointer 
  matrix_storage (ublas::symmetric_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }

#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::const_pointer 
  matrix_storage (const ublas::symmetric_adaptor<M, F> &m) {
    return matrix_storage (m.data()); 
  }
#endif
  // We need data_const() mostly due to MSVC 6.0.
  // But how shall we write portable code otherwise?
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::const_pointer 
  matrix_storage_const (const ublas::symmetric_adaptor<M, F> &m) {
    return matrix_storage_const (m.data()); 
  }
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::pointer matrix_storage (ublas::symmetric_adaptor<M, F> &m) {
    return matrix_storage (m.data()); 
  }


#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::hermitian_matrix<T,F1,F2,A>::const_pointer 
  matrix_storage (const ublas::hermitian_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }
#endif
  // We need data_const() mostly due to MSVC 6.0.
  // But how shall we write portable code otherwise?
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::hermitian_matrix<T,F1,F2,A>::const_pointer 
  matrix_storage_const (const ublas::hermitian_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }
  template<typename T, typename F1, typename F2, typename A>
  BOOST_UBLAS_INLINE
  typename ublas::hermitian_matrix<T,F1,F2,A>::pointer 
  matrix_storage (ublas::hermitian_matrix<T,F1,F2,A> &m) {
    return &m.data().begin()[0];
  }

#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::const_pointer 
  matrix_storage (const ublas::hermitian_adaptor<M, F> &m) {
    return matrix_storage (m.data()); 
  }
#endif
  // We need data_const() mostly due to MSVC 6.0.
  // But how shall we write portable code otherwise?
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::const_pointer 
  matrix_storage_const (const ublas::hermitian_adaptor<M, F> &m) {
    return matrix_storage_const (m.data()); 
  }
  template <typename M, typename F>
  BOOST_UBLAS_INLINE
  typename M::pointer matrix_storage (ublas::hermitian_adaptor<M, F> &m) {
    return matrix_storage (m.data()); 
  }

  namespace detail {

    inline char m_uplo_tag (ublas::upper_tag const&) { return 'U'; } 
    inline char m_uplo_tag (ublas::lower_tag const&) { return 'L'; } 

  }

  template <typename SymmM> 
  inline 
  char matrix_uplo_tag (SymmM&) {
      typedef typename SymmM::packed_category uplo_t; 
      return detail::m_uplo_tag (uplo_t());
  }
  

}}}}

#endif
