/* matrix/gsl_matrix_ulong.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program 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 of the License, or (at
 * your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_ULONG_H__
#define __GSL_MATRIX_ULONG_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_ulong.h>
#include <gsl/gsl_blas_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned long * data;
  gsl_block_ulong * block;
  int owner;
} gsl_matrix_ulong;

typedef struct
{
  gsl_matrix_ulong matrix;
} _gsl_matrix_ulong_view;

typedef _gsl_matrix_ulong_view gsl_matrix_ulong_view;

typedef struct
{
  gsl_matrix_ulong matrix;
} _gsl_matrix_ulong_const_view;

typedef const _gsl_matrix_ulong_const_view gsl_matrix_ulong_const_view;

/* Allocation */

GSL_FUN gsl_matrix_ulong *
gsl_matrix_ulong_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_ulong *
gsl_matrix_ulong_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_ulong *
gsl_matrix_ulong_alloc_from_block (gsl_block_ulong * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_FUN gsl_matrix_ulong *
gsl_matrix_ulong_alloc_from_matrix (gsl_matrix_ulong * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN gsl_vector_ulong *
gsl_vector_ulong_alloc_row_from_matrix (gsl_matrix_ulong * m,
                                        const size_t i);

GSL_FUN gsl_vector_ulong *
gsl_vector_ulong_alloc_col_from_matrix (gsl_matrix_ulong * m,
                                        const size_t j);

GSL_FUN void gsl_matrix_ulong_free (gsl_matrix_ulong * m);

/* Views */

GSL_FUN _gsl_matrix_ulong_view
gsl_matrix_ulong_submatrix (gsl_matrix_ulong * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_row (gsl_matrix_ulong * m, const size_t i);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_column (gsl_matrix_ulong * m, const size_t j);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_diagonal (gsl_matrix_ulong * m);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_subdiagonal (gsl_matrix_ulong * m, const size_t k);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_superdiagonal (gsl_matrix_ulong * m, const size_t k);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_subrow (gsl_matrix_ulong * m, const size_t i,
                         const size_t offset, const size_t n);

GSL_FUN _gsl_vector_ulong_view
gsl_matrix_ulong_subcolumn (gsl_matrix_ulong * m, const size_t j,
                            const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_ulong_view
gsl_matrix_ulong_view_array (unsigned long * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_ulong_view
gsl_matrix_ulong_view_array_with_tda (unsigned long * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_ulong_view
gsl_matrix_ulong_view_vector (gsl_vector_ulong * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_ulong_view
gsl_matrix_ulong_view_vector_with_tda (gsl_vector_ulong * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_ulong_const_view
gsl_matrix_ulong_const_submatrix (const gsl_matrix_ulong * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_row (const gsl_matrix_ulong * m,
                            const size_t i);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_column (const gsl_matrix_ulong * m,
                               const size_t j);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_diagonal (const gsl_matrix_ulong * m);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_subdiagonal (const gsl_matrix_ulong * m,
                                    const size_t k);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_superdiagonal (const gsl_matrix_ulong * m,
                                      const size_t k);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_subrow (const gsl_matrix_ulong * m, const size_t i,
                               const size_t offset, const size_t n);

GSL_FUN _gsl_vector_ulong_const_view
gsl_matrix_ulong_const_subcolumn (const gsl_matrix_ulong * m, const size_t j,
                                  const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_ulong_const_view
gsl_matrix_ulong_const_view_array (const unsigned long * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_ulong_const_view
gsl_matrix_ulong_const_view_array_with_tda (const unsigned long * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_ulong_const_view
gsl_matrix_ulong_const_view_vector (const gsl_vector_ulong * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_ulong_const_view
gsl_matrix_ulong_const_view_vector_with_tda (const gsl_vector_ulong * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_ulong_set_zero (gsl_matrix_ulong * m);
GSL_FUN void gsl_matrix_ulong_set_identity (gsl_matrix_ulong * m);
GSL_FUN void gsl_matrix_ulong_set_all (gsl_matrix_ulong * m, unsigned long x);

GSL_FUN int gsl_matrix_ulong_fread (FILE * stream, gsl_matrix_ulong * m) ;
GSL_FUN int gsl_matrix_ulong_fwrite (FILE * stream, const gsl_matrix_ulong * m) ;
GSL_FUN int gsl_matrix_ulong_fscanf (FILE * stream, gsl_matrix_ulong * m);
GSL_FUN int gsl_matrix_ulong_fprintf (FILE * stream, const gsl_matrix_ulong * m, const char * format);

GSL_FUN int gsl_matrix_ulong_memcpy(gsl_matrix_ulong * dest, const gsl_matrix_ulong * src);
GSL_FUN int gsl_matrix_ulong_swap(gsl_matrix_ulong * m1, gsl_matrix_ulong * m2);
GSL_FUN int gsl_matrix_ulong_tricpy(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix_ulong * dest, const gsl_matrix_ulong * src);

GSL_FUN int gsl_matrix_ulong_swap_rows(gsl_matrix_ulong * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_ulong_swap_columns(gsl_matrix_ulong * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_ulong_swap_rowcol(gsl_matrix_ulong * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_ulong_transpose (gsl_matrix_ulong * m);
GSL_FUN int gsl_matrix_ulong_transpose_memcpy (gsl_matrix_ulong * dest, const gsl_matrix_ulong * src);
GSL_FUN int gsl_matrix_ulong_transpose_tricpy (CBLAS_UPLO_t Uplo_src, CBLAS_DIAG_t Diag, gsl_matrix_ulong * dest, const gsl_matrix_ulong * src);

GSL_FUN unsigned long gsl_matrix_ulong_max (const gsl_matrix_ulong * m);
GSL_FUN unsigned long gsl_matrix_ulong_min (const gsl_matrix_ulong * m);
GSL_FUN void gsl_matrix_ulong_minmax (const gsl_matrix_ulong * m, unsigned long * min_out, unsigned long * max_out);

GSL_FUN void gsl_matrix_ulong_max_index (const gsl_matrix_ulong * m, size_t * imax, size_t *jmax);
GSL_FUN void gsl_matrix_ulong_min_index (const gsl_matrix_ulong * m, size_t * imin, size_t *jmin);
GSL_FUN void gsl_matrix_ulong_minmax_index (const gsl_matrix_ulong * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_FUN int gsl_matrix_ulong_equal (const gsl_matrix_ulong * a, const gsl_matrix_ulong * b);

GSL_FUN int gsl_matrix_ulong_isnull (const gsl_matrix_ulong * m);
GSL_FUN int gsl_matrix_ulong_ispos (const gsl_matrix_ulong * m);
GSL_FUN int gsl_matrix_ulong_isneg (const gsl_matrix_ulong * m);
GSL_FUN int gsl_matrix_ulong_isnonneg (const gsl_matrix_ulong * m);

GSL_FUN unsigned long gsl_matrix_ulong_norm1 (const gsl_matrix_ulong * m);

GSL_FUN int gsl_matrix_ulong_add (gsl_matrix_ulong * a, const gsl_matrix_ulong * b);
GSL_FUN int gsl_matrix_ulong_sub (gsl_matrix_ulong * a, const gsl_matrix_ulong * b);
GSL_FUN int gsl_matrix_ulong_mul_elements (gsl_matrix_ulong * a, const gsl_matrix_ulong * b);
GSL_FUN int gsl_matrix_ulong_div_elements (gsl_matrix_ulong * a, const gsl_matrix_ulong * b);
GSL_FUN int gsl_matrix_ulong_scale (gsl_matrix_ulong * a, const double x);
GSL_FUN int gsl_matrix_ulong_scale_rows (gsl_matrix_ulong * a, const gsl_vector_ulong * x);
GSL_FUN int gsl_matrix_ulong_scale_columns (gsl_matrix_ulong * a, const gsl_vector_ulong * x);
GSL_FUN int gsl_matrix_ulong_add_constant (gsl_matrix_ulong * a, const double x);
GSL_FUN int gsl_matrix_ulong_add_diagonal (gsl_matrix_ulong * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_ulong_get_row(gsl_vector_ulong * v, const gsl_matrix_ulong * m, const size_t i);
GSL_FUN int gsl_matrix_ulong_get_col(gsl_vector_ulong * v, const gsl_matrix_ulong * m, const size_t j);
GSL_FUN int gsl_matrix_ulong_set_row(gsl_matrix_ulong * m, const size_t i, const gsl_vector_ulong * v);
GSL_FUN int gsl_matrix_ulong_set_col(gsl_matrix_ulong * m, const size_t j, const gsl_vector_ulong * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL unsigned long   gsl_matrix_ulong_get(const gsl_matrix_ulong * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void    gsl_matrix_ulong_set(gsl_matrix_ulong * m, const size_t i, const size_t j, const unsigned long x);
GSL_FUN INLINE_DECL unsigned long * gsl_matrix_ulong_ptr(gsl_matrix_ulong * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const unsigned long * gsl_matrix_ulong_const_ptr(const gsl_matrix_ulong * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
unsigned long
gsl_matrix_ulong_get(const gsl_matrix_ulong * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
        }
    }
#endif
  return m->data[i * m->tda + j] ;
}

INLINE_FUN
void
gsl_matrix_ulong_set(gsl_matrix_ulong * m, const size_t i, const size_t j, const unsigned long x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  m->data[i * m->tda + j] = x ;
}

INLINE_FUN
unsigned long *
gsl_matrix_ulong_ptr(gsl_matrix_ulong * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (unsigned long *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const unsigned long *
gsl_matrix_ulong_const_ptr(const gsl_matrix_ulong * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (const unsigned long *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_ULONG_H__ */


#include <gsl/gsl_linalg.h>

/* user defined functions */


///
/// @file
///
/// @author Mirko Myllykoski (mirkom@cs.umu.se), Umeå University
///
/// @internal LICENSE
///
/// Copyright (c) 2019-2020, Umeå Universitet
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
///    this list of conditions and the following disclaimer.
///
/// 2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation
///    and/or other materials provided with the distribution.
///
/// 3. Neither the name of the copyright holder nor the names of its
///    contributors may be used to endorse or promote products derived from this
///    software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
/// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
/// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
/// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
/// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
/// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///

#include <starneig_config.h>
#include <starneig/configuration.h>
#include "cpu.h"
#include "../common/common.h"
#include "../common/sanity.h"
#include "../common/tiles.h"
#include "../common/trace.h"
#include <starpu.h>
#include <starpu_scheduler.h>
#include <hwloc.h>
#include <cblas.h>
#include <omp.h>

void starneig_hessenberg_cpu_prepare_column(
    void *buffers[], void *cl_args)
{
    // LAPACK subroutine that generates a real elementary reflector H
    extern void dlarfg_(int const *, double *, double *, int const *, double *);

    int i; // the index of the currect column inside the panel
    struct range_packing_info v_pi;
    starpu_codelet_unpack_args(cl_args, &i, &v_pi);

    int k = 0;

    double *Y = NULL; int ldY = 0;
    if (0 < i) {
        Y = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
        ldY = STARPU_MATRIX_GET_LD(buffers[k]);
        k++;
    }

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    int m = STARPU_MATRIX_GET_NX(buffers[k]);
    int nb = STARPU_MATRIX_GET_NY(buffers[k]);
    k++;

    double *T = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldT = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *P = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldP = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    // an intemediate vector interface for the trailing matrix operation
    struct starpu_vector_interface **v_i =
        (struct starpu_vector_interface **)buffers + k;
    k += v_pi.handles;

    // current column
    double *p = P+i*ldP;

    //
    // update the current column
    //

    if (0 < i) {

        // A <- A - Y * V' (update column from the right)
        cblas_dgemv(CblasColMajor, CblasNoTrans, m, i,
            -1.0, Y, ldY, V+i-1, ldV, 1.0, p, 1);

        //
        // update column from the left
        //

        // we use the last column of T as a work space
        double *w = T+(nb-1)*ldT;

        // w <- V1' * b1 (upper part)
        cblas_dcopy(i, p, 1, w, 1);
        cblas_dtrmv(
            CblasColMajor, CblasLower, CblasTrans, CblasUnit, i, V, ldV, w, 1);

        // w <- w + V2' * b2 (lower part)
        cblas_dgemv(CblasColMajor, CblasTrans, m-i, i,
            1.0, V+i, ldV, p+i, 1, 1.0, w, 1);

        // w <- T' * w
        cblas_dtrmv(
            CblasColMajor, CblasUpper, CblasTrans, CblasNonUnit, i,
            T, ldT, w, 1);

        // b2 <- b2 - V2 * w
        cblas_dgemv(CblasColMajor, CblasNoTrans, m-i, i,
            -1.0, V+i, ldV, w, 1, 1.0, p+i, 1);

        // b1 <- b1 - V1 * w
        cblas_dtrmv(
            CblasColMajor, CblasLower, CblasNoTrans, CblasUnit, i,
            V, ldV, w, 1);
        cblas_daxpy(i, -1.0, w, 1, p, 1);
    }

    //
    // compute the current unit vector
    //

    int height = m-i;
    double tau, *v = V+i*ldV+i;
    memcpy(v, p+i, height*sizeof(double));
    dlarfg_(&height, p+i, v+1, (const int[]){1}, &tau);
    v[0] = 1.0;

    //
    // copy the current unit vector to the intemediate vector interface
    //

    starneig_join_range(&v_pi, v_i, v, 1);

    //
    // set elements below the subdiagonal to zero
    //

    for (int j = i+1; j < m; j++)
        p[j] = 0.0;

    //
    // store tau for future use
    //

    T[i*ldT+i] = tau;
}

void starneig_hessenberg_cpu_compute_column(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi;
    struct range_packing_info v_pi, y_pi;
    starpu_codelet_unpack_args(cl_args, &A_pi, &v_pi, &y_pi);

    STARNEIG_EVENT_BEGIN(&A_pi, starneig_event_red);

    int k = 0;

    // involved trailing matrix tiles
    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += A_pi.handles;

    // intemediate vector interface for the trailing matrix operation
    struct starpu_vector_interface **v_i =
        (struct starpu_vector_interface **)buffers + k;
    k += v_pi.handles;

    // intemediate vector interface from the trailing matrix operation
    struct starpu_vector_interface **y_i =
        (struct starpu_vector_interface **)buffers + k;
    k += y_pi.handles;

    int t_rows = (A_pi.rend-1) / A_pi.bm + 1 - (A_pi.rbegin-1) / A_pi.bm;
    int t_cols = (A_pi.cend-1) / A_pi.bn + 1 - (A_pi.cbegin-1) / A_pi.bn;

    //
    // loop oper tile rows
    //

    for (int i = 0; i < t_rows; i++) {

        double *y = (double *) STARPU_VECTOR_GET_PTR(y_i[i]);

        int rbegin = MAX(     0,  A_pi.rbegin - i * A_pi.bm);
        int rend =   MIN(A_pi.bm, A_pi.rend   - i * A_pi.bm);

        //
        // loop over tile columns
        //

        for (int j = 0; j < t_cols; j++) {

            double *A = (double *) STARPU_MATRIX_GET_PTR(A_i[j*t_rows+i]);
            int ldA = STARPU_MATRIX_GET_LD(A_i[j*t_rows+i]);

            double *v = (double *) STARPU_VECTOR_GET_PTR(v_i[j]);

            int cbegin = MAX(      0, A_pi.cbegin - j * A_pi.bn);
            int cend =   MIN(A_pi.bn, A_pi.cend   - j * A_pi.bn);

            cblas_dgemv(
                CblasColMajor, CblasNoTrans, rend-rbegin, cend-cbegin,
                1.0, A+cbegin*ldA+rbegin, ldA, v+cbegin, 1, 1.0, y+rbegin, 1);
        }
    }

    STARNEIG_EVENT_END();
}

void starneig_hessenberg_cpu_finish_column(
    void *buffers[], void *cl_args)
{
    struct range_packing_info y_pi;
    int i;
    starpu_codelet_unpack_args(cl_args, &i, &y_pi);

    int k = 0;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int m = STARPU_MATRIX_GET_NX(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *T = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldT = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *Y = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldY = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    // intemediate vector interface from the trailing matrix operation
    struct starpu_vector_interface **y_i =
        (struct starpu_vector_interface **)buffers + k;
    k += y_pi.handles;

    double tau = T[i*ldT+i];
    double *v = V+i*ldV+i;

    //
    // finish Y update
    //

    starneig_join_range(&y_pi, y_i, Y+i*ldY, 0);

    // w <- V' * v (shared result)
    cblas_dgemv(CblasColMajor, CblasTrans, m-i, i,
        1.0, V+i, ldV, v, 1, 0.0, T+i*ldT, 1);

    // Y(:,i) <- Y(:,i) - Y * w
    cblas_dgemv(CblasColMajor, CblasNoTrans, m, i,
        -1.0, Y, ldY, T+i*ldT, 1, 1.0, Y+i*ldY, 1);

    cblas_dscal(m, tau, Y+i*ldY, 1);

    //
    // update T
    //

    // w <- tau * w
    cblas_dscal(i, -tau, T+i*ldT, 1);

    // T(0:i,i) = T * w
    cblas_dtrmv(
        CblasColMajor, CblasUpper, CblasNoTrans, CblasNonUnit, i,
        T, ldT, T+i*ldT, 1);

    T[i*ldT+i] = tau;
}

void starneig_hessenberg_cpu_update_trail_right(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi;
    int nb, roffset, coffset;
    starpu_codelet_unpack_args(cl_args, &A_pi, &nb, &roffset, &coffset);

    STARNEIG_EVENT_BEGIN(&A_pi, starneig_event_blue);

    int m = A_pi.rend - A_pi.rbegin;
    int n = A_pi.cend - A_pi.cbegin;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[0]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[0]);

    double *Y = (double *) STARPU_MATRIX_GET_PTR(buffers[1]);
    int ldY = STARPU_MATRIX_GET_LD(buffers[1]);

    double *A = (double *) STARPU_MATRIX_GET_PTR(buffers[2]);
    int ldA = STARPU_MATRIX_GET_LD(buffers[2]);

    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + 3;

    // join tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 0);

    // A <- Y V^T
    cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans,
        m, n, nb, -1.0, Y+roffset, ldY, V+coffset+nb-1, ldV, 1.0, A, ldA);

    // split tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 1);

    STARNEIG_EVENT_END();
}

void starneig_hessenberg_cpu_update_left_a(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi, W_pi;
    int nb, offset;
    starpu_codelet_unpack_args(cl_args, &A_pi, &W_pi, &nb, &offset);

    STARNEIG_EVENT_BEGIN(&A_pi, starneig_event_green);

    int m = A_pi.rend - A_pi.rbegin;
    int n = A_pi.cend - A_pi.cbegin;

    int k = 0;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *T = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldT = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *A = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldA = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *W = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldW = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *P = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldP = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += A_pi.handles;

    struct starpu_matrix_interface **W_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += W_pi.handles;

    // join A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 0);

    // join W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 0);

    // P <- A^T * V
    cblas_dgemm(
        CblasColMajor, CblasTrans, CblasNoTrans, n, nb, m,
        1.0, A, ldA, V+offset, ldV, 0.0, P, ldP);

    // P <- P * T
    cblas_dtrmm(
        CblasColMajor, CblasRight, CblasUpper, CblasNoTrans,
        CblasNonUnit, n, nb, 1.0, T, ldT, P, ldP);

    // W <- W + P
    for (int j = 0; j < nb; j++)
        cblas_daxpy(n, 1.0, P+j*ldP, 1, W+j*ldW, 1);

    // split W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 1);

    STARNEIG_EVENT_END();
}

void starneig_hessenberg_cpu_update_left_b(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi, W_pi;
    int nb, offset;
    starpu_codelet_unpack_args(cl_args, &A_pi, &W_pi, &nb, &offset);

    STARNEIG_EVENT_BEGIN(&packing_info, starneig_event_green);

    int m = A_pi.rend - A_pi.rbegin;
    int n = A_pi.cend - A_pi.cbegin;

    int k = 0;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *W = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldW = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *A = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldA = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    struct starpu_matrix_interface **W_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += W_pi.handles;

    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += A_pi.handles;

    // join A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 0);

    // join W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 0);

    //  A <- A - V * W^T
    cblas_dgemm(
        CblasColMajor, CblasNoTrans, CblasTrans, m, n,
        nb, -1.0, V+offset, ldV, W, ldW, 1.0, A, ldA);

    // split A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 1);

    STARNEIG_EVENT_END();
}

void starneig_hessenberg_cpu_update_right_a(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi, W_pi;
    int nb, offset;
    starpu_codelet_unpack_args(cl_args, &A_pi, &W_pi, &nb, &offset);

    STARNEIG_EVENT_BEGIN(&packing_info, starneig_event_blue);

    int m = A_pi.rend - A_pi.rbegin;
    int n = A_pi.cend - A_pi.cbegin;

    int k = 0;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *T = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldT = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *A = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldA = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *W = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldW = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *P = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldP = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += A_pi.handles;

    struct starpu_matrix_interface **W_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += W_pi.handles;

    // join A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 0);

    // join W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 0);

    // P <- A * V
    cblas_dgemm(
        CblasColMajor, CblasNoTrans, CblasNoTrans, m, nb, n,
        1.0, A, ldA, V+offset, ldV, 0.0, P, ldP);

    // P <- P * T
    cblas_dtrmm(
        CblasColMajor, CblasRight, CblasUpper, CblasNoTrans,
        CblasNonUnit, m, nb, 1.0, T, ldT, P, ldP);

    // W <- W + P
    for (int j = 0; j < nb; j++)
        cblas_daxpy(m, 1.0, P+j*ldP, 1, W+j*ldW, 1);

    // split W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 1);

    STARNEIG_EVENT_END();
}

void starneig_hessenberg_cpu_update_right_b(
    void *buffers[], void *cl_args)
{
    struct packing_info A_pi, W_pi;
    int nb, offset;
    starpu_codelet_unpack_args(cl_args, &A_pi, &W_pi, &nb, &offset);

    STARNEIG_EVENT_BEGIN(&packing_info, starneig_event_blue);

    int m = A_pi.rend - A_pi.rbegin;
    int n = A_pi.cend - A_pi.cbegin;

    int k = 0;

    double *V = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldV = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *W = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldW = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    double *A = (double *) STARPU_MATRIX_GET_PTR(buffers[k]);
    int ldA = STARPU_MATRIX_GET_LD(buffers[k]);
    k++;

    struct starpu_matrix_interface **W_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += W_pi.handles;

    struct starpu_matrix_interface **A_i =
        (struct starpu_matrix_interface **)buffers + k;
    k += A_pi.handles;

    // join A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 0);

    // join W tiles
    starneig_join_window(&W_pi, ldW, W_i, W, 0);

    //  A <- A - W * V
    cblas_dgemm(
        CblasColMajor, CblasNoTrans, CblasTrans, m, n,
        nb, -1.0, W, ldW, V+offset, ldV, 1.0, A, ldA);

    // split A tiles
    starneig_join_window(&A_pi, ldA, A_i, A, 1);

    STARNEIG_EVENT_END();
}


#ifndef DATA_H
#define DATA_H

#include <gsl/gsl_vector.h>

typedef struct {
  int imageIdx;
  int labelerId;
  int workflowId;
  int workerPoolId;
  int label;
  double trueLabel;
  double trueDifficulty;
} Label;

typedef struct {
  Label *labels;
  int numLabels;
  int numLabelers;
  int numImages;
  int numWorkflows;
  int numWorkerPools;
  double *priorAlpha;
  double *priorBeta;
  double *probZ1, *probZ0;
  double *alpha, *beta;
  double *priorZ1;
} Dataset;

void packX (gsl_vector *x, Dataset *data);
void unpackX (const gsl_vector *x, Dataset *data);
void readData (char *filename, Dataset *data, double*, double);
void outputResults (Dataset *data);

#endif


#pragma once

#include "detail.h"
#include "schedule.h"
#include <gsl/gsl-lite.hpp>
#include <vector>
#ifndef UNIT_TEST
#include "random_utils.h"
#include "temperature.h"
#include "utils.h"
#else // UNIT_TEST
#include "stub/random_utils.h"
#include "stub/temperature.h"
#include "stub/utils.h"
#endif // UNIT_TEST

namespace angonoka::stun {

struct ScheduleParams;
/**
    Stochastic tunneling algorithm.

    The internal schedule can be updated as many times as
    needed.
*/
class StochasticTunneling {
public:
    /**
        STUN auxiliary data and utilities.

        @var mutator    Instance of Mutator
        @var random     Instance of RandomUtils
        @var makespan   Instance of Makespan
        @var temp       Instance of Temperature
        @var gamma      Tunneling parameter
    */
    struct Options {
        gsl::not_null<const Mutator*> mutator;
        gsl::not_null<RandomUtils*> random;
        gsl::not_null<Makespan*> makespan;
        gsl::not_null<Temperature*> temp;
        float gamma;
    };

    /**
        Default constructor.

        The object will be in an uninitialized state. User must call
        reset to set the initial schedule.

        @param options Instance of StochasticTunneling::Options
    */
    StochasticTunneling(const Options& options);

    /**
        Constructor.

        @param options  Instance of StochasticTunneling::Options
        @param schedule Initial schedule
    */
    StochasticTunneling(const Options& options, Schedule schedule);

    StochasticTunneling(const StochasticTunneling& other);
    StochasticTunneling(StochasticTunneling&& other) noexcept;
    StochasticTunneling& operator=(const StochasticTunneling& other);
    StochasticTunneling&
    operator=(StochasticTunneling&& other) noexcept;
    ~StochasticTunneling() noexcept;

    /**
        Reset stochastic tunneling algorithm to a new
        schedule.

        @param schedule Initial schedule
    */
    void reset(Schedule schedule);

    /**
        Set stochastic tunneling options.

        @param options Instance of Options
    */
    void options(const Options& options);

    /**
        Get stochastic tunneling options.

        @return Stochastic tunneling options.
    */
    [[nodiscard]] Options options() const;

    /**
        Update the internal state according to stochastic
        tunneling algorithm.
    */
    void update() noexcept;

    /**
        The best schedule so far.

        @return A schedule.
    */
    [[nodiscard]] Schedule schedule() const;

    /**
        The best makespan so far.

        @return Makespan.
    */
    [[nodiscard]] float normalized_makespan() const;

private:
    struct Impl;

    using index = MutSchedule::index_type;

    gsl::not_null<const Mutator*> mutator;
    gsl::not_null<RandomUtils*> random;
    gsl::not_null<Makespan*> makespan;
    gsl::not_null<Temperature*> temp;

    std::vector<ScheduleItem> schedule_buffer;
    MutSchedule best_schedule;
    MutSchedule current_schedule;
    MutSchedule target_schedule;

    float current_e;
    float lowest_e;
    float target_e;

    float gamma;
    float current_s;
    float target_s;
};

} // namespace angonoka::stun


#ifndef _AVA_COMMON_SUPPORT_GEN_STAT_H_
#define _AVA_COMMON_SUPPORT_GEN_STAT_H_

#include <fmt/format.h>
#include <stdint.h>

#include <gsl/gsl>

#include "common/extensions/cmd_batching.h"
#include "common/support/io.h"
#include "guestlib/guest_thread.h"
#include "time_util.h"

namespace ava {
namespace support {

inline void stats_end(const char *func_name, int64_t begin_ts) {
  auto end_ts = ava::GetMonotonicNanoTimestamp();
  fmt::memory_buffer output;
  fmt::format_to(output, "GuestlibStat {}, {}\n", func_name, gsl::narrow_cast<int32_t>(end_ts - begin_ts));
  ava::guest_write_stats(output.data(), output.size());
}

}  // namespace support
}  // namespace ava

#endif  // _AVA_COMMON_SUPPORT_GEN_STAT_H_


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include <gsl/gsl>

#include "core/framework/tensor.h"
#include "core/util/math_cpuonly.h"

namespace onnxruntime {

template <typename T>
auto EigenMap(Tensor& t) -> EigenVectorMap<T> {
  return EigenVectorMap<T>(t.template MutableData<T>(), gsl::narrow<ptrdiff_t>(t.Shape().Size()));
}

template <typename T>
auto EigenMap(const Tensor& t) -> ConstEigenVectorMap<T> {
  return ConstEigenVectorMap<T>(t.template Data<T>(), gsl::narrow<ptrdiff_t>(t.Shape().Size()));
}

}  // namespace onnxruntime


/***************************************************************************
    File                 : FrequencyCountDialog.h
    Project              : QtiPlot
    --------------------------------------------------------------------
    Copyright            : (C) 2008 by Ion Vasilief
    Email (use @ for *)  : ion_vasilief*yahoo.fr
    Description          : Frequency count options dialog

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program 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 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program 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 program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#ifndef FREQUENCYCOUNTDIALOG_H
#define FREQUENCYCOUNTDIALOG_H

#include <QDialog>
#include <gsl/gsl_vector.h>

class QPushButton;
class DoubleSpinBox;
class Table;

//! Filter options dialog
class FrequencyCountDialog : public QDialog
{
    Q_OBJECT

public:
    FrequencyCountDialog(Table *t, QWidget* parent = 0, Qt::WFlags fl = 0 );
    ~FrequencyCountDialog();

private slots:
    bool apply();
    void accept();

private:
    Table *d_source_table;
    Table *d_result_table;
    QString d_col_name;
    gsl_vector *d_col_values;
	int d_bins;

    QPushButton* buttonApply;
	QPushButton* buttonCancel;
	QPushButton* buttonOk;
	DoubleSpinBox* boxStart;
	DoubleSpinBox* boxEnd;
	DoubleSpinBox* boxStep;
};

#endif


/* multiset/gsl_multiset.h
 * based on combination/gsl_combination.h by Szymon Jaroszewicz
 * based on permutation/gsl_permutation.h by Brian Gough
 *
 * Copyright (C) 2009 Rhys Ulerich
 *
 * This program 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 of the License, or (at
 * your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MULTISET_H__
#define __GSL_MULTISET_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_multiset_struct
{
  size_t n;
  size_t k;
  size_t *data;
};

typedef struct gsl_multiset_struct gsl_multiset;

GSL_FUN gsl_multiset *gsl_multiset_alloc (const size_t n, const size_t k);
GSL_FUN gsl_multiset *gsl_multiset_calloc (const size_t n, const size_t k);
GSL_FUN void gsl_multiset_init_first (gsl_multiset * c);
GSL_FUN void gsl_multiset_init_last (gsl_multiset * c);
GSL_FUN void gsl_multiset_free (gsl_multiset * c);
GSL_FUN int gsl_multiset_memcpy (gsl_multiset * dest, const gsl_multiset * src);

GSL_FUN int gsl_multiset_fread (FILE * stream, gsl_multiset * c);
GSL_FUN int gsl_multiset_fwrite (FILE * stream, const gsl_multiset * c);
GSL_FUN int gsl_multiset_fscanf (FILE * stream, gsl_multiset * c);
GSL_FUN int gsl_multiset_fprintf (FILE * stream, const gsl_multiset * c, const char *format);

GSL_FUN size_t gsl_multiset_n (const gsl_multiset * c);
GSL_FUN size_t gsl_multiset_k (const gsl_multiset * c);
GSL_FUN size_t * gsl_multiset_data (const gsl_multiset * c);

GSL_FUN int gsl_multiset_valid (gsl_multiset * c);
GSL_FUN int gsl_multiset_next (gsl_multiset * c);
GSL_FUN int gsl_multiset_prev (gsl_multiset * c);

GSL_FUN INLINE_DECL size_t gsl_multiset_get (const gsl_multiset * c, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
size_t
gsl_multiset_get (const gsl_multiset * c, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= c->k)) /* size_t is unsigned, can't be negative */
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return c->data[i];
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_MULTISET_H__ */


/* spio.c
 *
 * Copyright (C) 2016 Patrick Alken
 * Copyright (C) 2016 Alexis Tantet
 *
 * This program 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 of the License, or (at
 * your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spmatrix.h>

/*
gsl_spmatrix_fprintf()
  Print sparse matrix to file in MatrixMarket format:

M  N  NNZ
I1 J1 A(I1,J1)
...

Note that indices start at 1 and not 0
*/

int
gsl_spmatrix_fprintf(FILE *stream, const gsl_spmatrix *m,
                     const char *format)
{
  int status;

  /* print header */
  status = fprintf(stream, "%%%%MatrixMarket matrix coordinate real general\n");
  if (status < 0)
    {
      GSL_ERROR("fprintf failed for header", GSL_EFAILED);
    }

  /* print rows,columns,nnz */
  status = fprintf(stream, "%u\t%u\t%u\n",
                   (unsigned int) m->size1,
                   (unsigned int) m->size2,
                   (unsigned int) m->nz);
  if (status < 0)
    {
      GSL_ERROR("fprintf failed for dimension header", GSL_EFAILED);
    }

  if (GSL_SPMATRIX_ISTRIPLET(m))
    {
      size_t n;

      for (n = 0; n < m->nz; ++n)
        {
          status = fprintf(stream, "%u\t%u\t", (unsigned int) m->i[n] + 1, (unsigned int) m->p[n] + 1);
          if (status < 0)
            {
              GSL_ERROR("fprintf failed", GSL_EFAILED);
            }

          status = fprintf(stream, format, m->data[n]);
          if (status < 0)
            {
              GSL_ERROR("fprintf failed", GSL_EFAILED);
            }

          status = putc('\n', stream);
          if (status == EOF)
            {
              GSL_ERROR("putc failed", GSL_EFAILED);
            }
        }
    }
  else if (GSL_SPMATRIX_ISCCS(m))
    {
      size_t j, p;

      for (j = 0; j < m->size2; ++j)
        {
          for (p = m->p[j]; p < m->p[j + 1]; ++p)
            {
              status = fprintf(stream, "%u\t%u\t", (unsigned int) m->i[p] + 1, (unsigned int) j + 1);
              if (status < 0)
                {
                  GSL_ERROR("fprintf failed", GSL_EFAILED);
                }

              status = fprintf(stream, format, m->data[p]);
              if (status < 0)
                {
                  GSL_ERROR("fprintf failed", GSL_EFAILED);
                }

              status = putc('\n', stream);
              if (status == EOF)
                {
                  GSL_ERROR("putc failed", GSL_EFAILED);
                }
            }
        }
    }
  else if (GSL_SPMATRIX_ISCRS(m))
    {
      size_t i, p;

      for (i = 0; i < m->size1; ++i)
        {
          for (p = m->p[i]; p < m->p[i + 1]; ++p)
            {
              status = fprintf(stream, "%u\t%u\t", (unsigned int) i + 1, (unsigned int) m->i[p] + 1);
              if (status < 0)
                {
                  GSL_ERROR("fprintf failed", GSL_EFAILED);
                }

              status = fprintf(stream, format, m->data[p]);
              if (status < 0)
                {
                  GSL_ERROR("fprintf failed", GSL_EFAILED);
                }

              status = putc('\n', stream);
              if (status == EOF)
                {
                  GSL_ERROR("putc failed", GSL_EFAILED);
                }
            }
        }
    }
  else
    {
      GSL_ERROR("unknown sparse matrix type", GSL_EINVAL);
    }

  return GSL_SUCCESS;
}

gsl_spmatrix *
gsl_spmatrix_fscanf(FILE *stream)
{
  gsl_spmatrix *m;
  unsigned int size1, size2, nz;
  char buf[1024];
  int found_header = 0;

  /* read file until we find rows,cols,nz header */
  while (fgets(buf, 1024, stream) != NULL)
    {
      int c;

      /* skip comments */
      if (*buf == '%')
        continue;

      c = sscanf(buf, "%u %u %u",
                 &size1, &size2, &nz);
      if (c == 3)
        {
          found_header = 1;
          break;
        }
    }

  if (!found_header)
    {
      GSL_ERROR_NULL ("fscanf failed reading header", GSL_EFAILED);
    }

  m = gsl_spmatrix_alloc_nzmax((size_t) size1, (size_t) size2, (size_t) nz, GSL_SPMATRIX_TRIPLET);
  if (!m)
    {
      GSL_ERROR_NULL ("error allocating m", GSL_ENOMEM);
    }

  {
    unsigned int i, j;
    double val;

    while (fgets(buf, 1024, stream) != NULL)
      {
        int c = sscanf(buf, "%u %u %lg", &i, &j, &val);
        if (c < 3 || (i == 0) || (j == 0))
          {
            GSL_ERROR_NULL ("error in input file format", GSL_EFAILED);
          }
        else if ((i > size1) || (j > size2))
          {
            GSL_ERROR_NULL ("element exceeds matrix dimensions", GSL_EBADLEN);
          }
        else
          {
            /* subtract 1 from (i,j) since indexing starts at 1 */
            gsl_spmatrix_set(m, i - 1, j - 1, val);
          }
      }
  }

  return m;
}

int
gsl_spmatrix_fwrite(FILE *stream, const gsl_spmatrix *m)
{
  size_t items;

  /* write header: size1, size2, nz */

  items = fwrite(&(m->size1), sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fwrite failed on size1", GSL_EFAILED);
    }

  items = fwrite(&(m->size2), sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fwrite failed on size2", GSL_EFAILED);
    }

  items = fwrite(&(m->nz), sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fwrite failed on nz", GSL_EFAILED);
    }

  /* write m->i and m->data which are size nz in all storage formats */

  items = fwrite(m->i, sizeof(size_t), m->nz, stream);
  if (items != m->nz)
    {
      GSL_ERROR("fwrite failed on row indices", GSL_EFAILED);
    }

  items = fwrite(m->data, sizeof(double), m->nz, stream);
  if (items != m->nz)
    {
      GSL_ERROR("fwrite failed on data", GSL_EFAILED);
    }

  if (GSL_SPMATRIX_ISTRIPLET(m))
    {
      items = fwrite(m->p, sizeof(size_t), m->nz, stream);
      if (items != m->nz)
        {
          GSL_ERROR("fwrite failed on column indices", GSL_EFAILED);
        }
    }
  else if (GSL_SPMATRIX_ISCCS(m))
    {
      items = fwrite(m->p, sizeof(size_t), m->size2 + 1, stream);
      if (items != m->size2 + 1)
        {
          GSL_ERROR("fwrite failed on column indices", GSL_EFAILED);
        }
    }
  else if (GSL_SPMATRIX_ISCRS(m))
    {
      items = fwrite(m->p, sizeof(size_t), m->size1 + 1, stream);
      if (items != m->size1 + 1)
        {
          GSL_ERROR("fwrite failed on column indices", GSL_EFAILED);
        }
    }

  return GSL_SUCCESS;
}

int
gsl_spmatrix_fread(FILE *stream, gsl_spmatrix *m)
{
  size_t size1, size2, nz;
  size_t items;

  /* read header: size1, size2, nz */

  items = fread(&size1, sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fread failed on size1", GSL_EFAILED);
    }

  items = fread(&size2, sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fread failed on size2", GSL_EFAILED);
    }

  items = fread(&nz, sizeof(size_t), 1, stream);
  if (items != 1)
    {
      GSL_ERROR("fread failed on nz", GSL_EFAILED);
    }

  if (m->size1 != size1)
    {
      GSL_ERROR("matrix has wrong size1", GSL_EBADLEN);
    }
  else if (m->size2 != size2)
    {
      GSL_ERROR("matrix has wrong size2", GSL_EBADLEN);
    }
  else if (nz > m->nzmax)
    {
      GSL_ERROR("matrix nzmax is too small", GSL_EBADLEN);
    }
  else
    {
      /* read m->i and m->data arrays, which are size nz for all formats */

      items = fread(m->i, sizeof(size_t), nz, stream);
      if (items != nz)
        {
          GSL_ERROR("fread failed on row indices", GSL_EFAILED);
        }

      items = fread(m->data, sizeof(double), nz, stream);
      if (items != nz)
        {
          GSL_ERROR("fread failed on data", GSL_EFAILED);
        }

      m->nz = nz;

      if (GSL_SPMATRIX_ISTRIPLET(m))
        {
          items = fread(m->p, sizeof(size_t), nz, stream);
          if (items != nz)
            {
              GSL_ERROR("fread failed on column indices", GSL_EFAILED);
            }

          /* build binary search tree for m */
          gsl_spmatrix_tree_rebuild(m);
        }
      else if (GSL_SPMATRIX_ISCCS(m))
        {
          items = fread(m->p, sizeof(size_t), size2 + 1, stream);
          if (items != size2 + 1)
            {
              GSL_ERROR("fread failed on row pointers", GSL_EFAILED);
            }
        }
      else if (GSL_SPMATRIX_ISCRS(m))
        {
          items = fread(m->p, sizeof(size_t), size1 + 1, stream);
          if (items != size1 + 1)
            {
              GSL_ERROR("fread failed on column pointers", GSL_EFAILED);
            }
        }
    }

  return GSL_SUCCESS;
}


//        Copyright The Authors 2018.
//    Distributed under the 3-Clause BSD License.
//    (See accompanying file LICENSE or copy at
//   https://opensource.org/licenses/BSD-3-Clause)

#pragma once

#include <array>     // for std::array
#include <bitset>    // for std::bitset
#include <cstdint>   // for standard int types
#include <cstring>   // for std::memcpy
#include <iosfwd>    // for implementation of operator<<
#include <iterator>  // for std::reverse_iterator
#include <string>    // for std::string

#include <gsl/gsl>  // for gsl::span

#include <codec/base16.h>   // for hex enc/decoding
#include <common/assert.h>  // for assertions
#include <crypto/random.h>  // for random block generation

namespace blocxxi {
namespace crypto {

namespace detail {
/// Forward declarations for utility conversion function between host and
/// network (big endian) byte orders.
/// Avoid including header files of boost and pulling all its bloat here.
std::uint32_t HostToNetwork(std::uint32_t);
std::uint32_t NetworkToHost(std::uint32_t);

/// Count the number of leading zero bits in a buffer of contiguous 32-bit
/// integers.
int CountLeadingZeroBits(gsl::span<std::uint32_t const> buf);

}  // namespace detail

/*!
   Represents a N bits hash digest or any other kind of N bits sequence.
   The structure is 32-bit aligned and must be at least 32-bits wide. It
   has almost the same semantics of std::array<> except that it will never be
   zero-length (size() is always > 0). It offers a number of additional
   convenience methods, such as bitwise arithemtics, comparisons etc.
 */
template <unsigned int BITS>
class Hash {
  static_assert(BITS % 32 == 0, "Hash size in bits must be a multiple of 32");
  static_assert(BITS > 0, "Hash size in bits must be greater than 0");

 public:
  using value_type = std::uint8_t;
  using pointer = uint8_t *;
  using const_pointer = std::uint8_t const *;
  using size_type = std::size_t;
  using difference_type = std::ptrdiff_t;
  using reference = std::uint8_t &;
  using const_reference = std::uint8_t const &;
  using iterator = pointer;
  using const_iterator = const_pointer;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;

  ///@name Constructors and Factory methods
  //@{
  /// Initializes the hash with all '0' bits
  Hash() : storage_({}) {}

  Hash(const Hash &other) = default;

  Hash &operator=(const Hash &rhs) = default;

  Hash(Hash &&) noexcept = default;

  Hash &operator=(Hash &&rhs) noexcept = default;

  virtual ~Hash() = default;

  /*!
  \brief Construct a Hash by assigning content from the provided sequence of
  bytes.

  > The source sequence can be smaller than the hash size but it __cannot be
  > bigger__.

  In the case it is smaller, the hash will be padded (leading bytes)
  with 0. The last byte in the source sequence will be the LSB of the hash
  (last byte in the hash).

  In the case it is bigger, result is undefined and, if assertions are enabled,
  this would be considered as a fatal error.

  \param buf source sequence of bytes.
  */
  explicit Hash(gsl::span<std::uint8_t const> buf) noexcept : storage_({}) {
    auto src_size = buf.size();
    ASAP_ASSERT_PRECOND(src_size <= Size());
    auto start = begin();
    if (Size() > src_size) {
      // Pad with zeros and adjust the start
      std::memset(Data(), 0, Size() - src_size);
      start += Size() - src_size;
    }
    Assign(buf, start);
  }

  /// \brief Returns an all-1 hash, i.e. the biggest value representable by an N
  /// bit number (N/8 bytes).
  static Hash Max() noexcept {
    Hash h;
    h.storage_.fill(0xffffffffU);
    return h;
  }

  /// \brief Returns an all-0 hash, i.e. the smallest value representable by an
  /// N bit number (N/8 bytes).
  static Hash Min() noexcept {
    return Hash();
    // all bits are already 0
  }

  static Hash<BITS> FromHex(const std::string &src, bool reverse = false) {
    Hash<BITS> hash;
    codec::hex::Decode(gsl::make_span<>(src),
                       gsl::make_span<>(hash.Data(), hash.Size()), reverse);
    return hash;
  }

  static Hash<BITS> RandomHash() {
    Hash<BITS> hash;
    hash.Randomize();
    return hash;
  }
  //@}

  /// @name Checked element access
  /// access specified element with bounds checking.
  //@{
  /*!
    Returns a reference to the element at specified location `pos`, with
    bounds checking.

    \param pos position of the element to return
    \exception std::out_of_range if !(pos < size()).
   */
  reference At(size_type pos) {
    if (Size() <= pos) {
      throw std::out_of_range("Hash<BITS> index out of range");
    }
    return reinterpret_cast<pointer>(storage_.data())[pos];
  }
  const_reference At(size_type pos) const {
    if (Size() <= pos) {
      throw std::out_of_range("Hash<BITS> index out of range");
    }
    return reinterpret_cast<const_pointer>(storage_.data())[pos];
  };
  //@}

  /// @name Unchecked element access
  /// access specified element __without__ bounds checking.
  //@{
  /// Returns a reference to the element at specified location pos. No bounds
  /// checking is performed.
  reference operator[](size_type pos) noexcept {
    ASAP_ASSERT_PRECOND(pos < Size());
    return reinterpret_cast<pointer>(storage_.data())[pos];
  }
  const_reference operator[](size_type pos) const noexcept {
    ASAP_ASSERT_PRECOND(pos < Size());
    return reinterpret_cast<pointer>(storage_.data())[pos];
  }

  /// Returns a reference to the first element in the container.
  reference Front() { return reinterpret_cast<pointer>(storage_.data())[0]; }
  const_reference Front() const {
    return reinterpret_cast<const_pointer>(storage_.data())[0];
  }

  /// Returns reference to the last element in the container.
  reference Back() {
    return reinterpret_cast<pointer>(storage_.data())[Size() - 1];
  }
  const_reference Back() const {
    return reinterpret_cast<const_pointer>(storage_.data())[Size() - 1];
  }

  /// Returns pointer to the underlying array serving as element storage. The
  /// pointer is such that range [Data(); Data() + Size()) is always a valid
  /// range.
  pointer Data() noexcept { return reinterpret_cast<pointer>(storage_.data()); }
  const_pointer Data() const noexcept {
    return reinterpret_cast<const_pointer>(storage_.data());
  }
  //@}

  /// @name Iterators
  //@{
  /// Returns an iterator to the first element of the container.
  iterator begin() noexcept {
    return reinterpret_cast<pointer>(storage_.data());
  }
  const_iterator begin() const noexcept {
    return reinterpret_cast<const_pointer>(storage_.data());
  }
  const_iterator cbegin() const noexcept { return begin(); }

  /// Returns an iterator to the element following the last element of the
  /// container. This element acts as a placeholder; attempting to access it
  /// results in undefined behavior.
  iterator end() noexcept {
    return reinterpret_cast<pointer>(storage_.data()) + Size();
  }
  const_iterator end() const noexcept {
    return reinterpret_cast<const_pointer>(storage_.data()) + Size();
  }
  const_iterator cend() const noexcept { return end(); }

  /// Returns a reverse iterator to the first element of the reversed container.
  /// It corresponds to the last element of the non-reversed container.
  reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
  const_reverse_iterator rbegin() const noexcept {
    return const_reverse_iterator(end());
  }
  const_reverse_iterator crbegin() const noexcept { return rbegin(); }

  /// Returns a reverse iterator to the element following the last element of
  /// the reversed container. It corresponds to the element preceding the first
  /// element of the non-reversed container. This element acts as a placeholder,
  /// attempting to access it results in undefined behavior.
  reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
  const_reverse_iterator rend() const noexcept {
    return const_reverse_iterator(begin());
  }
  const_reverse_iterator crend() const noexcept { return rend(); }
  //@}

  // Capacity

  /// The size of the hash in bytes.
  constexpr static std::size_t Size() { return BITS / 8; };

  /// The size of the hash in bytes.
  constexpr static std::size_t BitSize() { return BITS; };

  // Operations

  /// Set all bits to 0.
  void Clear() { storage_.fill(0); }

  /// Return true if all bits are set to 0.
  bool IsAllZero() const {
    for (auto v : storage_)
      if (v != 0) return false;
    return true;
  }

  /// Exchanges the contents of the container with those of other. Does not
  /// cause iterators and references to associate with the other container.
  void swap(Hash &other) noexcept { storage_.swap(other.storage_); }

  /// Count leading zero bits.
  int LeadingZeroBits() const {
    return detail::CountLeadingZeroBits(gsl::make_span(storage_));
  }

  /// @name Comparison operators
  /// The important thing to note here is that only two of these operators
  /// actually do anything, the others are just forwarding their arguments to
  /// either of these two to do the actual work (== and <). Therefore only these
  /// two are implemented as friends.
  //@{
  /// \brief Checks if the contents of `lhs` and `rhs` are equal, that is,
  /// whether each element in `lhs` compares equal with the element in `rhs` at
  /// the same position.
  friend bool operator==(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
    return std::equal(lhs.begin(), lhs.end(), rhs.begin());
  }
  /// \brief Compares the contents of `lhs` and `rhs` lexicographically. The
  /// comparison is performed by a function equivalent to
  /// `std::lexicographical_compare`.
  friend bool operator<(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
    for (std::size_t i = 0; i < SIZE_DWORD; ++i) {
      std::uint32_t const native_lhs = detail::NetworkToHost(lhs.storage_[i]);
      std::uint32_t const native_rhs = detail::NetworkToHost(rhs.storage_[i]);
      if (native_lhs < native_rhs) return true;
      if (native_lhs > native_rhs) return false;
    }
    return false;
  }
  //@}

  /// In-place bitwise XOR with the given other hash.
  Hash &operator^=(Hash const &other) noexcept {
    for (std::size_t i = 0; i < SIZE_DWORD; ++i)
      storage_[i] ^= other.storage_[i];
    return *this;
  }

  /*!
  Starting at the given position in the hash, assign contents from the given
  sequence of bytes.

  > Data represented by the span is expected to be organized in network byte
  > order (i.e. the byte returned by span.first() is the MSB (Most Significant
  > Byte) and therefore will go into the MSB of the Hash).

  The _span_ size can be smaller than the remaining space after _start_ in
  the hash but it __cannot be bigger__. If this is not true, result is undefined
  and, if assertions are enabled, this would be considered as a fatal error.

  \param buf source sequence of bytes.
  \param start position at which to assign the sequence in the hash.
  */
  void Assign(gsl::span<std::uint8_t const> buf, iterator start) noexcept {
    size_type src_size = buf.size();
    size_type dst_size = end() - start;
    ASAP_ASSERT_PRECOND(src_size <= dst_size);
    std::memcpy(start, buf.data(), std::min(src_size, dst_size));
  }

  // TODO: Replace the parameters with a span
  void Randomize() { random::GenerateBlock(Data(), Size()); }

  const std::string ToHex() const {
    return codec::hex::Encode(gsl::make_span<>(Data(), Size()), false, true);
  }

  std::bitset<BITS> ToBitSet() const {
    std::bitset<BITS> bs;  // [0,0,...,0]
    int shift_left = BITS;
    for (uint32_t num_part : storage_) {
      shift_left -= 32;
      num_part = detail::HostToNetwork(num_part);
      std::bitset<BITS> bs_part(num_part);
      bs |= bs_part << shift_left;
    }
    return bs;
  }

  std::string ToBitStringShort(std::size_t length = 32U) const {
    auto truncate = false;
    if (length < BITS) { truncate = true; length -= 3; }
    auto str = ToBitSet().to_string().substr(0, length);
    if (truncate) str.append("...");
    return str;
  }

 private:
  /// The size of the underlying data buffer in terms of 32-bit DWORD
  static constexpr std::size_t SIZE_DWORD = BITS / 32;

  /// The underlying data buffer is built as an array of unsigned 32-bit
  /// integers to facilitate the math operations. Note however that iteration
  /// over the Hash and random access to the data should always be presented as
  /// if it were a byte array layed out in network order (big endian).
  ///
  /// > First DWORD is the most significant 4 bytes.
  std::array<std::uint32_t, SIZE_DWORD> storage_;
};

/// @name Comparison operators
/// Additional Comparison operators (not needing to be friend)
//@{

template <unsigned int BITS>
inline bool operator!=(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
  return !(lhs == rhs);
}
template <unsigned int BITS>
inline bool operator>(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
  return rhs < lhs;
}
template <unsigned int BITS>
inline bool operator<=(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
  return !(lhs > rhs);
}
template <unsigned int BITS>
inline bool operator>=(const Hash<BITS> &lhs, const Hash<BITS> &rhs) {
  return !(lhs < rhs);
}
//@}

/// Bitwise XOR
template <unsigned int BITS>
inline Hash<BITS> operator^(Hash<BITS> lhs, Hash<BITS> const &rhs) noexcept {
  return lhs.operator^=(rhs);
}

// Specialized algorithms

/// Exchanges the contents of one with other. Does not cause
/// iterators and references to associate with the other container.
template <unsigned int BITS>
inline void swap(Hash<BITS> &lhs, Hash<BITS> &rhs) {
  lhs.swap(rhs);
}

/// Dump the hex-encoded contents of the hash to the stream.
template <unsigned int BITS>
inline std::ostream &operator<<(std::ostream &out, Hash<BITS> const &hash) {
  out << hash.ToHex();
  return out;
}

using Hash512 = Hash<512>;  // 64 bytes
using Hash256 = Hash<256>;  // 32 bytes
using Hash160 = Hash<160>;  // 20 bytes

}  // namespace crypto
}  // namespace blocxxi


/* -*- mode: C; c-basic-offset: 4 -*- */
/* ex: set shiftwidth=4 tabstop=4 expandtab: */
/*
 * Copyright (c) 2018, Colorado School of Mines
 * All rights reserved.
 *
 * Author(s): Neil T. Dantam <ndantam@miens.edu>
 * Georgia Tech Humanoid Robotics Lab
 * Under Direction of Prof. Mike Stilman <mstilman@cc.gatech.edu>
 *
 *
 * This file is provided under the following "BSD-style" License:
 *
 *
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 *
 */

// uncomment to check that local allocs actually get freed
// #define AA_ALLOC_STACK_MAX 0

#include <stdlib.h>
#include <cblas.h>

#include "amino.h"
#include "amino/mat.h"
#include "amino/mat_internal.h"

#define VEC_LEN(X) ((int)(X->len))
#define MAT_ROWS(X) ((int)(X->rows))
#define MAT_COLS(X) ((int)(X->cols))

/******************/
/* Error Handling */
/******************/

static void
s_err_default( const char *message )
{
    fprintf(stderr, "AMINO ERROR: %s\n",message);
    abort();
    exit(EXIT_FAILURE);
}

static aa_la_err_fun  *s_err_fun = s_err_default;

void
aa_la_set_err( aa_la_err_fun *fun )
{
    s_err_fun = fun;
}

AA_API void
aa_la_err( const char *message ) {
    s_err_fun(message);
}

AA_API void
aa_la_fail_size( size_t a, size_t b )
{
        const size_t size = 256;
        char buf[size];
        snprintf(buf,size, "Mismatched sizes: %lu != %lu\n", a, b);
        aa_la_err(buf);
}

/****************/
/* Construction */
/****************/

AA_API void
aa_dvec_view( struct aa_dvec *vec, size_t len, double *data, size_t inc )
{
    *vec = AA_DVEC_INIT(len,data,inc);
}

AA_API void
aa_dvec_slice( const struct aa_dvec *src,
               size_t start,
               size_t stop,
               size_t step,
               struct aa_dvec *dst )
{
    if( stop > src->len || stop < start ) {
        aa_la_err("Slice out-of-bounds\n");
    }
    *dst = AA_DVEC_INIT( (stop - start) / step,
                         src->data + src->inc*start,
                         src->inc*step );
}

AA_API void
aa_dmat_row_vec( const struct aa_dmat *src, size_t row, struct aa_dvec *dst )
{
    if( row >= src->rows ) {
        aa_la_err("Row vector out-of-bounds\n");
    }
    aa_dvec_view(dst,src->cols, src->data + row, src->ld);
}

AA_API void
aa_dmat_col_vec( const struct aa_dmat *src, size_t col, struct aa_dvec *dst )
{
    if( col >= src->cols ) {
        aa_la_err("Row vector out-of-bounds\n");
    }
    aa_dvec_view(dst, src->rows, src->data + col*src->ld, 1);
}

AA_API void
aa_dmat_diag_vec( const struct aa_dmat *src, struct aa_dvec *dst )
{
    aa_dvec_view(dst, AA_MIN(src->rows, src->cols), src->data, src->ld + 1);
}

AA_API void
aa_dmat_view( struct aa_dmat *mat, size_t rows, size_t cols,
              double *data, size_t ld )
{
    *mat = AA_DMAT_INIT(rows,cols,data,ld);
}

AA_API void
aa_dmat_view_block(  struct aa_dmat *dst,
                     const struct aa_dmat *src,
                     size_t row_start, size_t col_start,
                     size_t rows, size_t cols )
{
    aa_dmat_block( src,
                   row_start, col_start,
                   row_start + rows, col_start + cols,
                   dst );
}

AA_API void
aa_dmat_block( const struct aa_dmat *src,
               size_t row_start, size_t col_start,
               size_t row_end, size_t col_end,
               struct aa_dmat *dst )
{
    size_t m = src->rows, n = src->cols;
    if( row_start >= row_end ||
        row_start >= m ||
        row_end > m ||
        col_start >= col_end ||
        col_start >= n ||
        col_end > n )
    {
        aa_la_err("Block out-of-bounds\n");
    }

    aa_dmat_view( dst,
                  row_end - row_start, col_end - col_start,
                  src->data + col_start*src->ld + row_start, src->ld );
}

AA_API struct aa_dvec *
aa_dvec_malloc( size_t len ) {
    struct aa_dvec *r;
    const size_t s_desc = sizeof(*r);
    const size_t s_elem = sizeof(r->data[0]);
    const size_t pad =  s_elem - (s_desc % s_elem);
    const size_t size = s_desc + pad + len * s_elem;

    const size_t off = s_desc + pad;

    char *ptr = (char*)malloc( size );

    r = (struct aa_dvec*)ptr;
    aa_dvec_view(r, len, (double*)(ptr+off), 1);
    return r;
}

AA_API struct aa_dvec *
aa_dvec_alloc( struct aa_mem_region *reg, size_t len ) {
    struct aa_dvec *r;
    const size_t s_desc = sizeof(*r);
    const size_t s_elem = sizeof(r->data[0]);
    const size_t pad =  s_elem - (s_desc % s_elem);
    char *ptr = (char*)aa_mem_region_alloc(reg, s_desc + pad + len * s_elem );

    r = (struct aa_dvec*)ptr;
    aa_dvec_view(r, len, (double*)(ptr+s_desc+pad), 1);
    return r;
}

AA_API struct aa_dvec *
aa_dvec_dup( struct aa_mem_region *reg, const struct aa_dvec *src)
{
    struct aa_dvec *dst = aa_dvec_alloc(reg,src->len);
    aa_dvec_copy(src,dst);
    return dst;
}

AA_API struct aa_dmat *
aa_dmat_dup( struct aa_mem_region *reg, const struct aa_dmat *src)
{
    struct aa_dmat *dst = aa_dmat_alloc(reg,src->rows,src->cols);
    aa_dmat_copy(src,dst);
    return dst;
}

AA_API struct aa_dmat *
aa_dmat_malloc( size_t rows, size_t cols )
{
    struct aa_dmat *r;
    const size_t s_desc = sizeof(*r);
    const size_t s_elem = sizeof(r->data[0]);
    const size_t pad =  s_elem - (s_desc % s_elem);
    char *ptr = (char*)malloc( s_desc + pad + rows*cols * s_elem );

    r = (struct aa_dmat*)ptr;
    aa_dmat_view(r, rows, cols, (double*)(ptr+s_desc+pad), rows);

    return r;
}

AA_API struct aa_dmat *
aa_dmat_alloc( struct aa_mem_region *reg, size_t rows, size_t cols )
{
    struct aa_dmat *r;
    const size_t s_desc = sizeof(*r);
    const size_t s_elem = sizeof(r->data[0]);
    const size_t pad =  s_elem - (s_desc % s_elem);
    size_t size = s_desc + pad + rows*cols * s_elem ;

    char *ptr = (char*)aa_mem_region_alloc(reg, size);

    r = (struct aa_dmat*)ptr;
    aa_dmat_view( r, rows, cols, (double*)(ptr+s_desc+pad), rows );

    return r;
}

AA_API void
aa_dmat_set( struct aa_dmat *A, double alpha, double beta )
{
    int mi   = (int)(A->rows);
    int ni   = (int)(A->cols);
    int ldai = (int)(A->ld);

    dlaset_( "G", &mi, &ni,
             &alpha, &beta,
             A->data, &ldai );

}

AA_API void
aa_dvec_set( struct aa_dvec *vec, double alpha )
{
    double *end = vec->data + vec->len*vec->inc;
    for( double *x = vec->data; x < end; x += vec->inc ) {
        *x = alpha;
    }
}

void
aa_dvec_zero( struct aa_dvec *vec )
{
    aa_dvec_set(vec,0);
}

AA_API void
aa_dmat_zero( struct aa_dmat *mat )
{
    aa_dmat_set(mat, 0, 0);
}

/* Level 1 BLAS */
AA_API void
aa_dvec_swap( struct aa_dvec *x, struct aa_dvec *y )
{
    aa_la_check_size(x->len, y->len);
    cblas_dswap( VEC_LEN(x), AA_VEC_ARGS(x), AA_VEC_ARGS(y) );
}

AA_API void
aa_dvec_scal( double a, struct aa_dvec *x )
{
    cblas_dscal( VEC_LEN(x), a, AA_VEC_ARGS(x) );
}

static void s_inc( size_t n, double alpha, double *x, size_t inc ) {
    for( double *end = x + n*inc; x < end; x += inc ) {
        *x += alpha;
    }
}

AA_API void
aa_dvec_inc( double alpha, struct aa_dvec *x )
{
    s_inc( x->len, alpha, x->data, x->inc );
}

AA_API void
aa_dvec_copy( const struct aa_dvec *x, struct aa_dvec *y )
{
    aa_la_check_size(x->len, y->len);
    cblas_dcopy( VEC_LEN(x), AA_VEC_ARGS(x), AA_VEC_ARGS(y) );
}

AA_API void
aa_dvec_axpy( double a, const struct aa_dvec *x, struct aa_dvec *y )
{
    aa_la_check_size(x->len, y->len);
    cblas_daxpy( VEC_LEN(x), a, AA_VEC_ARGS(x), AA_VEC_ARGS(y) );
}

AA_API double
aa_dvec_dot( const struct aa_dvec *x, struct aa_dvec *y )
{
    aa_la_check_size(x->len, y->len);
    return cblas_ddot( VEC_LEN(x), AA_VEC_ARGS(x), AA_VEC_ARGS(y) );
}

AA_API double
aa_dvec_nrm2( const struct aa_dvec *x )
{
    return cblas_dnrm2( VEC_LEN(x), AA_VEC_ARGS(x) );
}

/* Level 2 BLAS */
AA_API void
aa_dmat_gemv( CBLAS_TRANSPOSE trans,
              double alpha, const struct aa_dmat *A,
              const struct aa_dvec *x,
              double beta, struct aa_dvec *y )
{
    if( CblasTrans == trans ) {
        aa_la_check_size( A->rows, x->len );
        aa_la_check_size( A->cols, y->len );
    } else {
        aa_la_check_size( A->rows, y->len );
        aa_la_check_size( A->cols, x->len );
    }

    cblas_dgemv( CblasColMajor, trans,
                 MAT_ROWS(A), MAT_COLS(A),
                 alpha, AA_MAT_ARGS(A),
                 AA_VEC_ARGS(x),
                 beta, AA_VEC_ARGS(y) );

}

/* Level 3 BLAS */
AA_API void
aa_dmat_gemm( CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB,
              double alpha, const struct aa_dmat *A,
              const struct aa_dmat *B,
              double beta, struct aa_dmat *C )
{
    aa_la_check_size( A->rows, C->rows );
    aa_la_check_size( A->cols, B->rows );
    aa_la_check_size( B->cols, C->cols );

    assert( A->rows <= A->ld );
    assert( B->rows <= B->ld );
    assert( C->rows <= C->ld );

    cblas_dgemm( CblasColMajor,
                 transA, transB,
                 MAT_ROWS(A), MAT_COLS(B), MAT_COLS(A),
                 alpha, AA_MAT_ARGS(A),
                 AA_MAT_ARGS(B),
                 beta, AA_MAT_ARGS(C) );
}

/* LAPACK */

AA_API void
aa_dmat_lacpy( const char uplo[1],
               const struct aa_dmat *A,
               struct aa_dmat *B )
{

    aa_la_check_size(A->rows,B->rows);
    aa_la_check_size(A->cols,B->cols);
    int mi   = (int)(A->rows);
    int ni   = (int)(A->cols);
    int ldai = (int)(A->ld);
    int ldbi = (int)(B->ld);

    dlacpy_(uplo, &mi, &ni,
            A->data, &ldai,
            B->data, &ldbi);
}

AA_API void
aa_dmat_copy(  const struct aa_dmat *A, struct aa_dmat *B)
{
    aa_dmat_lacpy("G",A,B);
}

/* Matrix Functions */

static double s_ssd( size_t n,
                     double a,
                     double *x, size_t incx,
                     double *y, size_t incy )
{
    for( double *e = x + n*incx; x < e; x += incx, y+=incy ) {
        double d = *x - *y;
        a += d*d;
    }
    return a;
}

AA_API double
aa_dvec_ssd( const struct aa_dvec *x, const struct aa_dvec *y)
{
    aa_la_check_size( x->len, y->len );
    return s_ssd( x->len, 0,
                  x->data, x->inc,
                  y->data, y->inc );
}

AA_API double
aa_dmat_ssd( const struct aa_dmat *A, const struct aa_dmat *B)
{
    size_t m = A->rows, n = A->cols;
    aa_la_check_size( m, B->rows );
    aa_la_check_size( n, B->cols );
    double a = 0;
    for( double *Ac=A->data, *Bc=B->data, *ec=A->data + n*A->ld;
         Ac < ec;
         Ac+=A->ld, Bc+=B->ld )
    {
        a = s_ssd( m, a, Ac, 1, Bc, 1 );
    }
    return a;
}

AA_API double
aa_dmat_nrm2( const struct aa_dmat *A )
{
    int m=(int)A->rows, n=(int)A->cols, ld=(int)A->ld;
    return dlange_("F", &m, &n, A->data, &ld, NULL );
}

AA_API void
aa_dmat_scal( struct aa_dmat *x, double alpha )
{
    int m = (int)x->rows, n=(int)x->cols, ld=(int)x->ld;
    double cfrom=1;
    int info;
    dlascl_("G", NULL, NULL,
            &cfrom, &alpha,
            &m, &n, x->data, &ld,
            &info);
}

AA_API void
aa_dmat_inc( struct aa_dmat *A, double alpha )
{
    size_t m=A->rows, n=A->cols, ld=A->ld;
    double *x = A->data;

    if( m == n ) {
        s_inc(m*n, alpha, x, 1);
    } else {
        for( double *e = x + n*ld; x < e; x+=ld ) {
            s_inc(m, alpha, x, 1);
        }
    }

}

AA_API void
aa_dmat_axpy( double alpha, const struct aa_dmat *X, struct aa_dmat *Y)
{
    size_t m=X->rows, n=X->cols, ldX=X->ld, ldY=Y->ld;
    double *x=X->data, *y=Y->data;

    aa_la_check_size(m, Y->rows);
    aa_la_check_size(n, Y->cols);

    if( m == ldX && m == ldY ) {
        cblas_daxpy( (int)(m*n), alpha, x,1, y,1 );
    } else {
        int mi = (int)m;
        for( double *e = x + n*ldX; x < e; x+=ldX, y+=ldY ) {
            cblas_daxpy( mi, alpha, x,1, y,1 );
        }
    }
}

void
aa_dmat_trans( const struct aa_dmat *A, struct aa_dmat *B)
{
    const size_t m   = A->rows;
    const size_t n   = A->cols;
    const size_t lda = A->ld;
    const size_t ldb = B->ld;

    aa_la_check_size(m, B->cols);
    aa_la_check_size(n, B->rows);

    for ( double *Acol = A->data, *Brow=B->data, *Be=B->data + n;
          Brow < Be;
          Brow++, Acol+=lda )
    {
        cblas_dcopy( (int)m, Acol, 1, Brow, (int)ldb );
    }
}

AA_API int
aa_dmat_inv( struct aa_dmat *A )
{
    aa_la_check_size(A->rows,A->cols);
    int info;

    int *ipiv = (int*)
        aa_mem_region_local_alloc(sizeof(int)*A->rows);

    // LU-factor
    info = aa_cla_dgetrf( MAT_ROWS(A), MAT_COLS(A), AA_MAT_ARGS(A), ipiv );

    int lwork = -1;
    while(1) {
        double *work = (double*)
            aa_mem_region_local_tmpalloc( sizeof(double)*
                                      (size_t)(lwork < 0 ? 1 : lwork) );
        aa_cla_dgetri( MAT_ROWS(A), AA_MAT_ARGS(A), ipiv, work, lwork );
        if( lwork > 0 ) break;
        assert( -1 == lwork );
        lwork = (int)work[0];
    }

    aa_mem_region_local_pop(ipiv);

    return info;
}

int s_svd_helper ( const struct aa_dmat *A,
                   struct aa_mem_region *reg,
                   size_t *pkmin, size_t *pkmax,
                   double **pU, double **pVt, double **pS )
{

    size_t m = A->rows;
    size_t n = A->cols;

    // find  min/max dimensions
    if( m < n ) {
        *pkmin = m;
        *pkmax = n;
    } else {
        *pkmin = n;
        *pkmax = m;
    }

    // This method uses the SVD
    double *W  = (double*)aa_mem_region_alloc( reg, sizeof(double) * (m*m + n*n + *pkmin) );
    *pU  = W;        // size m*m
    *pVt = *pU + m*m; // size n*n
    *pS  = *pVt + n*n; // size min(m,n)

    // A = U S V^T
    aa_la_d_svd( m,n,
                 A->data, A->ld,
                 *pU, m, *pS, *pVt, n );

    return 0;
}

int
aa_dmat_pinv( const struct aa_dmat *A, double tol, struct aa_dmat *As )
{

    aa_la_check_size(A->rows, As->cols);
    aa_la_check_size(A->cols, As->rows);

    struct aa_mem_region *reg =  aa_mem_region_local_get();
    void *ptrtop = aa_mem_region_ptr(reg);

    size_t kmin, kmax;
    double *U, *Vt, *S;
    s_svd_helper( A, reg,
                  &kmin, &kmax, &U, &Vt, &S );

    size_t m = A->rows;
    const int mi = (int)(A->rows);
    const int ni = (int)(A->cols);

    if( tol < 0 ) {
        tol = (double)kmax * S[0] * DBL_EPSILON;
    }

    // \sum 1/s_i * v_i * u_i^T
    aa_dmat_zero(As);
    double *Asd = As->data;
    for( size_t i = 0; i < kmin && S[i] > tol; i ++ ) {
        cblas_dger( CblasColMajor, ni, mi, 1/S[i],
                    Vt + i, ni,
                    U + m*i, 1,
                    Asd, ni
            );
    }

    aa_mem_region_pop( reg, ptrtop );

    return 0;

    /* struct aa_mem_region *reg = aa_mem_region_local_get(); */
    /* struct aa_dmat *Ap = aa_dmat_alloc(reg,m,n); */

    /* // B = AA^T */
    /* double *B; */
    /* int ldb; */
    /* if( m <= n ) { */
    /*     /\* Use A_star as workspace when it's big enough *\/ */
    /*     B = As->data; */
    /*     ldb = (int)(As->ld); */
    /* } else { */
    /*     B = AA_MEM_REGION_NEW_N( reg, double, m*m ); */
    /*     ldb = (int)m; */
    /* } */

    /* aa_la_dlacpy( "A", A, Ap ); */

    /* // B is symmetric.  Only compute the upper half. */
    /* cblas_dsyrk( CblasColMajor, CblasUpper, CblasNoTrans, */
    /*              MAT_ROWS(Ap), MAT_COLS(Ap), */
    /*              1, AA_MAT_ARGS(Ap), */
    /*              0, B, ldb ); */

    /* // B += kI */
    /* /\* for( size_t i = 0; i < m*(size_t)ldb; i += ((size_t)ldb+1) ) *\/ */
    /* /\*     B[i] += k; *\/ */

    /* /\* Solve via Cholesky Decomp *\/ */
    /* /\* B^T (A^*)^T = A    and     B = B^T (Hermitian) *\/ */

    /* aa_cla_dposv( 'U', (int)m, (int)n, */
    /*               B, ldb, */
    /*               AA_MAT_ARGS(Ap) ); */

    /* aa_dmat_trans( Ap, As ); */

    /* aa_mem_region_pop( reg, Ap ); */

}

int
aa_dmat_dpinv( const struct aa_dmat *A, double k, struct aa_dmat *As)
{
    aa_la_check_size(A->rows, As->cols);
    aa_la_check_size(A->cols, As->rows);

    // TODO: Try DGESV for non-positive-definite matrices

    size_t m = A->rows;
    size_t n = A->cols;

    struct aa_mem_region *reg = aa_mem_region_local_get();
    void *ptrtop = aa_mem_region_ptr(reg);
    int r = -1;

    /* As = inv(A'*A - k*I) * A' A'*inv(A*A' - k*I) */
    if( m <= n ) {
        /*
         * (A^*) = A^T*inv(A*A^T - k*I)
         * (A^*) = A^T*inv(B)
         * (A^*)*B = A^T
         * B^T (A^*)^T = A    and     B = B^T (Hermitian)
         *
         */

        /* Use A_star as workspace for B */
        double *B = As->data;
        int ldb = (int)(As->ld);

        /* Ap = A */
        struct aa_dmat *Ap = aa_dmat_alloc(reg,m,n);
        aa_dmat_lacpy( "A", A, Ap );

        // B is symmetric.  Only compute the upper half.
        // B = A * A'
        cblas_dsyrk( CblasColMajor, CblasUpper, CblasNoTrans,
                     MAT_ROWS(Ap), MAT_COLS(Ap),
                     1, AA_MAT_ARGS(Ap),
                     0, B, ldb );

        /* B += kI */
        for( double *x = B, *e = B+(int)m*ldb; x < e; x+=ldb+1 )
            *x += k;

        /* B^T (A^*)^T = A    and     B = B^T (Hermitian) */

        /* Solve via Cholesky (positive definite) */
        r = aa_cla_dposv( 'U', (int)m, (int)n,
                           B, ldb,
                           AA_MAT_ARGS(Ap) );

        /* Solve via LU */
        /* int *ipiv = AA_MEM_REGION_NEW_N(reg,int,m); */
        /* r = aa_la_d_sysv( "U", m, n, */
        /*                   B, (size_t)ldb, */
        /*                   ipiv, */
        /*                   Ap->data, Ap->ld ); */

        aa_dmat_trans( Ap, As );

    } else {

        /*
         * (A^*) = inv(A^T*A - k*I) * A^T
         * (A^*) = inv(B) * A^T
         * B*(A^*) = A^T
         */

        /* Use A_star as workspace for B */
        double *B = AA_MEM_REGION_NEW_N(reg,double,n*n);
        int ldb = (int)(n);

        /* As = A^T */
        aa_dmat_trans( A, As );

        // B is symmetric.  Only compute the upper half.
        // B = A' * A = As * As'
        cblas_dsyrk( CblasColMajor, CblasUpper, CblasNoTrans,
                     MAT_ROWS(As), MAT_COLS(As),
                     1, AA_MAT_ARGS(As),
                     0, B, ldb );

        /* B += kI */
        //for( size_t i = 0; i < n*(size_t)ldb; i += ((size_t)ldb+1) )
        for( double *x = B, *e = B+n*n; x < e; x+=ldb+1 )
            *x += k;

        /* B * (A^*)^T = A    and     B = B^T (Hermitian) */

        /* Solve via Cholesky (positive definite) */
        r = aa_cla_dposv( 'U', (int)n, (int)m,
                           B, ldb,
                           AA_MAT_ARGS(As) );

        /* Solve via LU */
        /* int *ipiv = AA_MEM_REGION_NEW_N(reg,int,n); */
        /* r = aa_la_d_sysv( "U", n, m, */
        /*                   B, (size_t)ldb, */
        /*                   ipiv, */
        /*                   As->data, As->ld ); */

    }

    aa_mem_region_pop( reg, ptrtop );

    return r;
}

int
aa_dmat_dzdpinv(  const struct aa_dmat *A, double s_min, struct aa_dmat *As)
{

    aa_la_check_size(A->rows, As->cols);
    aa_la_check_size(A->cols, As->rows);

    struct aa_mem_region *reg =  aa_mem_region_local_get();
    void *ptrtop = aa_mem_region_ptr(reg);

    size_t kmin, kmax;
    double *U, *Vt, *S;
    s_svd_helper( A, reg,
                  &kmin, &kmax, &U, &Vt, &S );

    size_t m = A->rows;
    const int mi = (int)(A->rows);
    const int ni = (int)(A->cols);

    // \sum s_i/(s_i**2+k) * v_i * u_i^T
    aa_dmat_zero(As);
    double *Asd = As->data;
    size_t i = 0;

    // Undamped parts
    for( ; i < kmin && S[i] >= s_min; i ++ ) {
        cblas_dger( CblasColMajor, ni, mi, 1/S[i],
                    Vt + i, ni,
                    U + m*i, 1,
                    Asd, ni
            );
    }

    // Damped parts
    double s2 = s_min*s_min;
    for( ; i < kmin; i ++ ) {
        cblas_dger( CblasColMajor, ni, mi, S[i]/s2,
                    Vt + i, ni,
                    U + m*i, 1,
                    Asd, ni
            );
    }

    aa_mem_region_pop( reg, ptrtop );

    return 0;

}


/* roots/fdfsolver.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Reid Priedhorsky, Brian Gough
 *
 * This program 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 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>

gsl_root_fdfsolver *
gsl_root_fdfsolver_alloc (const gsl_root_fdfsolver_type * T)
{

  gsl_root_fdfsolver * s = (gsl_root_fdfsolver *) malloc (sizeof (gsl_root_fdfsolver));

  if (s == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for root solver struct",
			GSL_ENOMEM, 0);
    };

  s->state = malloc (T->size);

  if (s->state == 0)
    {
      free (s);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for root solver state",
			GSL_ENOMEM, 0);
    };

  s->type = T ;
  s->fdf = NULL;

  return s;
}

int
gsl_root_fdfsolver_set (gsl_root_fdfsolver * s, gsl_function_fdf * f, double root)
{
  s->fdf = f;
  s->root = root;

  return (s->type->set) (s->state, s->fdf, &(s->root));
}

int
gsl_root_fdfsolver_iterate (gsl_root_fdfsolver * s)
{
  return (s->type->iterate) (s->state, s->fdf, &(s->root));
}

void
gsl_root_fdfsolver_free (gsl_root_fdfsolver * s)
{
  free (s->state);
  free (s);
}

const char *
gsl_root_fdfsolver_name (const gsl_root_fdfsolver * s)
{
  return s->type->name;
}

double
gsl_root_fdfsolver_root (const gsl_root_fdfsolver * s)
{
  return s->root;
}