/*
 *
 * Copyright (c) Kresimir Fresl 2003
 *
 * Permission to copy, modify, use and distribute this software
 * for any non-commercial or commercial purpose is granted provided
 * that this license appear on all copies of the software source code.
 *
 * Author assumes no responsibility whatsoever for its use and makes
 * no guarantees about its quality, correctness or reliability.
 *
 * 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_HPP
#define BOOST_NUMERIC_BINDINGS_UMFPACK_HPP

#include <boost/noncopyable.hpp>
#include <boost/numeric/bindings/traits/traits.hpp>
#include <boost/numeric/bindings/traits/sparse_traits.hpp>
#include <boost/numeric/bindings/umfpack/umfpack_overloads.hpp>

namespace boost { namespace numeric { namespace bindings {  namespace umfpack {

  template <typename T = double>
  struct symbolic_type : private noncopyable {
    void *ptr;
    ~symbolic_type() {
      if (ptr)
        detail::free_symbolic (T(), 0, &ptr);
    }
    void free() {
      if (ptr)
        detail::free_symbolic (T(), 0, &ptr);
      ptr = 0;
    }
  };

  template <typename T>
  void free (symbolic_type<T>& s) { s.free(); }

  template <typename T = double>
  struct numeric_type : private noncopyable {
    void *ptr;
    ~numeric_type() {
      if (ptr)
        detail::free_numeric (T(), 0, &ptr);
    }
    void free() {
      if (ptr)
        detail::free_numeric (T(), 0, &ptr);
      ptr = 0;
    }
  };

  template <typename T>
  void free (numeric_type<T>& n) { n.free(); }

  template <typename T = double>
  struct control_type : private noncopyable {
    double ptr[UMFPACK_CONTROL];
    control_type() { detail::defaults (T(), 0, ptr); }
    double operator[] (int i) const { return ptr[i]; }
    double& operator[] (int i) { return ptr[i]; }
    void defaults() { detail::defaults (T(), 0, ptr); }
  };

  template <typename T>
  void defaults (control_type<T>& c) { c.defaults(); }

  template <typename T = double>
  struct info_type : private noncopyable {
    double ptr[UMFPACK_INFO];
    double operator[] (int i) const { return ptr[i]; }
    double& operator[] (int i) { return ptr[i]; }
  };

  /////////////////////////////////////
  // solving system of linear equations
  /////////////////////////////////////

  // symbolic
  /*
   * Given nonzero pattern of a sparse matrix A in column-oriented form,
   * umfpack_*_symbolic performs a column pre-ordering to reduce fill-in
   * (using COLAMD or AMD) and a symbolic factorisation.  This is required
   * before the matrix can be numerically factorised with umfpack_*_numeric.
   */
  namespace detail {

    template <typename MatrA>
    inline
    int symbolic (traits::compressed_t,
                  MatrA const& A, void **Symbolic,
                  double const* Control = 0, double* Info = 0)
    {
      return detail::symbolic (traits::spmatrix_size1 (A),
                               traits::spmatrix_size2 (A),
                               traits::spmatrix_index1_storage (A),
                               traits::spmatrix_index2_storage (A),
                               traits::spmatrix_value_storage (A),
                               Symbolic, Control, Info);
    }

    template <typename MatrA, typename QVec>
    inline
    int symbolic (traits::compressed_t,
                  MatrA const& A, QVec const& Qinit, void **Symbolic,
                  double const* Control = 0, double* Info = 0)
    {
      return detail::qsymbolic (traits::spmatrix_size1 (A),
                                traits::spmatrix_size2 (A),
                                traits::spmatrix_index1_storage (A),
                                traits::spmatrix_index2_storage (A),
                                traits::spmatrix_value_storage (A),
                                traits::vector_storage (Qinit),
                                Symbolic, Control, Info);
    }

    template <typename MatrA>
    inline
    int symbolic (traits::coordinate_t,
                  MatrA const& A, void **Symbolic,
                  double const* Control = 0, double* Info = 0)
    {
      int n_row = traits::spmatrix_size1 (A);
      int n_col = traits::spmatrix_size2 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n_col+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n_row, n_col, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      return detail::symbolic (n_row, n_col,
                               Ap.storage(), Ai.storage(),
                               traits::spmatrix_value_storage (A),
                               Symbolic, Control, Info);
    }

    template <typename MatrA, typename QVec>
    inline
    int symbolic (traits::coordinate_t,
                  MatrA const& A, QVec const& Qinit, void **Symbolic,
                  double const* Control = 0, double* Info = 0)
    {
      int n_row = traits::spmatrix_size1 (A);
      int n_col = traits::spmatrix_size2 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n_col+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n_row, n_col, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      return detail::qsymbolic (n_row, n_col,
                                Ap.storage(), Ai.storage(),
                                traits::spmatrix_value_storage (A),
                                traits::vector_storage (Qinit),
                                Symbolic, Control, Info);
    }

  } // detail

  template <typename MatrA>
  inline
  int symbolic (MatrA const& A,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    return detail::symbolic (storage_f(), A, &Symbolic.ptr, Control, Info);
  }

  template <typename MatrA>
  inline
  int symbolic (MatrA const& A,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                control_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                > const& Control,
                info_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Info)
  {
    return symbolic (A, Symbolic, Control.ptr, Info.ptr);
  }

  template <typename MatrA>
  inline
  int symbolic (MatrA const& A,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                control_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                > const& Control)
  {
    return symbolic (A, Symbolic, Control.ptr);
  }

  template <typename MatrA, typename QVec>
  inline
  int symbolic (MatrA const& A, QVec const& Qinit,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    assert (traits::spmatrix_size2 (A) == traits::vector_size (Qinit));

    return detail::symbolic (storage_f(), A, Qinit,
                             &Symbolic.ptr, Control, Info);
  }

  template <typename MatrA, typename QVec>
  inline
  int symbolic (MatrA const& A, QVec const& Qinit,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                control_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                > const& Control,
                info_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Info)
  {
    return symbolic (A, Qinit, Symbolic, Control.ptr, Info.ptr);
  }

  template <typename MatrA, typename QVec>
  inline
  int symbolic (MatrA const& A, QVec const& Qinit,
                symbolic_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                >& Symbolic,
                control_type<
                  typename traits::sparse_matrix_traits<MatrA>::value_type
                > const& Control)
  {
    return symbolic (A, Qinit, Symbolic, Control.ptr);
  }

  // numeric
  /*
   * Given a sparse matrix A in column-oriented form, and a symbolic analysis
   * computed by umfpack_*_*symbolic, the umfpack_*_numeric routine performs
   * the numerical factorisation, PAQ=LU, PRAQ=LU, or P(R\A)Q=LU, where P
   * and Q are permutation matrices (represented as permutation vectors),
   * R is the row scaling, L is unit-lower triangular, and U is upper
   * triangular.  This is required before the system Ax=b (or other related
   * linear systems) can be solved.
   */
  namespace detail {

    template <typename MatrA>
    inline
    int numeric (traits::compressed_t, MatrA const& A,
                 void *Symbolic, void** Numeric,
                 double const* Control = 0, double* Info = 0)
    {
      return detail::numeric (traits::spmatrix_size1 (A),
                              traits::spmatrix_size2 (A),
                              traits::spmatrix_index1_storage (A),
                              traits::spmatrix_index2_storage (A),
                              traits::spmatrix_value_storage (A),
                              Symbolic, Numeric, Control, Info);
    }

    template <typename MatrA>
    inline
    int numeric (traits::coordinate_t, MatrA const& A,
                 void *Symbolic, void** Numeric,
                 double const* Control = 0, double* Info = 0)
    {
      int n_row = traits::spmatrix_size1 (A);
      int n_col = traits::spmatrix_size2 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n_col+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n_row, n_col, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      return detail::numeric (n_row, n_col,
                              Ap.storage(), Ai.storage(),
                              traits::spmatrix_value_storage (A),
                              Symbolic, Numeric, Control, Info);
    }

  } // detail

  template <typename MatrA>
  inline
  int numeric (MatrA const& A,
               symbolic_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               > const& Symbolic,
               numeric_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               >& Numeric,
               double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    return detail::numeric (storage_f(), A,
                            Symbolic.ptr, &Numeric.ptr, Control, Info);
  }

  template <typename MatrA>
  inline
  int numeric (MatrA const& A,
               symbolic_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               > const& Symbolic,
               numeric_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               >& Numeric,
               control_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               > const& Control,
               info_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               >& Info)

  {
    // g++ (3.2) is unable to distinguish
    //           function numeric() and namespace boost::numeric ;o)
    return umfpack::numeric (A, Symbolic, Numeric, Control.ptr, Info.ptr);
  }

  template <typename MatrA>
  inline
  int numeric (MatrA const& A,
               symbolic_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               > const& Symbolic,
               numeric_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               >& Numeric,
               control_type<
                 typename traits::sparse_matrix_traits<MatrA>::value_type
               > const& Control)
  {
    return umfpack::numeric (A, Symbolic, Numeric, Control.ptr);
  }

  // factor
  /*
   * symbolic and numeric
   */
  namespace detail {

    template <typename MatrA>
    inline
    int factor (traits::compressed_t, MatrA const& A,
                void** Numeric, double const* Control = 0, double* Info = 0)
    {
      symbolic_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Symbolic;

      int status;
      status = detail::symbolic (traits::spmatrix_size1 (A),
                                 traits::spmatrix_size2 (A),
                                 traits::spmatrix_index1_storage (A),
                                 traits::spmatrix_index2_storage (A),
                                 traits::spmatrix_value_storage (A),
                                 &Symbolic.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      return detail::numeric (traits::spmatrix_size1 (A),
                              traits::spmatrix_size2 (A),
                              traits::spmatrix_index1_storage (A),
                              traits::spmatrix_index2_storage (A),
                              traits::spmatrix_value_storage (A),
                              Symbolic.ptr, Numeric, Control, Info);
    }

    template <typename MatrA>
    inline
    int factor (traits::coordinate_t, MatrA const& A,
                void** Numeric, double const* Control = 0, double* Info = 0)
    {
      int n_row = traits::spmatrix_size1 (A);
      int n_col = traits::spmatrix_size2 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n_col+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n_row, n_col, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      symbolic_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Symbolic;

      status = detail::symbolic (n_row, n_col,
                                 Ap.storage(), Ai.storage(),
                                 traits::spmatrix_value_storage (A),
                                 &Symbolic.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      return detail::numeric (n_row, n_col,
                              Ap.storage(), Ai.storage(),
                              traits::spmatrix_value_storage (A),
                              Symbolic.ptr, Numeric, Control, Info);
    }

  } // detail

  template <typename MatrA>
  inline
  int factor (MatrA const& A,
              numeric_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              >& Numeric,
              double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    return detail::factor (storage_f(), A, &Numeric.ptr, Control, Info);
  }

  template <typename MatrA>
  inline
  int factor (MatrA const& A,
              numeric_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              >& Numeric,
              control_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              > const& Control,
              info_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              >& Info)
  {
    return factor (A, Numeric, Control.ptr, Info.ptr);
  }

  template <typename MatrA>
  inline
  int factor (MatrA const& A,
              numeric_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              >& Numeric,
              control_type<
                typename traits::sparse_matrix_traits<MatrA>::value_type
              > const& Control)
  {
    return factor (A, Numeric, Control.ptr);
  }

  // solve
  /*
   * Given LU factors computed by umfpack_*_numeric and the right-hand-side,
   * B, solve a linear system for the solution X.  Iterative refinement is
   * optionally performed.  Only square systems are handled.
   */
  namespace detail {

    template <typename MatrA, typename VecX, typename VecB>
    inline
    int solve (traits::compressed_t, int sys,
               MatrA const& A, VecX& X, VecB const& B,
               void *Numeric, double const* Control = 0, double* Info = 0)
    {
      return detail::solve (sys, traits::spmatrix_size1 (A),
                            traits::spmatrix_index1_storage (A),
                            traits::spmatrix_index2_storage (A),
                            traits::spmatrix_value_storage (A),
                            traits::vector_storage (X),
                            traits::vector_storage (B),
                            Numeric, Control, Info);
    }

    template <typename MatrA, typename VecX, typename VecB>
    inline
    int solve (traits::coordinate_t, int sys,
               MatrA const& A, VecX& X, VecB const& B,
               void *Numeric, double const* Control = 0, double* Info = 0)
    {

      int n = traits::spmatrix_size1 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n, n, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      return detail::solve (sys, n, Ap.storage(), Ai.storage(),
                            traits::spmatrix_value_storage (A),
                            traits::vector_storage (X),
                            traits::vector_storage (B),
                            Numeric, Control, Info);
    }

  } // detail

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (int sys, MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    assert (traits::spmatrix_size1 (A) == traits::spmatrix_size1 (A));
    assert (traits::spmatrix_size2 (A) == traits::vector_size (X));
    assert (traits::spmatrix_size2 (A) == traits::vector_size (B));

    return detail::solve (storage_f(), sys, A, X, B,
                          Numeric.ptr, Control, Info);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (int sys, MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             control_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Control,
             info_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             >& Info)
  {
    return solve (sys, A, X, B, Numeric, Control.ptr, Info.ptr);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (int sys, MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             control_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Control)
  {
    return solve (sys, A, X, B, Numeric, Control.ptr);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             double const* Control = 0, double* Info = 0)
  {
    return solve (UMFPACK_A, A, X, B, Numeric, Control, Info);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             control_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Control,
             info_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             >& Info)
  {
    return solve (UMFPACK_A, A, X, B, Numeric,
                  Control.ptr, Info.ptr);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int solve (MatrA const& A, VecX& X, VecB const& B,
             numeric_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Numeric,
             control_type<
               typename traits::sparse_matrix_traits<MatrA>::value_type
             > const& Control)
  {
    return solve (UMFPACK_A, A, X, B, Numeric, Control.ptr);
  }

  // umf_solve
  /*
   * symbolic, numeric and solve
   */
  namespace detail {

    template <typename MatrA, typename VecX, typename VecB>
    inline
    int umf_solve (traits::compressed_t,
                   MatrA const& A, VecX& X, VecB const& B,
                   double const* Control = 0, double* Info = 0)
    {
      symbolic_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Symbolic;
      numeric_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Numeric;

      int status;
      status = detail::symbolic (traits::spmatrix_size1 (A),
                                 traits::spmatrix_size2 (A),
                                 traits::spmatrix_index1_storage (A),
                                 traits::spmatrix_index2_storage (A),
                                 traits::spmatrix_value_storage (A),
                                 &Symbolic.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      status = detail::numeric (traits::spmatrix_size1 (A),
                                traits::spmatrix_size2 (A),
                                traits::spmatrix_index1_storage (A),
                                traits::spmatrix_index2_storage (A),
                                traits::spmatrix_value_storage (A),
                                Symbolic.ptr, &Numeric.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      return detail::solve (UMFPACK_A, traits::spmatrix_size1 (A),
                            traits::spmatrix_index1_storage (A),
                            traits::spmatrix_index2_storage (A),
                            traits::spmatrix_value_storage (A),
                            traits::vector_storage (X),
                            traits::vector_storage (B),
                            Numeric.ptr, Control, Info);
    }

    template <typename MatrA, typename VecX, typename VecB>
    inline
    int umf_solve (traits::coordinate_t,
                   MatrA const& A, VecX& X, VecB const& B,
                   double const* Control = 0, double* Info = 0)
    {
      int n_row = traits::spmatrix_size1 (A);
      int n_col = traits::spmatrix_size2 (A);
      int nnz = traits::spmatrix_num_nonzeros (A);

      typedef typename traits::sparse_matrix_traits<MatrA>::value_type val_t;

      int const* Ti = traits::spmatrix_index2_storage (A);
      int const* Tj = traits::spmatrix_index1_storage (A);
      traits::detail::array<int> Ap (n_col+1);
      if (!Ap.valid()) return UMFPACK_ERROR_out_of_memory;
      traits::detail::array<int> Ai (nnz);
      if (!Ai.valid()) return UMFPACK_ERROR_out_of_memory;

      int status = detail::triplet_to_col (n_row, n_col, nnz,
                                           Ti, Tj, static_cast<val_t*> (0),
                                           Ap.storage(), Ai.storage(),
                                           static_cast<val_t*> (0), 0);
      if (status != UMFPACK_OK) return status;

      symbolic_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Symbolic;
      numeric_type<typename traits::sparse_matrix_traits<MatrA>::value_type>
        Numeric;

      status = detail::symbolic (n_row, n_col,
                                 Ap.storage(), Ai.storage(),
                                 traits::spmatrix_value_storage (A),
                                 &Symbolic.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      status = detail::numeric (n_row, n_col,
                                Ap.storage(), Ai.storage(),
                                traits::spmatrix_value_storage (A),
                                Symbolic.ptr, &Numeric.ptr, Control, Info);
      if (status != UMFPACK_OK) return status;

      return detail::solve (UMFPACK_A, n_row, Ap.storage(), Ai.storage(),
                            traits::spmatrix_value_storage (A),
                            traits::vector_storage (X),
                            traits::vector_storage (B),
                            Numeric.ptr, Control, Info);
    }

  } // detail

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int umf_solve (MatrA const& A, VecX& X, VecB const& B,
                 double const* Control = 0, double* Info = 0)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    assert (traits::spmatrix_size1 (A) == traits::spmatrix_size1 (A));
    assert (traits::spmatrix_size2 (A) == traits::vector_size (X));
    assert (traits::spmatrix_size2 (A) == traits::vector_size (B));

    return detail::umf_solve (storage_f(), A, X, B, Control, Info);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int umf_solve (MatrA const& A, VecX& X, VecB const& B,
                 control_type<
                   typename traits::sparse_matrix_traits<MatrA>::value_type
                 > const& Control,
                 info_type<
                   typename traits::sparse_matrix_traits<MatrA>::value_type
                 >& Info)
  {
    return umf_solve (A, X, B, Control.ptr, Info.ptr);
  }

  template <typename MatrA, typename VecX, typename VecB>
  inline
  int umf_solve (MatrA const& A, VecX& X, VecB const& B,
                 control_type<
                   typename traits::sparse_matrix_traits<MatrA>::value_type
                 > const& Control)
  {
    return umf_solve (A, X, B, Control.ptr);
  }

  ///////////////////////
  // matrix manipulations
  ///////////////////////

  // scale

  template <typename VecX, typename VecB>
  inline
  int scale (VecX& X, VecB const& B,
             numeric_type<
               typename traits::vector_traits<VecB>::value_type
             > const& Numeric)
  {
    return detail::scale (traits::vector_size (B),
                          traits::vector_storage (X),
                          traits::vector_storage (B),
                          Numeric.ptr);
  }

  ////////////
  // reporting
  ////////////

  // report status

  template <typename T>
  inline
  void report_status (control_type<T> const& Control, int status) {
    detail::report_status (T(), 0, Control.ptr, status);
  }

#if 0
  template <typename T>
  inline
  void report_status (int printing_level, int status) {
    control_type<T> Control;
    Control[UMFPACK_PRL] = printing_level;
    detail::report_status (T(), 0, Control.ptr, status);
  }
  template <typename T>
  inline
  void report_status (int status) {
    control_type<T> Control;
    detail::report_status (T(), 0, Control.ptr, status);
  }
#endif

  // report control

  template <typename T>
  inline
  void report_control (control_type<T> const& Control) {
    detail::report_control (T(), 0, Control.ptr);
  }

  // report info

  template <typename T>
  inline
  void report_info (control_type<T> const& Control, info_type<T> const& Info) {
    detail::report_info (T(), 0, Control.ptr, Info.ptr);
  }

#if 0
  template <typename T>
  inline
  void report_info (int printing_level, info_type<T> const& Info) {
    control_type<T> Control;
    Control[UMFPACK_PRL] = printing_level;
    detail::report_info (T(), 0, Control.ptr, Info.ptr);
  }
  template <typename T>
  inline
  void report_info (info_type<T> const& Info) {
    control_type<T> Control;
    detail::report_info (T(), 0, Control.ptr, Info.ptr);
  }
#endif

  // report matrix (compressed column and coordinate)

  namespace detail {

    template <typename MatrA>
    inline
    int report_matrix (traits::compressed_t, MatrA const& A,
                       double const* Control)
    {
      return detail::report_matrix (traits::spmatrix_size1 (A),
                                    traits::spmatrix_size2 (A),
                                    traits::spmatrix_index1_storage (A),
                                    traits::spmatrix_index2_storage (A),
                                    traits::spmatrix_value_storage (A),
                                    1, Control);
    }

    template <typename MatrA>
    inline
    int report_matrix (traits::coordinate_t, MatrA const& A,
                       double const* Control)
    {
      return detail::report_triplet (traits::spmatrix_size1 (A),
                                     traits::spmatrix_size2 (A),
                                     traits::spmatrix_num_nonzeros (A),
                                     traits::spmatrix_index1_storage (A),
                                     traits::spmatrix_index2_storage (A),
                                     traits::spmatrix_value_storage (A),
                                     Control);
    }

  } // detail

  template <typename MatrA>
  inline
  int report_matrix (MatrA const& A,
                     control_type<
                       typename traits::sparse_matrix_traits<MatrA>::value_type
                     > const& Control)
  {
#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::matrix_structure,
      traits::general_t
    >::value));
    BOOST_STATIC_ASSERT((boost::is_same<
      typename traits::sparse_matrix_traits<MatrA>::ordering_type,
      traits::column_major_t
    >::value));
    BOOST_STATIC_ASSERT(traits::sparse_matrix_traits<MatrA>::index_base == 0);
#endif

    typedef
      typename traits::sparse_matrix_traits<MatrA>::storage_format storage_f;

#ifndef BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK
    BOOST_STATIC_ASSERT(
      (boost::is_same<storage_f, traits::compressed_t>::value
       ||
       boost::is_same<storage_f, traits::coordinate_t>::value
       ));
#endif

    return detail::report_matrix (storage_f(), A, Control.ptr);
  }

  // report vector

  template <typename VecX>
  inline
  int report_vector (VecX const& X,
                     control_type<
                       typename traits::vector_traits<VecX>::value_type
                     > const& Control)
  {
    return detail::report_vector (traits::vector_size (X),
                                  traits::vector_storage (X),
                                  Control.ptr);
  }

  // report numeric

  template <typename T>
  inline
  int report_numeric (numeric_type<T> const& Numeric,
                      control_type<T> const& Control)
  {
    return detail::report_numeric (T(), 0, Numeric.ptr, Control.ptr);
  }

  // report symbolic

  template <typename T>
  inline
  int report_symbolic (symbolic_type<T> const& Symbolic,
                       control_type<T> const& Control)
  {
    return detail::report_symbolic (T(), 0, Symbolic.ptr, Control.ptr);
  }

  // report permutation vector

  template <typename VecP, typename T>
  inline
  int report_permutation (VecP const& Perm, control_type<T> const& Control) {
    return detail::report_perm (T(), 0,
                                traits::vector_storage (Perm),
                                Control.ptr);
  }

}}}}

#endif // BOOST_NUMERIC_BINDINGS_UMFPACK_HPP


#ifndef CAT2MAP_HPP
#define CAT2MAP_HPP

#include <fstream>
#include <iostream>
#include <exception>
#include <vector>
#include <cmath>
#include <cassert>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/log/trivial.hpp>
#include <boost/tokenizer.hpp>

#include <healpix_map.h>
#include <pointing.h>
#include <healpix_map_fitsio.h>
#include <datatypes.h>

/**
 * \class Cat2Map
 * \brief A class for converting catalogues to HEALPix maps
 *
 * This class reads a catalogue and convert the information to a
 * HEALPix map.
 */
class Cat2Map
{
public:

    /**
     * \typedef boost::property_tree::ptree propertyTreeType
     * \brief defines the property tree type
     */
    typedef boost::property_tree::ptree propertyTreeType;

    /**
     * \typedef Healpix_Map<double> mapType
     * \brief defines the HEALPix map type
     */
    typedef Healpix_Map<double> mapType;

    static const constexpr double deg2rad = M_PI/double(180);/**< factor for converting degree to radians */
    static const constexpr double rotPhi = double(0); /**< rotaion to phi required for convertion from ra */

    /**
     * \brief The default constructor
     *
     * \param iniFileName name of the input configuration file
     */
    explicit Cat2Map(std::string const& iniFileName)
    {
        BOOST_LOG_TRIVIAL(info) << std::string("Reading the ini file ") + std::string(iniFileName);
        boost::property_tree::ini_parser::read_ini(iniFileName,mPropTree);

        // set the output maps
        BOOST_LOG_TRIVIAL(info) << "Resulution of the output map is "<<mPropTree.get<std::string>("output.n_side");

        int nSide = mPropTree.get<int>("output.n_side");
        mMapN.SetNside(nSide,RING);
        mMapNNInv.SetNside(nSide,RING);
        mMapE1.SetNside(nSide,RING);
        mMapE2.SetNside(nSide,RING);
        mMapE1NInv.SetNside(nSide,RING);
        mMapE2NInv.SetNside(nSide,RING);

        mMapN.fill(double(0));
        mMapNNInv.fill(double(0));
        mMapE1.fill(double(0));
        mMapE1.fill(double(0));
        mMapE1NInv.fill(double(0));
        mMapE2NInv.fill(double(0));

        // read the mask
        read_Healpix_map_from_fits(mPropTree.get<std::string>("input.mask_file_name"),mMask);

        try
        {
            std::string testMapFileName = mPropTree.get<std::string>("test.map_file_name");

            BOOST_LOG_TRIVIAL(info) << "Test map file specified as "<< testMapFileName;

            read_Healpix_map_from_fits(testMapFileName,mTestE1,int(2),int(2));
            read_Healpix_map_from_fits(testMapFileName,mTestE2,int(3),int(2));

            mDoTest = true;
        }
        catch(std::exception)
        {
            BOOST_LOG_TRIVIAL(info) << "No test map file specified.";
            mDoTest = false;
        }

        try
        {
            std::string testMapFileName = mPropTree.get<std::string>("output.z_bounds");
            BOOST_LOG_TRIVIAL(info) << "z bounds specified as "<< testMapFileName;
            typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
            boost::char_separator<char> sep(",");
            tokenizer tokens(testMapFileName, sep);

            for(tokenizer::iterator tokIter = tokens.begin(); tokIter !=tokens.end(); ++tokIter)
            {
                mZBounds.push_back(boost::lexical_cast<double>(*tokIter));
            }
        }
        catch(std::exception)
        {
            BOOST_LOG_TRIVIAL(info) << "No z bounds specified. All objects will be accumulated.";
        }

        if(mZBounds.size() != size_t(2))
        {
            std::string msg("The z-bounds should consist of two values. No more, no less.");
            throw std::runtime_error(msg);
        }

        if(mZBounds[0] >= mZBounds[1])
        {
            std::string msg("The upper bound should be greater than the lower bound.");
            throw std::runtime_error(msg);
        }

        mMisMatchCountE1 = size_t(0);
        mMisMatchCountE2 = size_t(0);
        mNumObsPix = size_t(0);
    }

    /**
     * \brief A function that accumulates the objects in a catalogue
     */
    void accumulate()
    {
        // open file for reading
        std::ifstream inputCatFile;
        inputCatFile.open( mPropTree.get<std::string>("input.catlogue_file_name").c_str(),std::ios::in );

        if(inputCatFile.is_open())
        {

            BOOST_LOG_TRIVIAL(info) << "Accumulating objects";
            BOOST_LOG_TRIVIAL(info) << "Number of rows to be skipped = " << mPropTree.get<std::string>("input.skip_rows");
            BOOST_LOG_TRIVIAL(info) << "Delimiter for separation is "<< mPropTree.get<std::string>("input.delimiter");

            size_t col_ra =  mPropTree.get<size_t>("input.col_ra");
            size_t col_dec =  mPropTree.get<size_t>("input.col_dec");
            size_t col_z = mPropTree.get<size_t>("input.col_z");
            size_t col_ellip_1 = mPropTree.get<size_t>("input.col_ellip_1");
            size_t col_ellip_2 = mPropTree.get<size_t>("input.col_ellip_2");

            BOOST_LOG_TRIVIAL(info) << "Column of ra values = "<< col_ra;
            BOOST_LOG_TRIVIAL(info) << "Column of dec values = "<< col_dec;
            BOOST_LOG_TRIVIAL(info) << "Column of z values = "<< col_z;
            BOOST_LOG_TRIVIAL(info) << "Column of ellip_1 values = "<< col_ellip_1;
            BOOST_LOG_TRIVIAL(info) << "Column of ellip_2 values = "<< col_ellip_2;

            size_t line_id = 0;
            while(!inputCatFile.eof())
            {
                std::string line;

                std::getline(inputCatFile,line);

                ++line_id;

                if(line_id > mPropTree.get<size_t>("input.skip_rows"))
                {
                    typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
                    boost::char_separator<char> sep(mPropTree.get<std::string>("input.delimiter").c_str());
                    tokenizer tokens(line, sep);

                    std::vector<double> ents;
                    for(tokenizer::iterator tokIter = tokens.begin(); tokIter !=tokens.end(); ++tokIter)
                    {
                        ents.push_back(boost::lexical_cast<double>(*tokIter));
                    }

                    if(ents.size()>0)
                    {
                        assert(col_ra<ents.size());
                        assert(col_dec<ents.size());
                        assert(col_ellip_1<ents.size());
                        assert(col_ellip_2<ents.size());

                        double ra = ents[col_ra];
                        double dec = ents[col_dec];
                        double z = ents[col_z];
                        double e1 = ents[col_ellip_1];
                        double e2 = ents[col_ellip_2];

                        double theta = -deg2rad*dec + M_PI*double(0.5);
                        double phi = deg2rad*(ra - rotPhi);

                        auto pix = mMapE1.ang2pix(pointing(theta,phi));

                        // check if the pixel falls in the masked region
                        if(mMask[pix]>0)
                        {
                            if(z >= mZBounds[0] and z < mZBounds[1])
                            {
                                if(mDoTest)
                                {
                                    // if testing accumulate the miss-match
                                    // between pixel values
                                    if(std::abs( (e1 - mTestE1[pix])/e1 ) >= 1e-5)
                                    {
                                        mMisMatchCountE1 += size_t(1);
                                    }

                                    if(std::abs( (e2 - mTestE2[pix])/e2 ) > 1e-5)
                                    {
                                        mMisMatchCountE2 += size_t(1);
                                    }
                                }

                                // do accumulation
                                mMapN[pix] += double(1);
                                mMapE1[pix] += e1;
                                mMapE2[pix] += e2;
                                mMapE1NInv[pix] += e1*e1;
                                mMapE2NInv[pix] += e2*e2;
                                //mNumObsPix += size_t(1); //TODO this is wrong
                            }
                        }

                    }

                }
                else
                {
                    BOOST_LOG_TRIVIAL(info) << "Skipping line " << line_id;
                }
            }

            // make it the average , also count the total objects
            double totObjs(0);
            for(auto pix = 0; pix<mMapN.Npix(); ++pix)
            {
                if(mMapN[pix]>0) // TODO how many gals we need to make an estimate
                {
                    mMapE1[pix] /= mMapN[pix];
                    mMapE2[pix] /= mMapN[pix];

                    // var = E(X^2) - E(X)^2
                    mMapE1NInv[pix] = ( mMapE1NInv[pix]/mMapN[pix] - mMapE1[pix]*mMapE1[pix]);
                    mMapE2NInv[pix] = ( mMapE2NInv[pix]/mMapN[pix] - mMapE2[pix]*mMapE2[pix]);

                    // nInv = 1/var
                    if(mMapE1NInv[pix] > double(0) and mMapE2NInv[pix] > double(0) )
                    {
                        mMapE1NInv[pix] = double(1)/mMapE1NInv[pix];
                        mMapE2NInv[pix] = double(1)/mMapE2NInv[pix];

                        // compute the observed number of pixels in the data
                        mNumObsPix += size_t(1);
                        totObjs += mMapN[pix];

                        //mMapNNInv[pix] = nbar; //TODO note that I may have to recalculate this again
                    }
                    else
                    {
                        // we don't have the variance properly defined
                        mMask[pix] = 0;
                        mMapE1NInv[pix] = 0.;
                        mMapE2NInv[pix] = 0;
                        mMapE1[pix] = 0;
                        mMapE2[pix] = 0;
                        mMapN[pix] = 0;
                        mMapNNInv[pix] = 0;
                    }
                }
                else
                {
                    // make the new mask
                    mMask[pix] = 0;
                    mMapE1NInv[pix] = 0.;
                    mMapE2NInv[pix] = 0;
                    mMapE1[pix] = 0;
                    mMapE2[pix] = 0;
                    mMapNNInv[pix] = 0;
                }
            }

            // output the sky fraction of the augmented mask
            size_t nPix = (size_t) mTestE1.Npix();
            double fKsy = (double)mNumObsPix / (double)nPix;
            BOOST_LOG_TRIVIAL(info) << "Sky fraction " << fKsy;

            BOOST_LOG_TRIVIAL(info) << "Toal objects in the map " << totObjs;

            // output the n_bar or the mean number of objects per pixel
            double nBar = totObjs/(double)mNumObsPix;
            BOOST_LOG_TRIVIAL(info) << "Mean objects per pixel " << nBar;

            assert(nBar > double(0));

            // assign nbar to nInv map for number density
            for(auto pix = 0; pix<mMapN.Npix(); ++pix)
            {
                if(mMapN[pix] > 0)
                {
                    mMapN[pix] = (mMapN[pix] - nBar)/nBar;
                    mMapNNInv[pix] = nBar;
                }
            }

            // if testing print the mismatch stats
            if(mDoTest)
            {

                double fracMissMatchE1 = (double)mMisMatchCountE1 / (double)mNumObsPix;
                double fracMissMatchE2 = (double)mMisMatchCountE2 / (double)mNumObsPix;

                BOOST_LOG_TRIVIAL(warning) << "Pixel value miss-match for e1 = "<<fracMissMatchE1;
                BOOST_LOG_TRIVIAL(warning) << "Pixel value miss-match for e2 = "<<fracMissMatchE2;
            }
        }
        else
        {
            std::string msg = std::string("Input catalogue file ")
                + std::string(mPropTree.get<std::string>("input.catlogue_file_name"))
                + std::string(" failed to open.");
            BOOST_LOG_TRIVIAL(error) << msg;
            throw std::runtime_error(msg);
        }

    }

    /**
     * \brief write the maps to fits files
     */
    void writeMaps()
    {
        BOOST_LOG_TRIVIAL(info) << "Output data map file name :  "
            << mPropTree.get<std::string>("output.data_map_file_name");

        write_Healpix_map_to_fits(std::string("!")+mPropTree.get<std::string>("output.data_map_file_name"),
            mMapN,mMapE1,mMapE2,planckType<double>());

        BOOST_LOG_TRIVIAL(info) << "Output nInv map file name :  "
            << mPropTree.get<std::string>("output.nInv_map_file_name");

        write_Healpix_map_to_fits(std::string("!")+mPropTree.get<std::string>("output.nInv_map_file_name"),
            mMapNNInv,mMapE1NInv,mMapE2NInv,planckType<double>());

        BOOST_LOG_TRIVIAL(info) << "Output augmented mask file name :  "
            << mPropTree.get<std::string>("output.augmented_mask_file_name");

        write_Healpix_map_to_fits(std::string("!")+mPropTree.get<std::string>("output.augmented_mask_file_name"),
            mMask,planckType<double>());
    }

private:
    propertyTreeType mPropTree; /**< property tree that stores the ini file information */
    mapType mMapN; /**< number density map */
    mapType mMapNNInv; /**< number density map */
    mapType mMapE1; /**<  ellipticity-1 map */
    mapType mMapE2; /**< ellipticity-2 map */
    mapType mMapE1NInv; /**<  ellipticity-1 nInv map */
    mapType mMapE2NInv; /**< ellipticity-2 nInv map */
    mapType mMask; /**< mask */
    std::vector<double> mZBounds; /**< z bounds */

    mapType mTestE1; /**<  ellipticity-1 test map*/
    mapType mTestE2; /**< ellipticity-2 test map */
    bool mDoTest; /**< a flag to to sanity test */
    size_t mMisMatchCountE1; /**< sanity check mismatch for ellipticity-1*/
    size_t mMisMatchCountE2; /**< sanity check mismatch for ellipticity-2*/
    size_t mNumObsPix; /**< number of observed pixels*/

};

#endif //CAT2MAP_HPP


// Boost.Geometry

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2014, 2015, 2016.
// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Use, modification and distribution is subject to 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_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP
#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP

#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/radius.hpp>

#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/promote_floating_point.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>

#include <boost/geometry/strategies/side.hpp>
//#include <boost/geometry/strategies/concepts/side_concept.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace side
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

/*!
\brief Check at which side of a segment a point lies
         left of segment (> 0), right of segment (< 0), on segment (0)
\ingroup strategies
\tparam InverseFormula Geodesic inverse solution formula.
\tparam Model Reference model of coordinate system.
\tparam CalculationType \tparam_calculation
 */
template <template<typename, bool, bool, bool, bool, bool> class InverseFormula,
          typename Model,
          typename CalculationType = void>
class by_azimuth
{
public:
    by_azimuth(Model const& model = Model())
        : m_model(model)
    {}

    template <typename P1, typename P2, typename P>
    inline int apply(P1 const& p1, P2 const& p2, P const& p)
    {
        typedef typename promote_floating_point
            <
                typename select_calculation_type_alt
                    <
                        CalculationType,
                        P1, P2, P
                    >::type
            >::type calc_t;

        typedef InverseFormula<calc_t, false, true, false, false, false> inverse_formula;

        calc_t a1p = azimuth<calc_t, inverse_formula>(p1, p, m_model);
        calc_t a12 = azimuth<calc_t, inverse_formula>(p1, p2, m_model);

        calc_t const pi = math::pi<calc_t>();

        // instead of the formula from XTD
        //calc_t a_diff = asin(sin(a1p - a12));

        calc_t a_diff = a1p - a12;
        // normalize, angle in [-pi, pi]
        while ( a_diff > pi )
            a_diff -= calc_t(2) * pi;
        while ( a_diff < -pi )
            a_diff += calc_t(2) * pi;

        // NOTE: in general it shouldn't be required to support the pi/-pi case
        // because in non-cartesian systems it makes sense to check the side
        // only "between" the endpoints.
        // However currently the winding strategy calls the side strategy
        // for vertical segments to check if the point is "between the endpoints.
        // This could be avoided since the side strategy is not required for that
        // because meridian is the shortest path. So a difference of
        // longitudes would be sufficient (of course normalized to [-pi, pi]).

        // NOTE: with the above said, the pi/-pi check is temporary
        // however in case if this was required
        // the geodesics on ellipsoid aren't "symmetrical"
        // therefore instead of comparing a_diff to pi and -pi
        // one should probably use inverse azimuths and compare
        // the difference to 0 as well

        // positive azimuth is on the right side
        return math::equals(a_diff, 0)
            || math::equals(a_diff, pi)
            || math::equals(a_diff, -pi) ? 0
             : a_diff > 0 ? -1 // right
             : 1; // left
    }

private:
    template <typename ResultType,
              typename InverseFormulaType,
              typename Point1,
              typename Point2,
              typename ModelT>
    static inline ResultType azimuth(Point1 const& point1, Point2 const& point2, ModelT const& model)
    {
        return InverseFormulaType::apply(get_as_radian<0>(point1),
                                         get_as_radian<1>(point1),
                                         get_as_radian<0>(point2),
                                         get_as_radian<1>(point2),
                                         model).azimuth;
    }

    Model m_model;
};

} // detail
#endif // DOXYGEN_NO_DETAIL

}} // namespace strategy::side

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP


// __BEGIN_LICENSE__
//  Copyright (c) 2006-2012, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NASA Vision Workbench is licensed under the Apache License,
//  Version 2.0 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__

#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4996)
#endif

#include <cstdlib>
#include <iostream>
#include <cmath>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/foreach.hpp>
namespace fs = boost::filesystem;
namespace po = boost::program_options;

#include <vw/Core/Functors.h>
#include <vw/Image/Algorithms.h>
#include <vw/Image/ImageMath.h>
#include <vw/Image/ImageViewRef.h>
#include <vw/Image/PerPixelViews.h>
#include <vw/Image/PixelMask.h>
#include <vw/Image/MaskViews.h>
#include <vw/Image/PixelTypes.h>
#include <vw/Image/Statistics.h>
#include <vw/FileIO/DiskImageView.h>
#include <vw/Cartography/GeoReference.h>
#include <vw/tools/Common.h>
#include <vw/FileIO/DiskImageResourceGDAL.h>
#include <vw/Image/Interpolation.h>
#include <asp/Core/Macros.h>
#include <asp/Core/Common.h>

using namespace vw;
using namespace vw::cartography;

struct Options : asp::BaseOptions {};

// Convolve a DEM with exp(-sigma*x^2). The input DEM must
// have its invalid pixels masked.

template <class ImageT>
class BlurDEM:
  public ImageViewBase< BlurDEM<ImageT> > {
  ImageT m_img;
  int m_search_dist; // half of size of window to convolve with
  ImageView<double> m_gauss_kernel;
  typedef typename ImageT::pixel_type PixelT;

public:

  typedef PixelT pixel_type;
  typedef PixelT result_type;
  typedef ProceduralPixelAccessor<BlurDEM> pixel_accessor;

  BlurDEM( ImageViewBase<ImageT> const& img,
                     double blur_sigma) :
    m_img(img.impl()) {
    VW_ASSERT(blur_sigma > 0,
              ArgumentErr() << "Expecting positive sigma.");

    // Cut the gaussian exp(-sigma*x^2) where its value is 'scale'.
    double scale = 0.001;
    m_search_dist = (int)ceil(sqrt(-log(scale)/blur_sigma));
    std::cout << "Search distance is " << m_search_dist << std::endl;

    // The gaussian kernel
    int h = m_search_dist;
    m_gauss_kernel.set_size(2*h+1, 2*h+1);
    for (int c = 0; c < m_gauss_kernel.cols(); c++){
      for (int r = 0; r < m_gauss_kernel.rows(); r++){
        double r2 = double(c-h)*(c-h) + double(r-h)*(r-h);
        m_gauss_kernel(c, r) = exp(-blur_sigma*r2);
      }
    }

  }

  inline int32 cols() const { return m_img.cols(); }
  inline int32 rows() const { return m_img.rows(); }
  inline int32 planes() const { return 1; }

  inline pixel_accessor origin() const { return pixel_accessor(*this); }

  inline result_type operator()( size_t i, size_t j, size_t p=0 ) const {
    vw_throw( NoImplErr() << "BlurDEM: operator() not implemented.\n" );
  }

  typedef CropView< ImageView<PixelT> > prerasterize_type;
  inline prerasterize_type prerasterize( BBox2i const& bbox ) const {

    // Crop into an expanded box as to have enough pixels to do
    // the blurring at every pixel in the current box.
    int h = m_search_dist; // shorten
    BBox2i biased_box = bbox;
    biased_box.expand(h+1);
    biased_box.crop(bounding_box(m_img));
    ImageView<PixelT> img( crop( m_img, biased_box ) );
    ImageView<PixelT> filled_img = copy(img);

    int nc = img.cols(), nr = img.rows(); // shorten
    for (int row = 0; row < nr; row++){
      for (int col = 0; col < nc; col++){
        PixelT V; V.validate();
        double sum = 0.0;
        for (int c = std::max(col-h, 0); c <= std::min(col+h, nc-1); c++){
          for (int r = std::max(row-h, 0); r <= std::min(row+h, nr-1); r++){
            if (!is_valid(img(c, r))) continue;
            double wt = m_gauss_kernel(c-col+h, r-row+h);
              V   += wt*img(c, r);
              sum += wt;
          }
        }
        if (sum > 0) filled_img(col, row) = V/sum;

      }
    }

    return prerasterize_type(filled_img,
                             -biased_box.min().x(), -biased_box.min().y(),
                             cols(), rows());

  }
  template <class ImgT>
  inline void rasterize( ImgT const& img, BBox2i const& bbox ) const {
    vw::rasterize( prerasterize(bbox), img, bbox );
  }

};

template <class ImgT>
BlurDEM<ImgT>
blur_dem( ImageViewBase<ImgT> const& img, double blur_sigma) {
  typedef BlurDEM<ImgT> result_type;
  return result_type( img.impl(), blur_sigma);
}

int main( int argc, char *argv[] ){

  Options opt;

  if (argc < 4){
    std::cerr << "Usage: " << argv[0] << " input-DEM.tif blur_sigma output-DEM.tif"
              << std::endl;
    exit(1);
  }

  std::string infile = argv[1];
  double blur_sigma = atof(argv[2]);
  std::string outfile = argv[3];

  std::cout << "Reading: " << infile << std::endl;
  std::cout << "blur sigma is " << blur_sigma << std::endl;

  DiskImageResourceGDAL in_rsrc(infile);
  float nodata_val = -32768;
  if ( in_rsrc.has_nodata_read() ) {
    nodata_val = in_rsrc.nodata_read();
    vw_out() << "\tFound input nodata value: " << nodata_val << std::endl;
  }else{
    std::cerr << "Nodata value not found in: " << infile << std::endl;
    exit(1);
  }

  DiskImageView<float> dem(in_rsrc);

  GeoReference georef;
  read_georeference(georef, in_rsrc);

  std::cout << "Writing: " << outfile << std::endl;
  block_write_gdal_image(outfile,
                         apply_mask
                         (blur_dem
                          (create_mask(dem, nodata_val), blur_sigma),
                          nodata_val),
                         georef, nodata_val, opt,
                         TerminalProgressCallback("asp","")
                         );
  return 0;

}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file orea/aggregation/postprocess.hpp
    \brief Exposure aggregation and XVA calculation
    \ingroup analytics
*/

#pragma once

#include <orea/aggregation/collatexposurehelper.hpp>
#include <orea/cube/inmemorycube.hpp>
#include <orea/scenario/aggregationscenariodata.hpp>

#include <ored/portfolio/nettingsetmanager.hpp>
#include <ored/portfolio/portfolio.hpp>
#include <ored/report/report.hpp>

#include <ql/time/date.hpp>

#include <boost/shared_ptr.hpp>

namespace ore {
namespace analytics {
using namespace QuantLib;
using namespace data;

enum class AllocationMethod {
    None,
    Marginal, // Pykhtin & Rosen, 2010
    RelativeFairValueGross,
    RelativeFairValueNet,
    RelativeXVA
};

std::ostream& operator<<(std::ostream& out, AllocationMethod m);

AllocationMethod parseAllocationMethod(const string& s);

//! Exposure Aggregation and XVA Calculation
/*!
  This class aggregates NPV cube data, computes exposure statistics
  and various XVAs, all at trade and netting set level:

  1) Exposures
  - Expected Positive Exposure, EPE: E[max(NPV(t),0) / N(t)]
  - Expected Negative Exposure, ENE: E[max(-NPV(t),0) / N(t)]
  - Basel Expected Exposure, EE_B: EPE(t)/P(t)
  - Basel Expected Positive Exposure, EPE_B
  - Basel Effective Expected Exposure, EEE_B: max( EEE_B(t-1), EE_B(t))
  - Basel Effective Expected Positive Exposure, EEPE_B
  - Potential Future Exposure, PFE: q-Quantile of the distribution of

  2) Dynamic Initial Margin via regression

  3) XVAs:
  - Credit Value Adjustment, CVA
  - Debit Value Adjustment, DVA
  - Funding Value Adjustment, FVA
  - Collateral Value Adjustment, COLVA
  - Margin Value Adjustment, MVA

  4) Allocation from netting set to trade level such that allocated contributions
  add up to the netting set
  - CVA and DVA
  - EPE and ENE

  All analytics are precomputed when the class constructor is called.
  A number of inspectors described below then return the individual analytics results.

  Note:
  - exposures are discounted at the numeraire N(t) used in the
  Monte Carlo simulation which produces the NPV cube.
  - NPVs take collateral into account, depending on CSA settings

  \ingroup analytics

  \todo Introduce enumeration for TradeAction type and owner
  \todo Interpolation for DIM(t-MPOR) when the simulation grid spacing is different from MPOR
  \todo Revise alternatives to the RelativeXVA exposure and XVA allocation method
  \todo Add trade-level MVA
  \todo Take the spread received on posted initial margin into account in MVA calculation
*/
class PostProcess {
public:
    //! Constructor
    PostProcess( //! Trade portfolio to identidy e.g. netting set, maturity, break dates for each trade
        const boost::shared_ptr<Portfolio>& portfolio,
        //! Netting set manager to access CSA details for each netting set
        const boost::shared_ptr<NettingSetManager>& nettingSetManager,
        //! Market data object to access e.g. discounting and funcing curves
        const boost::shared_ptr<Market>& market,
        //! Market configuration to use
        const std::string& configuration,
        //! Input NPV Cube
        const boost::shared_ptr<NPVCube>& cube,
        //! Subset of simulated market data, index fixings and FX spot rates, associated with the NPV cube
        const boost::shared_ptr<AggregationScenarioData>& scenarioData,
        //! Selection of analytics to be produced
        const map<string, bool>& analytics,
        //! Expression currency for all results
        const string& baseCurrency,
        //! Method to be used for Exposure/XVA allocation down to trade level
        const string& allocationMethod,
        //! Cutoff parameter for the marginal allocation method below which we switch to equal disctribution
        Real cvaMarginalAllocationLimit,
        //! Quantile for Potential Future Exposure output
        Real quantile = 0.95,
        //! Collateral calculation type to be used, see class %CollateralExposureHelper
        const string& calculationType = "Symmetric",
        //! Credit curve name to be used for "our" credit risk in DVA calculations
        const string& dvaName = "",
        //! Borrowing curve name to be used in FVA calculations
        const string& fvaBorrowingCurve = "",
        //! Lending curve name to be used in FVA calculations
        const string& fvaLendingCurve = "",
        //! Quantile used in dynamic initial margin calculation
        Real dimQuantile = 0.99,
        //! Initial margin horizon in calendar days, 2 weeks = 14 days
        Size dimHorizonCalendarDays = 14,
        //! Order of the regression polynomial used in DIM estmation
        Size dimRegressionOrder = 0,
        //! Regressors to be used in the DIM estimation by regression, each must match an additional scenario data key
        vector<string> dimRegressors = vector<string>(),
        //! Number of local regression evaluations, e.g. to validata DIM by regression
        Size dimLocalRegressionEvaluations = 0,
        //! Local regression band width in standard deviations of the regression variable
        Real dimLocalRegressionBandwidth = 0,
        //! Scaling factor applied to all DIM values
        Real dimScaling = 1.0,
        //! Assume t=0 collateral balance equals NPV (set to 0 if false)
        bool fullInitialCollateralisation = false,
        //! own capital discounting rate for discounting expected capital for KVA
        Real kvaCapitalDiscountRate = 0.10,
        //! alpha to adjust EEPE to give EAD for risk capital
        Real kvaAlpha = 1.4,
        //! regulatory adjustment, 1/min cap requirement
        Real kvaRegAdjustment = 12.5,
        //! Cost of Capital for KVA = regulatory adjustment x capital hurdle
        Real kvaCapitalHurdle = 0.012,
        //! Our KVA PD floor
        Real kvaOurPdFloor = 0.03,
        //! Their KVA PD floor
        Real kvaTheirPdFloor = 0.03,
        //! Our KVA CVA Risk Weight
        Real kvaOurCvaRiskWeight = 0.05,
        //! Their KVA CVA Risk Weight,
        Real kvaTheirCvaRiskWeight = 0.05);

    //! Return list of Trade IDs in the portfolio
    const vector<string>& tradeIds() { return tradeIds_; }
    //! Return list of netting set IDs in the portfolio
    const vector<string>& nettingSetIds() { return nettingSetIds_; }

    //! Return trade level Expected Positive Exposure evolution
    const vector<Real>& tradeEPE(const string& tradeId);
    //! Return trade level Expected Negative Exposure evolution
    const vector<Real>& tradeENE(const string& tradeId);
    //! Return trade level Basel Expected Exposure evolution
    const vector<Real>& tradeEE_B(const string& tradeId);
    //! Return trade level Basel Expected Positive Exposure evolution
    const Real& tradeEPE_B(const string& tradeId);
    //! Return trade level Effective Expected Exposure evolution
    const vector<Real>& tradeEEE_B(const string& tradeId);
    //! Return trade level Effective Expected Positive Exposure evolution
    const Real& tradeEEPE_B(const string& tradeId);
    //! Return trade level Potential Future Exposure evolution
    const vector<Real>& tradePFE(const string& tradeId);
    // const vector<Real>& tradeVAR(const string& tradeId);

    //! Return Netting Set Expected Positive Exposure evolution
    const vector<Real>& netEPE(const string& nettingSetId);
    //! Return Netting Set Expected Negative Exposure evolution
    const vector<Real>& netENE(const string& nettingSetId);
    //! Return Netting Set Basel Expected Exposure evolution
    const vector<Real>& netEE_B(const string& nettingSetId);
    //! Return Netting Set Basel Expected Positive Exposure evolution
    const Real& netEPE_B(const string& nettingSetId);
    //! Return Netting Set Effective Expected Exposure evolution
    const vector<Real>& netEEE_B(const string& nettingSetId);
    //! Return Netting Set Effective Expected Positive Exposure evolution
    const Real& netEEPE_B(const string& nettingSetId);
    //! Return Netting Set Potential Future Exposure evolution
    const vector<Real>& netPFE(const string& nettingSetId);
    // const vector<Real>& netVAR(const string& nettingSetId);

    //! Return the netting set's expected collateral evolution
    const vector<Real>& expectedCollateral(const string& nettingSetId);
    //! Return the netting set's expected COLVA increments through time
    const vector<Real>& colvaIncrements(const string& nettingSetId);
    //! Return the netting set's expected Collateral Floor increments through time
    const vector<Real>& collateralFloorIncrements(const string& nettingSetId);

    //! Return the trade EPE, allocated down from the netting set level
    const vector<Real>& allocatedTradeEPE(const string& tradeId);
    //! Return trade ENE, allocated down from the netting set level
    const vector<Real>& allocatedTradeENE(const string& tradeId);

    //! Return trade (stand-alone) CVA
    Real tradeCVA(const string& tradeId);
    //! Return trade (stand-alone) DVA
    Real tradeDVA(const string& tradeId);
    //! Return trade (stand-alone) MVA
    Real tradeMVA(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment)
    Real tradeFBA(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment)
    Real tradeFCA(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment) excluding own survival probability
    Real tradeFBA_exOwnSP(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment) excluding own survival probability
    Real tradeFCA_exOwnSP(const string& tradeId);
    //! Return trade (stand-alone) FBA (Funding Benefit Adjustment) excluding both survival probabilities
    Real tradeFBA_exAllSP(const string& tradeId);
    //! Return trade (stand-alone) FCA (Funding Cost Adjustment) excluding both survival probabilities
    Real tradeFCA_exAllSP(const string& tradeId);
    //! Return allocated trade CVA (trade CVAs add up to netting set CVA)
    Real allocatedTradeCVA(const string& tradeId);
    //! Return allocated trade DVA (trade DVAs add up to netting set DVA)
    Real allocatedTradeDVA(const string& tradeId);
    //! Return netting set CVA
    Real nettingSetCVA(const string& nettingSetId);
    //! Return netting set DVA
    Real nettingSetDVA(const string& nettingSetId);
    //! Return netting set MVA
    Real nettingSetMVA(const string& nettingSetId);
    //! Return netting set FBA
    Real nettingSetFBA(const string& nettingSetId);
    //! Return netting set FCA
    Real nettingSetFCA(const string& nettingSetId);
    //! Return netting set KVA-CCR
    Real nettingSetOurKVACCR(const string& nettingSetId);
    //! Return netting set KVA-CCR from counterparty persepctive
    Real nettingSetTheirKVACCR(const string& nettingSetId);
    //! Return netting set KVA-CVA
    Real nettingSetOurKVACVA(const string& nettingSetId);
    //! Return netting set KVA-CVA from counterparty persepctive
    Real nettingSetTheirKVACVA(const string& nettingSetId);
    //! Return netting set FBA excluding own survival probability
    Real nettingSetFBA_exOwnSP(const string& nettingSetId);
    //! Return netting set FCA excluding own survival probability
    Real nettingSetFCA_exOwnSP(const string& nettingSetId);
    //! Return netting set FBA excluding both survival probabilities
    Real nettingSetFBA_exAllSP(const string& nettingSetId);
    //! Return netting set FCA excluding both survival probabilities
    Real nettingSetFCA_exAllSP(const string& nettingSetId);
    //! Return netting set COLVA
    Real nettingSetCOLVA(const string& nettingSetId);
    //! Return netting set Collateral Floor value
    Real nettingSetCollateralFloor(const string& nettingSetId);

    //! Inspector for the input NPV cube (by trade, time, scenario)
    const boost::shared_ptr<NPVCube>& cube() { return cube_; }
    //! Return the  for the input NPV cube after netting and collateral (by netting set, time, scenario)
    const boost::shared_ptr<NPVCube>& netCube() { return nettedCube_; }
    //! Return the dynamic initial margin cube (regression approach)
    const boost::shared_ptr<NPVCube>& dimCube() { return dimCube_; }
    //! Write average (over samples) DIM evolution through time for all netting sets
    void exportDimEvolution(ore::data::Report& dimEvolutionReport);
    //! Write DIM as a function of sample netting set NPV for a given time step
    void exportDimRegression(const std::string& nettingSet, const std::vector<Size>& timeSteps,
                             const std::vector<boost::shared_ptr<ore::data::Report>>& dimRegReports);

private:
    //! Helper function to return the collateral account evolution for a given netting set
    boost::shared_ptr<vector<boost::shared_ptr<CollateralAccount>>>
    collateralPaths(const string& nettingSetId, const boost::shared_ptr<NettingSetManager>& nettingSetManager,
                    const boost::shared_ptr<Market>& market, const std::string& configuration,
                    const boost::shared_ptr<AggregationScenarioData>& scenarioData, Size dates, Size samples,
                    const vector<vector<Real>>& nettingSetValue, Real nettingSetValueToday,
                    const Date& nettingSetMaturity);

    void updateNettingSetKVA();
    void updateStandAloneXVA();
    void updateAllocatedXVA();

    //! Fill dynamic initial margin cube (per netting set, date and sample)
    void dynamicInitialMargin();
    //! Compile the array of DIM regressors for the specified netting set, date and sample index
    Disposable<Array> regressorArray(string nettingSet, Size dateIndex, Size sampleIndex);
    //! Perform the calculation of IM as of t=t0
    void performT0DimCalc();

    boost::shared_ptr<Portfolio> portfolio_;
    boost::shared_ptr<NettingSetManager> nettingSetManager_;
    boost::shared_ptr<Market> market_;
    const std::string configuration_;
    boost::shared_ptr<NPVCube> cube_;
    boost::shared_ptr<AggregationScenarioData> scenarioData_;
    map<string, bool> analytics_;

    map<string, vector<vector<Real>>> nettingSetNPV_, nettingSetFLOW_, nettingSetDIM_, nettingSetLocalDIM_,
        nettingSetDeltaNPV_;
    map<string, vector<vector<Array>>> regressorArray_;
    map<string, vector<Real>> nettingSetExpectedDIM_, nettingSetZeroOrderDIM_, nettingSetSimpleDIMh_,
        nettingSetSimpleDIMp_;
    map<string, vector<Real>> tradeEPE_, tradeENE_, tradeEE_B_, tradeEEE_B_, tradePFE_, tradeVAR_;
    map<string, Real> tradeEPE_B_, tradeEEPE_B_;
    map<string, vector<Real>> allocatedTradeEPE_, allocatedTradeENE_;
    map<string, vector<Real>> netEPE_, netENE_, netEE_B_, netEEE_B_, netPFE_, netVAR_, expectedCollateral_;
    map<string, Real> netEPE_B_, netEEPE_B_;
    map<string, vector<Real>> colvaInc_, eoniaFloorInc_;
    map<string, Real> tradeCVA_, tradeDVA_, tradeMVA_, tradeFBA_, tradeFCA_, tradeFBA_exOwnSP_, tradeFCA_exOwnSP_,
        tradeFBA_exAllSP_, tradeFCA_exAllSP_;
    map<string, Real> sumTradeCVA_, sumTradeDVA_; // per netting set
    map<string, Real> allocatedTradeCVA_, allocatedTradeDVA_;
    map<string, Real> nettingSetCVA_, nettingSetDVA_, nettingSetMVA_;
    map<string, Real> nettingSetCOLVA_, nettingSetCollateralFloor_;
    map<string, Real> ourNettingSetKVACCR_, theirNettingSetKVACCR_, ourNettingSetKVACVA_, theirNettingSetKVACVA_;
    map<string, Real> nettingSetFCA_, nettingSetFBA_, nettingSetFCA_exOwnSP_, nettingSetFBA_exOwnSP_,
        nettingSetFCA_exAllSP_, nettingSetFBA_exAllSP_;
    boost::shared_ptr<NPVCube> nettedCube_;
    boost::shared_ptr<NPVCube> dimCube_;
    map<string, Real> net_t0_im_reg_h_, net_t0_im_simple_h_;

    vector<string> tradeIds_;
    vector<string> nettingSetIds_;
    map<string, string> counterpartyId_; // for each nettingSetId
    string baseCurrency_;
    Real quantile_;
    CollateralExposureHelper::CalculationType calcType_;
    string dvaName_;
    string fvaBorrowingCurve_;
    string fvaLendingCurve_;
    Real dimQuantile_;
    Size dimHorizonCalendarDays_;
    Size dimRegressionOrder_;
    vector<string> dimRegressors_;
    Size dimLocalRegressionEvaluations_;
    Real dimLocalRegressionBandwidth_;
    Real dimScaling_;
    bool fullInitialCollateralisation_;
    Real kvaCapitalDiscountRate_;
    Real kvaAlpha_;
    Real kvaRegAdjustment_;
    Real kvaCapitalHurdle_;
    Real kvaOurPdFloor_;
    Real kvaTheirPdFloor_;
    Real kvaOurCvaRiskWeight_;
    Real kvaTheirCvaRiskWeight_;
};
} // namespace analytics
} // namespace ore


/* -*- c++ -*- */
/*
 * Copyright 2015 Free Software Foundation, Inc.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 3, or (at your
 * option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <ldpc_bit_flip_decoder_impl.h>
#include <math.h>
#include <boost/assign/list_of.hpp>
#include <volk/volk.h>
#include <sstream>
#include <stdio.h>
#include <vector>

namespace gr {
  namespace fec {
    namespace code {

      generic_decoder::sptr
      ldpc_bit_flip_decoder::make(const fec_mtrx_sptr mtrx_obj,
                                  unsigned int max_iter)
      {
        return generic_decoder::sptr
          (new ldpc_bit_flip_decoder_impl(mtrx_obj, max_iter));
      }

      ldpc_bit_flip_decoder_impl::ldpc_bit_flip_decoder_impl(const fec_mtrx_sptr mtrx_obj,
                                                             unsigned int max_iter)
        : generic_decoder("ldpc_bit_flip_decoder")
      {
        // FEC matrix object to use for decoding
        d_mtrx = mtrx_obj;

        d_rate = static_cast<double>(d_mtrx->k())/static_cast<double>(d_mtrx->n());

        // Set frame size to k, the # of bits in the information word
        // All buffers and settings will be based on this value.
        set_frame_size(d_mtrx->k());
        // Maximum number of iterations in the decoding algorithm
        d_max_iterations = max_iter;
      }

      ldpc_bit_flip_decoder_impl::~ldpc_bit_flip_decoder_impl()
      {
      }

      int
      ldpc_bit_flip_decoder_impl::get_output_size()
      {
        return d_output_size;
      }

      int
      ldpc_bit_flip_decoder_impl::get_input_size()
      {
        return d_input_size;
      }

      bool
      ldpc_bit_flip_decoder_impl::set_frame_size(unsigned int frame_size)
      {
        if(frame_size % d_mtrx->k() != 0) {
          GR_LOG_ERROR(d_logger, boost::format("Frame size (%1% bits) must be a "
                                               "multiple of the information word "
                                               "size of the LDPC matrix, %2%") \
                       % frame_size % (d_mtrx->k()));
          throw std::runtime_error("ldpc_bit_flip_decoder: cannot use frame size.");
        }

        d_output_size = frame_size;
        d_input_size = static_cast<int>(round(frame_size / d_rate));

        return true;
      }

      double
      ldpc_bit_flip_decoder_impl::rate()
      {
        return d_rate;
      }

      void
      ldpc_bit_flip_decoder_impl::generic_work(void *inbuffer,
                                               void *outbuffer)
      {
        // Populate the information word
        const float *in = (const float*)inbuffer;
        unsigned char *out = (unsigned char*) outbuffer;

        int j = 0;
        for(int i = 0; i < d_input_size; i+=d_mtrx->n()) {
          d_mtrx->decode(&out[j], &in[i], d_mtrx->k(), d_max_iterations);
          j += d_mtrx->k();
        }

      } /* ldpc_bit_flip_decoder_impl::generic_work() */

    } /* namespace code */
  } /* namespace fec */
} /* namespace gr */