/*
 * 
 * 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_CLAPACK_OVERLOADS_HPP
#define BOOST_NUMERIC_BINDINGS_CLAPACK_OVERLOADS_HPP

#include <boost/numeric/bindings/atlas/clapack_inc.hpp>
#include <boost/numeric/bindings/traits/type.hpp>


namespace boost { namespace numeric { namespace bindings { 

  namespace atlas { namespace detail {

    //
    // general system of linear equations A * X = B
    //

    // 'driver' function -- factor and solve
    inline 
    int gesv (CBLAS_ORDER const Order, 
              int const N, int const NRHS,
              float* A, int const lda, int* ipiv, 
              float* B, int const ldb) 
    {
      return clapack_sgesv (Order, N, NRHS, A, lda, ipiv, B, ldb);
    }
    
    inline 
    int gesv (CBLAS_ORDER const Order, 
              int const N, int const NRHS,
              double* A, int const lda, int* ipiv, 
              double* B, int const ldb) 
    {
      return clapack_dgesv (Order, N, NRHS, A, lda, ipiv, B, ldb);
    }
    
    inline 
    int gesv (CBLAS_ORDER const Order, 
              int const N, int const NRHS,
              traits::complex_f* A, int const lda, int* ipiv, 
              traits::complex_f* B, int const ldb) 
    {
      return clapack_cgesv (Order, N, NRHS, 
                            static_cast<void*> (A), lda, ipiv, 
                            static_cast<void*> (B), ldb);
    }
    
    inline 
    int gesv (CBLAS_ORDER const Order, 
              int const N, int const NRHS,
              traits::complex_d* A, int const lda, int* ipiv, 
              traits::complex_d* B, int const ldb) 
    {
      return clapack_zgesv (Order, N, NRHS, 
                            static_cast<void*> (A), lda, ipiv, 
                            static_cast<void*> (B), ldb);
    }
    
    // LU factorization 
    inline 
    int getrf (CBLAS_ORDER const Order, 
               int const M, int const N,
               float* A, int const lda, int* ipiv)
    {
      return clapack_sgetrf (Order, M, N, A, lda, ipiv);
    }
    
    inline 
    int getrf (CBLAS_ORDER const Order, 
               int const M, int const N,
               double* A, int const lda, int* ipiv)
    {
      return clapack_dgetrf (Order, M, N, A, lda, ipiv);
    }
    
    inline 
    int getrf (CBLAS_ORDER const Order, 
               int const M, int const N,
               traits::complex_f* A, int const lda, int* ipiv)
    {
      return clapack_cgetrf (Order, M, N, static_cast<void*> (A), lda, ipiv); 
    }
    
    inline 
    int getrf (CBLAS_ORDER const Order, 
               int const M, int const N,
               traits::complex_d* A, int const lda, int* ipiv)
    {
      return clapack_zgetrf (Order, M, N, static_cast<void*> (A), lda, ipiv); 
    }

    // solve (using factorization computed by getrf()) 
    inline 
    int getrs (CBLAS_ORDER const Order, CBLAS_TRANSPOSE const Trans, 
               int const N, int const NRHS,
               float const* A, int const lda, int const* ipiv, 
               float* B, int const ldb) 
    {
      return clapack_sgetrs (Order, Trans, N, NRHS, A, lda, ipiv, B, ldb);
    }
    
    inline 
    int getrs (CBLAS_ORDER const Order, CBLAS_TRANSPOSE const Trans, 
               int const N, int const NRHS,
               double const* A, int const lda, int const* ipiv, 
               double* B, int const ldb) 
    {
      return clapack_dgetrs (Order, Trans, N, NRHS, A, lda, ipiv, B, ldb);
    }
    
    inline 
    int getrs (CBLAS_ORDER const Order, CBLAS_TRANSPOSE const Trans, 
               int const N, int const NRHS,
               traits::complex_f const* A, int const lda, 
               int const* ipiv, 
               traits::complex_f* B, int const ldb) 
    {
      return clapack_cgetrs (Order, Trans, N, NRHS, 
                             static_cast<void const*> (A), lda, ipiv, 
                             static_cast<void*> (B), ldb);
    }
    
    inline 
    int getrs (CBLAS_ORDER const Order, CBLAS_TRANSPOSE const Trans, 
               int const N, int const NRHS,
               traits::complex_d const* A, int const lda, 
               int const* ipiv, 
               traits::complex_d* B, int const ldb) 
    {
      return clapack_zgetrs (Order, Trans, N, NRHS, 
                             static_cast<void const*> (A), lda, ipiv, 
                             static_cast<void*> (B), ldb);
    }

    // invert (using factorization computed by getrf()) 
    inline 
    int getri (CBLAS_ORDER const Order, 
               int const N, float* A, int const lda,
               int const* ipiv) 
    {
      return clapack_sgetri (Order, N, A, lda, ipiv);
    }

    inline 
    int getri (CBLAS_ORDER const Order, 
               int const N, double* A, int const lda,
               int const* ipiv) 
    {
      return clapack_dgetri (Order, N, A, lda, ipiv);
    }

    inline 
    int getri (CBLAS_ORDER const Order, 
               int const N, traits::complex_f* A, int const lda,
               int const* ipiv) 
    {
      return clapack_cgetri (Order, N, static_cast<void*> (A), lda, ipiv);
    }

    inline 
    int getri (CBLAS_ORDER const Order, 
               int const N, traits::complex_d* A, int const lda,
               int const* ipiv) 
    {
      return clapack_zgetri (Order, N, static_cast<void*> (A), lda, ipiv);
    }


    //
    // system of linear equations A * X = B
    // with A symmetric positive definite matrix
    //

    // 'driver' function -- factor and solve
    inline 
    int posv (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
              int const N, int const NRHS,
              float* A, int const lda, float* B, int const ldb) 
    {
      return clapack_sposv (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }
    
    inline 
    int posv (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
              int const N, int const NRHS,
              double* A, int const lda, double* B, int const ldb) 
    {
      return clapack_dposv (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }
    
    inline 
    int posv (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
              int const N, int const NRHS,
              traits::complex_f* A, int const lda, 
              traits::complex_f* B, int const ldb) 
    {
      return clapack_cposv (Order, Uplo, N, NRHS, 
                            static_cast<void*> (A), lda, 
                            static_cast<void*> (B), ldb);
    }
    
    inline 
    int posv (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
              int const N, int const NRHS,
              traits::complex_d* A, int const lda, 
              traits::complex_d* B, int const ldb) 
    {
      return clapack_zposv (Order, Uplo, N, NRHS, 
                            static_cast<void*> (A), lda, 
                            static_cast<void*> (B), ldb);
    }

    // Cholesky factorization
    inline 
    int potrf (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, float* A, int const lda) 
    {
      return clapack_spotrf (Order, Uplo, N, A, lda);
    }
    
    inline 
    int potrf (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, double* A, int const lda) 
    {
      return clapack_dpotrf (Order, Uplo, N, A, lda);
    }
    
    inline 
    int potrf (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, traits::complex_f* A, int const lda) 
    {
      return clapack_cpotrf (Order, Uplo, N, static_cast<void*> (A), lda);
    }
    
    inline 
    int potrf (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, traits::complex_d* A, int const lda) 
    {
      return clapack_zpotrf (Order, Uplo, N, static_cast<void*> (A), lda);
    }
    
    // solve (using factorization computed by potrf()) 
    inline 
    int potrs (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, int const NRHS,
               float const* A, int const lda, float* B, int const ldb) 
    {
      return clapack_spotrs (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }

    inline 
    int potrs (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, int const NRHS,
               double const* A, int const lda, double* B, int const ldb) 
    {
      return clapack_dpotrs (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }
   
    inline 
    int potrs (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, int const NRHS,
               traits::complex_f const* A, int const lda, 
               traits::complex_f* B, int const ldb) 
    {
      return clapack_cpotrs (Order, Uplo, N, NRHS, 
                             static_cast<void const*> (A), lda, 
                             static_cast<void*> (B), ldb);
    }
   
    inline 
    int potrs (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, int const NRHS,
               traits::complex_d const* A, int const lda, 
               traits::complex_d* B, int const ldb) 
    {
      return clapack_zpotrs (Order, Uplo, N, NRHS, 
                             static_cast<void const*> (A), lda, 
                             static_cast<void*> (B), ldb);
    }

#ifdef BOOST_NUMERIC_BINDINGS_ATLAS_POTRF_BUG 
    // .. ATLAS bug with row major hermitian matrices 
    // .... symmetric matrices are OK, but to simplify generic potrs() ... 
    inline 
    int potrs_bug (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                   int const N, int const NRHS,
                   float const* A, int const lda, float* B, int const ldb) 
    {
      return clapack_spotrs (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }

    inline 
    int potrs_bug (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                   int const N, int const NRHS,
                   double const* A, int const lda, double* B, int const ldb) 
    {
      return clapack_dpotrs (Order, Uplo, N, NRHS, A, lda, B, ldb);
    }
   
    inline 
    int potrs_bug (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                   int const N, int const NRHS,
                   traits::complex_f const* A, int const lda, 
                   traits::complex_f* B, int const ldb) 
    {
      int sz = N * lda; 
      traits::complex_f* A1 = new traits::complex_f[sz]; 
      for (int i = 0; i < sz; ++i) 
        A1[i] = std::conj (A[i]); 
      int r = clapack_cpotrs (Order, Uplo, N, NRHS, 
                              static_cast<void const*> (A1), lda, 
                              static_cast<void*> (B), ldb);
      delete[] A1; 
      return r; 
    }
   
    inline 
    int potrs_bug (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
                   int const N, int const NRHS,
                   traits::complex_d const* A, int const lda, 
                   traits::complex_d* B, int const ldb) 
    {
      int sz = N * lda; 
      traits::complex_d* A1 = new traits::complex_d[sz]; 
      for (int i = 0; i < sz; ++i) 
        A1[i] = std::conj (A[i]); 
      int r = clapack_zpotrs (Order, Uplo, N, NRHS, 
                              static_cast<void const*> (A1), lda, 
                              static_cast<void*> (B), ldb);
      delete[] A1; 
      return r; 
    }
#endif // BOOST_NUMERIC_BINDINGS_ATLAS_POTRF_BUG 

    // invert (using factorization computed by potrf()) 
    inline 
    int potri (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, float* A, int const lda) 
    {
      return clapack_spotri (Order, Uplo, N, A, lda);
    }

    inline 
    int potri (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, double* A, int const lda) 
    {
      return clapack_dpotri (Order, Uplo, N, A, lda);
    }

    inline 
    int potri (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, traits::complex_f* A, int const lda) 
    {
      return clapack_cpotri (Order, Uplo, N, static_cast<void*> (A), lda);
    }

    inline 
    int potri (CBLAS_ORDER const Order, CBLAS_UPLO const Uplo,
               int const N, traits::complex_d* A, int const lda) 
    {
      return clapack_zpotri (Order, Uplo, N, static_cast<void*> (A), lda);
    }


  }} // namepaces detail & atlas

}}} 


#endif // BOOST_NUMERIC_BINDINGS_CLAPACK_OVERLOADS_HPP
