/*
 * 
 * Copyright (c) Kresimir Fresl 2003 
 *
 * 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.
 *
 */

/* for UMFPACK Copyright, License and Availability see umfpack_inc.hpp */ 


#ifndef BOOST_NUMERIC_BINDINGS_UMFPACK_OVERLOADS_HPP
#define BOOST_NUMERIC_BINDINGS_UMFPACK_OVERLOADS_HPP

#include <boost/numeric/bindings/umfpack/umfpack_inc.hpp>
#include <boost/numeric/bindings/detail/array.hpp>
#include <boost/numeric/bindings/traits/detail/utils.hpp>
#include <cassert>

namespace boost { namespace numeric { namespace bindings { 

  namespace umfpack { namespace detail {

    ////////////////////////////
    // UMFPACK primary routines:
    ////////////////////////////

    // symbolic 

    inline
    int symbolic (int n_row, int n_col, 
                  int const* Ap, int const* Ai, double const* Ax, 
                  void **Symbolic, double const* Control, double* Info) 
    {
      return umfpack_di_symbolic (n_row, n_col, Ap, Ai, Ax, 
                                  Symbolic, Control, Info); 
    }

    inline
    int symbolic (int n_row, int n_col,
                  int const* Ap, int const* Ai, traits::complex_d const* Ax, 
                  void **Symbolic, double const* Control, double* Info)
    {
      int nnz = Ap[n_col];
      bindings::detail::array<double> Axr (nnz); 
      if (!Axr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Axi (nnz); 
      if (!Axi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Ax, Ax+nnz, 
                                   Axr.storage(), Axi.storage()); 
      return umfpack_zi_symbolic (n_row, n_col, Ap, Ai, 
                                  Axr.storage(), Axi.storage(), 
                                  Symbolic, Control, Info); 
    }


    // numeric 

    inline
    int numeric (int, int, 
                 int const* Ap, int const* Ai, double const* Ax, 
                 void *Symbolic, void **Numeric, 
                 double const* Control, double* Info) 
    {
      return umfpack_di_numeric (Ap, Ai, Ax, Symbolic, Numeric, Control, Info);
    }

    inline
    int numeric (int, int n_col, 
                 int const* Ap, int const* Ai, traits::complex_d const* Ax, 
                 void *Symbolic, void **Numeric,
                 double const* Control, double* Info)
    {
      int nnz = Ap[n_col];
      bindings::detail::array<double> Axr (nnz); 
      if (!Axr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Axi (nnz); 
      if (!Axi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Ax, Ax+nnz, 
                                   Axr.storage(), Axi.storage()); 
      return umfpack_zi_numeric (Ap, Ai, Axr.storage(), Axi.storage(), 
                                 Symbolic, Numeric, Control, Info); 
    }


    // solve 

    inline
    int solve (int sys, int, 
               int const* Ap, int const* Ai, double const* Ax,
               double* X, double const* B, void *Numeric,
               double const* Control, double* Info)
    {
      return umfpack_di_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info);
    }

    inline
    int solve (int sys, int n, 
               int const* Ap, int const* Ai, traits::complex_d const* Ax, 
               traits::complex_d* X, traits::complex_d const* B, 
               void *Numeric, double const* Control, double* Info)
    {
      int nnz = Ap[n];
      bindings::detail::array<double> Axr (nnz); 
      if (!Axr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Axi (nnz); 
      if (!Axi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Ax, Ax+nnz, 
                                   Axr.storage(), Axi.storage()); 
      bindings::detail::array<double> Br (n); 
      if (!Br.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Bi (n); 
      if (!Bi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (B, B+n, 
                                   Br.storage(), Bi.storage()); 
      bindings::detail::array<double> Xr (n); 
      if (!Xr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Xi (n); 
      if (!Xi.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = umfpack_zi_solve (sys, Ap, Ai, 
                                     Axr.storage(), Axi.storage(), 
                                     Xr.storage(), Xi.storage(), 
                                     Br.storage(), Bi.storage(), 
                                     Numeric, Control, Info);
      if (status != UMFPACK_OK) return status; 
      traits::detail::interlace (Xr.storage(), Xr.storage() + n,
                                 Xi.storage(), X); 
      return status; 
    }


    // free_symbolic 

    inline
    void free_symbolic (double, int, void **Symbolic) {
      umfpack_di_free_symbolic (Symbolic); 
    }
    inline
    void free_symbolic (traits::complex_d const&, int, void **Symbolic) {
      umfpack_zi_free_symbolic (Symbolic); 
    }


    // free_numeric

    inline
    void free_numeric (double, int, void **Numeric) {
      umfpack_di_free_numeric (Numeric); 
    }
    inline
    void free_numeric (traits::complex_d const&, int, void **Numeric) {
      umfpack_zi_free_numeric (Numeric); 
    }


    ////////////////////////////////
    // UMFPACK alternative routines:
    ////////////////////////////////

    // default control 

    inline
    void defaults (double, int, double* Control) {
      umfpack_di_defaults (Control); 
    }
    inline
    void defaults (traits::complex_d const&, int, double* Control) {
      umfpack_zi_defaults (Control); 
    }


    // symbolic with specified column preordering

    inline
    int qsymbolic (int n_row, int n_col, 
                   int const* Ap, int const* Ai, double const* Ax, 
                   int const* Qinit, 
                   void **Symbolic, double const* Control, double* Info) 
    {
      return umfpack_di_qsymbolic (n_row, n_col, Ap, Ai, Ax, Qinit, 
                                  Symbolic, Control, Info); 
    }

    inline
    int qsymbolic (int n_row, int n_col,
                   int const* Ap, int const* Ai, traits::complex_d const* Ax, 
                   int const* Qinit, 
                   void **Symbolic, double const* Control, double* Info)
    {
      int nnz = Ap[n_col];
      bindings::detail::array<double> Axr (nnz); 
      if (!Axr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Axi (nnz); 
      if (!Axi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Ax, Ax+nnz, 
                                   Axr.storage(), Axi.storage()); 
      return umfpack_zi_qsymbolic (n_row, n_col, Ap, Ai, 
                                   Axr.storage(), Axi.storage(), 
                                   Qinit, Symbolic, Control, Info); 
    }


    ///////////////////////////////////////
    // UMFPACK matrix manipulation routines
    ///////////////////////////////////////

    // triplet (coordinate) to compressed column 

    inline
    int triplet_to_col (int n_row, int n_col, int nz,
                        int const* Ti, int const* Tj, double const* Tx, 
                        int* Ap, int* Ai, double* Ax, int* Map) 
    {
      return umfpack_di_triplet_to_col (n_row, n_col, nz, 
                                        Ti, Tj, Tx,
                                        Ap, Ai, Ax, Map);
    }

    inline 
    int triplet_to_col (int n_row, int n_col, int nz,
                        int const* Ti, int const* Tj, 
                        traits::complex_d const* Tx, 
                        int* Ap, int* Ai, traits::complex_d* Ax, 
                        int* Map)
    {
      assert (Tx == 0 && Ax == 0 || Tx != 0 && Ax != 0); 
      double *Txr = 0, *Txi = 0;
      if (Tx != 0) {
        bindings::detail::array<double> ATxr (nz); 
        if (!ATxr.valid()) return UMFPACK_ERROR_out_of_memory;
        bindings::detail::array<double> ATxi (nz); 
        if (!ATxi.valid()) return UMFPACK_ERROR_out_of_memory;
        Txr = ATxr.storage();
        Txi = ATxi.storage(); 
        traits::detail::disentangle (Tx, Tx+nz, Txr, Txi); 
      }
      double *Axr = 0, *Axi = 0;
      if (Ax != 0) {
        bindings::detail::array<double> AAxr (nz); 
        if (!AAxr.valid()) return UMFPACK_ERROR_out_of_memory;
        bindings::detail::array<double> AAxi (nz); 
        if (!AAxi.valid()) return UMFPACK_ERROR_out_of_memory;
        Axr = AAxr.storage();
        Axi = AAxi.storage(); 
      } 
      int status; 
      status = umfpack_zi_triplet_to_col (n_row, n_col, nz, 
                                          Ti, Tj, Txr, Txi,
                                          Ap, Ai, Axr, Axi, Map);
      if (Ax != 0) {
        if (status != UMFPACK_OK) return status; 
        traits::detail::interlace (Axr, Axr + nz, Axi, Ax); 
      }
      return status; 
    }


    // scale

    inline
    int scale (int, double* X, double const* B, void* Numeric) {
      return umfpack_di_scale (X, B, Numeric); 
    } 

    inline
    int scale (int n, traits::complex_d* X, 
               traits::complex_d const* B, void* Numeric) 
    {
      bindings::detail::array<double> Br (n); 
      if (!Br.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Bi (n); 
      if (!Bi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (B, B+n, 
                                   Br.storage(), Bi.storage()); 
      bindings::detail::array<double> Xr (n); 
      if (!Xr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Xi (n); 
      if (!Xi.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = umfpack_zi_scale (Xr.storage(), Xi.storage(), 
                                     Br.storage(), Bi.storage(), 
                                     Numeric); 
      if (status != UMFPACK_OK) return status; 
      traits::detail::interlace (Xr.storage(), Xr.storage() + n,
                                 Xi.storage(), X); 
      return status; 
    } 


    //////////////////////////////
    // UMFPACK reporting routines:
    //////////////////////////////

    // report status

    inline
    void report_status (double, int, double const* Control, int status) {
      umfpack_di_report_status (Control, status); 
    }
    inline
    void report_status (traits::complex_d const&, int, 
                        double const* Control, int status) 
    {
      umfpack_zi_report_status (Control, status); 
    }


    // report control

    inline
    void report_control (double, int, double const* Control) {
      umfpack_di_report_control (Control); 
    }
    inline
    void 
    report_control (traits::complex_d const&, int, double const* Control) {
      umfpack_zi_report_control (Control); 
    }


    // report info

    inline
    void report_info (double, int, double const* Control, double const* Info) {
      umfpack_di_report_info (Control, Info); 
    }
    inline
    void report_info (traits::complex_d const&, int, 
                      double const* Control, double const* Info) 
    {
      umfpack_zi_report_info (Control, Info); 
    }


    // report matrix 

    inline 
    int report_matrix (int n_row, int n_col, 
                       int const* Ap, int const* Ai, double const* Ax,
                       int col_form, double const* Control) 
    {
      return umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, 
                                       col_form, Control); 
    }

    inline 
    int report_matrix (int n_row, int n_col, 
                       int const* Ap, int const* Ai, 
                       traits::complex_d const* Ax, 
                       int col_form, double const* Control) 
    {
      int nnz = Ap[n_col];
      bindings::detail::array<double> Axr (nnz); 
      if (!Axr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Axi (nnz); 
      if (!Axi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Ax, Ax+nnz, 
                                   Axr.storage(), Axi.storage()); 
      return umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, 
                                       Axr.storage(), Axi.storage(), 
                                       col_form, Control); 
    }


    // report triplet (coordinate)

    inline
    int report_triplet (int n_row, int n_col, int nz,
                        int const* Ti, int const* Tj, double const* Tx,
                        double const* Control) 
    {
      return umfpack_di_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Control);
    }

    inline
    int report_triplet (int n_row, int n_col, int nz,
                        int const* Ti, int const* Tj, 
                        traits::complex_d const* Tx, 
                        double const* Control) 
    {
      bindings::detail::array<double> Txr (nz); 
      if (!Txr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Txi (nz); 
      if (!Txi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (Tx, Tx+nz, 
                                   Txr.storage(), Txi.storage()); 
      return umfpack_zi_report_triplet (n_row, n_col, nz, Ti, Tj, 
                                        Txr.storage(), Txi.storage(), 
                                        Control);
    }


    // report vector 

    inline
    int report_vector (int n, double const* X, double const* Control) {
      return umfpack_di_report_vector (n, X, Control);
    }

    inline
    int report_vector (int n, traits::complex_d const* X, 
                       double const* Control) 
    {
#if 0
      // see UMFPACK v 4.1 User Guide
      bindings::detail::array<double> Xr (n); 
      if (!Xr.valid()) return UMFPACK_ERROR_out_of_memory;
      bindings::detail::array<double> Xi (n); 
      if (!Xi.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::disentangle (X, X+n, 
                                   Xr.storage(), Xi.storage()); 
      return umfpack_zi_report_vector (n, Xr.storage(), Xi.storage(), Control);
#endif 
      return umfpack_zi_report_vector (n, 
                                       reinterpret_cast<double const*> (X), 
                                       reinterpret_cast<double const*> (0), 
                                       Control);
    }


    // report numeric 

    inline
    int report_numeric (double, int, void* Numeric, double const* Control) {
      return umfpack_di_report_numeric (Numeric, Control); 
    }
    inline
    int report_numeric (traits::complex_d const&, int, 
                        void* Numeric, double const* Control) 
    {
      return umfpack_zi_report_numeric (Numeric, Control); 
    }


    // report symbolic 

    inline
    int report_symbolic (double, int, void* Symbolic, double const* Control) {
      return umfpack_di_report_symbolic (Symbolic, Control); 
    }
    inline
    int report_symbolic (traits::complex_d const&, int, 
                         void* Symbolic, double const* Control) 
    {
      return umfpack_zi_report_symbolic (Symbolic, Control); 
    }


    // report permutation vector

    inline
    int report_perm (double, int, int np, 
                     int const* Perm, double const* Control) {
      return umfpack_di_report_perm (np, Perm, Control); 
    }
    inline
    int report_perm (traits::complex_d const&, int, int np, 
                     int const* Perm, double const* Control) {
      return umfpack_zi_report_perm (np, Perm, Control); 
    }

  }}

}}}

#endif // BOOST_NUMERIC_BINDINGS_UMFPACK_OVERLOADS_HPP
