/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Sun Nov  7 20:44:26 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2real 15 */

/*
 * This function contains 64 FP additions, 31 FP multiplications,
 * (or, 47 additions, 14 multiplications, 17 fused multiply/add),
 * 36 stack variables, and 30 memory accesses
 */
static const fftw_real K1_118033988 = FFTW_KONST(+1.118033988749894848204586834365638117720309180);
static const fftw_real K1_902113032 = FFTW_KONST(+1.902113032590307144232878666758764286811397268);
static const fftw_real K1_175570504 = FFTW_KONST(+1.175570504584946258337411909278145537195304875);
static const fftw_real K500000000 = FFTW_KONST(+0.500000000000000000000000000000000000000000000);
static const fftw_real K866025403 = FFTW_KONST(+0.866025403784438646763723170752936183471402627);
static const fftw_real K2_000000000 = FFTW_KONST(+2.000000000000000000000000000000000000000000000);
static const fftw_real K1_732050807 = FFTW_KONST(+1.732050807568877293527446341505872366942805254);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2real_15(const fftw_real *real_input, const fftw_real *imag_input, fftw_real *output, int real_istride, int imag_istride, int ostride)
{
     fftw_real tmp3;
     fftw_real tmp30;
     fftw_real tmp18;
     fftw_real tmp37;
     fftw_real tmp61;
     fftw_real tmp62;
     fftw_real tmp45;
     fftw_real tmp40;
     fftw_real tmp23;
     fftw_real tmp31;
     fftw_real tmp42;
     fftw_real tmp28;
     fftw_real tmp32;
     fftw_real tmp8;
     fftw_real tmp13;
     fftw_real tmp14;
     ASSERT_ALIGNED_DOUBLE;
     {
	  fftw_real tmp17;
	  fftw_real tmp1;
	  fftw_real tmp2;
	  fftw_real tmp15;
	  fftw_real tmp16;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp16 = imag_input[5 * imag_istride];
	  tmp17 = K1_732050807 * tmp16;
	  tmp1 = real_input[0];
	  tmp2 = real_input[5 * real_istride];
	  tmp15 = tmp1 - tmp2;
	  tmp3 = tmp1 + (K2_000000000 * tmp2);
	  tmp30 = tmp15 - tmp17;
	  tmp18 = tmp15 + tmp17;
     }
     {
	  fftw_real tmp4;
	  fftw_real tmp39;
	  fftw_real tmp5;
	  fftw_real tmp6;
	  fftw_real tmp7;
	  fftw_real tmp22;
	  fftw_real tmp38;
	  fftw_real tmp9;
	  fftw_real tmp44;
	  fftw_real tmp10;
	  fftw_real tmp11;
	  fftw_real tmp12;
	  fftw_real tmp27;
	  fftw_real tmp43;
	  fftw_real tmp19;
	  fftw_real tmp24;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp20;
	       fftw_real tmp21;
	       fftw_real tmp25;
	       fftw_real tmp26;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp4 = real_input[3 * real_istride];
	       tmp39 = imag_input[3 * imag_istride];
	       tmp5 = real_input[7 * real_istride];
	       tmp6 = real_input[2 * real_istride];
	       tmp7 = tmp5 + tmp6;
	       tmp20 = imag_input[7 * imag_istride];
	       tmp21 = imag_input[2 * imag_istride];
	       tmp22 = K866025403 * (tmp20 - tmp21);
	       tmp38 = tmp20 + tmp21;
	       tmp9 = real_input[6 * real_istride];
	       tmp44 = imag_input[6 * imag_istride];
	       tmp10 = real_input[4 * real_istride];
	       tmp11 = real_input[real_istride];
	       tmp12 = tmp10 + tmp11;
	       tmp25 = imag_input[4 * imag_istride];
	       tmp26 = imag_input[imag_istride];
	       tmp27 = K866025403 * (tmp25 + tmp26);
	       tmp43 = tmp25 - tmp26;
	  }
	  tmp37 = K866025403 * (tmp5 - tmp6);
	  tmp61 = tmp39 - tmp38;
	  tmp62 = tmp44 - tmp43;
	  tmp45 = (K500000000 * tmp43) + tmp44;
	  tmp40 = (K500000000 * tmp38) + tmp39;
	  tmp19 = tmp4 - (K500000000 * tmp7);
	  tmp23 = tmp19 - tmp22;
	  tmp31 = tmp19 + tmp22;
	  tmp42 = K866025403 * (tmp10 - tmp11);
	  tmp24 = tmp9 - (K500000000 * tmp12);
	  tmp28 = tmp24 - tmp27;
	  tmp32 = tmp24 + tmp27;
	  tmp8 = tmp4 + tmp7;
	  tmp13 = tmp9 + tmp12;
	  tmp14 = tmp8 + tmp13;
     }
     output[0] = tmp3 + (K2_000000000 * tmp14);
     {
	  fftw_real tmp63;
	  fftw_real tmp65;
	  fftw_real tmp60;
	  fftw_real tmp64;
	  fftw_real tmp58;
	  fftw_real tmp59;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp63 = (K1_175570504 * tmp61) - (K1_902113032 * tmp62);
	  tmp65 = (K1_902113032 * tmp61) + (K1_175570504 * tmp62);
	  tmp58 = tmp3 - (K500000000 * tmp14);
	  tmp59 = K1_118033988 * (tmp8 - tmp13);
	  tmp60 = tmp58 - tmp59;
	  tmp64 = tmp59 + tmp58;
	  output[12 * ostride] = tmp60 - tmp63;
	  output[3 * ostride] = tmp60 + tmp63;
	  output[6 * ostride] = tmp64 - tmp65;
	  output[9 * ostride] = tmp64 + tmp65;
     }
     {
	  fftw_real tmp51;
	  fftw_real tmp29;
	  fftw_real tmp50;
	  fftw_real tmp55;
	  fftw_real tmp57;
	  fftw_real tmp53;
	  fftw_real tmp54;
	  fftw_real tmp56;
	  fftw_real tmp52;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp51 = K1_118033988 * (tmp23 - tmp28);
	  tmp29 = tmp23 + tmp28;
	  tmp50 = tmp18 - (K500000000 * tmp29);
	  tmp53 = tmp40 - tmp37;
	  tmp54 = tmp45 - tmp42;
	  tmp55 = (K1_175570504 * tmp53) - (K1_902113032 * tmp54);
	  tmp57 = (K1_902113032 * tmp53) + (K1_175570504 * tmp54);
	  output[5 * ostride] = tmp18 + (K2_000000000 * tmp29);
	  tmp56 = tmp51 + tmp50;
	  output[11 * ostride] = tmp56 - tmp57;
	  output[14 * ostride] = tmp56 + tmp57;
	  tmp52 = tmp50 - tmp51;
	  output[2 * ostride] = tmp52 - tmp55;
	  output[8 * ostride] = tmp52 + tmp55;
     }
     {
	  fftw_real tmp35;
	  fftw_real tmp33;
	  fftw_real tmp34;
	  fftw_real tmp47;
	  fftw_real tmp49;
	  fftw_real tmp41;
	  fftw_real tmp46;
	  fftw_real tmp48;
	  fftw_real tmp36;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp35 = K1_118033988 * (tmp31 - tmp32);
	  tmp33 = tmp31 + tmp32;
	  tmp34 = tmp30 - (K500000000 * tmp33);
	  tmp41 = tmp37 + tmp40;
	  tmp46 = tmp42 + tmp45;
	  tmp47 = (K1_175570504 * tmp41) - (K1_902113032 * tmp46);
	  tmp49 = (K1_902113032 * tmp41) + (K1_175570504 * tmp46);
	  output[10 * ostride] = tmp30 + (K2_000000000 * tmp33);
	  tmp48 = tmp35 + tmp34;
	  output[ostride] = tmp48 - tmp49;
	  output[4 * ostride] = tmp48 + tmp49;
	  tmp36 = tmp34 - tmp35;
	  output[7 * ostride] = tmp36 - tmp47;
	  output[13 * ostride] = tmp36 + tmp47;
     }
}

fftw_codelet_desc fftw_hc2real_15_desc =
{
     "fftw_hc2real_15",
     (void (*)()) fftw_hc2real_15,
     15,
     FFTW_BACKWARD,
     FFTW_HC2REAL,
     345,
     0,
     (const int *) 0,
};


#ifndef _RandomHistObject_H
#define _RandomHistObject_H

#include <gsl/gsl_histogram.h>
#include <QObject>
#include <QVector>
#include "src_config.h"

class RandomNumberGen;

class _HIST_EXPORT_ RandomHistObject : public QObject
{
public:
    static RandomHistObject * getRandomHistObject (QObject * parent=0);
    void GUIRandomProc (QWidget * parent=0, Qt::WindowFlags flags=0);
    void GUIHistogramParams (QWidget * parent=0, Qt::WindowFlags flags=0);
    void calcHist (void);
    void GUIViewHist (QWidget * parent=0, Qt::WindowFlags flags=0);

    enum DistrFuncs
    {
        Uniform=0,
        Gaussian=1,
        Exponential=2
    };

signals:
    void viewResults (QWidget * w);

private:
    //
    // Functions
    //
    RandomHistObject (QObject * parent=0);
    virtual ~RandomHistObject (void);

private:
    //
    // Variables
    //
    static RandomHistObject * self;
    RandomNumberGen * rGenerator;
    QVector<double> resNumb;
    gsl_histogram * hist;
private:
    Q_OBJECT
};

#endif


//
// ePDF
//

#pragma once

#include "ePDF/ndistributions.h"

#include <yaml-cpp/yaml.h>
#include <complex>
#include <vector>
#include <memory>
#include <gsl/gsl_integration.h>
#include <functional>

namespace ePDF
{
  /**
   * @brief The "evol_params" structure contains the evolution
   * parameters.
   */
  struct evol_params
  {
    double                          x;
    double                          Q;
    int                             id;
    std::shared_ptr<NDistributions> Ndist;
    evol_params operator = (evol_params const& p)
    {
      x     = p.x;
      Q     = p.Q;
      id    = p.id;
      Ndist = p.Ndist;
      return *this;
    }
  };

  /**
   * @brief The "xDistribution" class
   */
  class xDistributions
  {
  public:
    /**
     * @brief The "xDistribution" constructor.
     * @param config: the YAML:Node with the parameters
     */
    xDistributions(YAML::Node const& config);

    /**
     * @brief The "xDistribution" destructor.
     */
    ~xDistributions();

    /**
     * @brief Function that returns the PDFs in x space.
     * @param x: Bjorken x
     * @param Q: the final scale
     */
    std::vector<double> Evolve(double const& x, double const& Q);

    /**
     * @brief Interfaces to the integrand functions according to the
     * path chosen to perform the inverse Mellin transform.
     */
    std::vector<double> integrand(double const& y) const;
    std::vector<double> talbot(double const& y) const;
    std::vector<double> straight(double const& y) const;

    /**
     * @brief Integrators functions
     */
    std::vector<double> trapezoid(double const& a, double const& b) const;
    std::vector<double> gauss(double const& a, double const& b) const;
    std::vector<double> gaussGSL(double const& a, double const& b);

    /**
     * @brief Function that returns the N-th (complex) moment of PDFs.
     * @param N: moment
     * @param Q: the final scale
     */
    std::vector<std::complex<double>> Moments(std::complex<double> const& N, double const& Q) const;

    /**
     * @brief Function that sets the parameter structure externally
     * @param p: the input structure
     */
    void SetParameters(evol_params const& p) { _p = p; };

  private:
    std::string               const _contour;
    std::string               const _integrator;
    double                    const _eps;
    evol_params                     _p;
    gsl_integration_workspace      *_w;
  };
}


/***************************************************************************
Copyright (c) 2014, The OpenBLAS Project
All rights reserved.
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 OpenBLAS project 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 OPENBLAS PROJECT 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 "bench.h"

#include <cblas.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

// mlir-clang gemm.c -I .. -I /home/lchelini/scratch/polygeist/llvm-project/llvm/../clang/lib/Headers -o gemm --emit-llvm

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

  float *a, *b;
  float *c;
  float alpha = 1.0;
  float beta = 1.0;
  char transa = 'N';
  char transb = 'N';
  int m, n, k, i, j, lda, ldb, ldc;

  m = 500;
  k = 500;
  n = 500;

  a = (float *)malloc(sizeof(float) * m * k);
  b = (float *)malloc(sizeof(float) * k * n);
  c = (float *)malloc(sizeof(float) * m * n);

  for (i = 0; i < m * k; i++) {
    a[i] = ((float)rand() / (float)RAND_MAX) - 0.5;
  }
  for (i = 0; i < k * n; i++) {
    b[i] = ((float)rand() / (float)RAND_MAX) - 0.5;
  }
  for (i = 0; i < m * n; i++) {
    c[i] = ((float)rand() / (float)RAND_MAX) - 0.5;
  }

  if (transa == 'N') {
    lda = m;
  } else {
    lda = k;
  }
  if (transb == 'N') {
    ldb = k;
  } else {
    ldb = n;
  }
  ldc = m;

  sgemm_(&transa, &transb, &m, &n, &k, &alpha, &(a[5]), &lda, b, &ldb, &beta, c, &ldc);
  fprintf(stderr, "%.6f\n", c[100]);

  return 0;
}


/**
 *
 * @file core_slaswp.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated s Tue Jan  7 11:44:48 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

#define A(m, n) BLKADDR(descA, float, m, n)

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 *  CORE_slaswp performs a series of row interchanges on the matrix A.
 *  One row interchange is initiated for each of rows I1 through I2 of A.
 *
 *******************************************************************************
 *
 *  @param[in] N
 *          The number of columns in the matrix A. N >= 0.
 *
 *  @param[in,out] A
 *         On entry, the matrix of column dimension N to which the row
 *         interchanges will be applied.
 *         On exit, the permuted matrix.
 *
 *  @param[in] LDA
 *         The leading dimension of the array A.  LDA >= max(1,max(IPIV[I1..I2])).
 *
 *  @param[in] I1
 *          The first element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] I2
 *          The last element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] IPIV
 *          The pivot indices; Only the element in position i1 to i2
 *          are accessed. The pivot are offset by A.i.
 *
 *  @param[in] INC
 *          The increment between successive values of IPIV.  If IPIV
 *          is negative, the pivots are applied in reverse order.
 *
 *******************************************************************************
 */
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp = PCORE_slaswp
#define CORE_slaswp PCORE_slaswp
#endif
void CORE_slaswp(int N, float *A, int LDA, int I1, int I2, const int *IPIV, int INC)
{
    LAPACKE_slaswp_work( LAPACK_COL_MAJOR, N, A, LDA, I1, I2, IPIV, INC );
}

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 *  CORE_slaswp_ontile apply the slaswp function on a matrix stored in
 *  tile layout
 *
 *******************************************************************************
 *
 *  @param[in,out] descA
 *          The descriptor of the matrix A to permute.
 *
 *  @param[in] i1
 *          The first element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] i2
 *          The last element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] ipiv
 *          The pivot indices; Only the element in position i1 to i2
 *          are accessed. The pivot are offset by A.i.
 *
 *  @param[in] inc
 *          The increment between successive values of IPIV.  If IPIV
 *          is negative, the pivots are applied in reverse order.
 *
 *******************************************************************************
 *
 * @return
 *         \retval PLASMA_SUCCESS successful exit
 *         \retval <0 if INFO = -k, the k-th argument had an illegal value
 *
 *******************************************************************************
 */
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp_ontile = PCORE_slaswp_ontile
#define CORE_slaswp_ontile PCORE_slaswp_ontile
#endif
int CORE_slaswp_ontile(PLASMA_desc descA, int i1, int i2, const int *ipiv, int inc)
{
    int i, j, ip, it;
    float *A1;
    int lda1, lda2;

    /* Change i1 to C notation */
    i1--;

    /* Check parameters */
    if ( descA.nt > 1 ) {
        coreblas_error(1, "Illegal value of descA.nt");
        return -1;
    }
    if ( i1 < 0 ) {
        coreblas_error(2, "Illegal value of i1");
        return -2;
    }
    if ( (i2 <= i1) || (i2 > descA.m) ) {
        coreblas_error(3, "Illegal value of i2");
        return -3;
    }
    if ( ! ( (i2 - i1 - i1%descA.mb -1) < descA.mb ) ) {
        coreblas_error(2, "Illegal value of i1,i2. They have to be part of the same block.");
        return -3;
    }

    if (inc > 0) {
        it = i1 / descA.mb;
        A1 = A(it, 0);
        lda1 = BLKLDD(descA, 0);

        for (j = i1; j < i2; ++j, ipiv+=inc) {
            ip = (*ipiv) - descA.i - 1;
            if ( ip != j )
            {
                it = ip / descA.mb;
                i  = ip % descA.mb;
                lda2 = BLKLDD(descA, it);
                cblas_sswap(descA.n, A1       + j, lda1,
                                     A(it, 0) + i, lda2 );
            }
        }
    }
    else
    {
        it = (i2-1) / descA.mb;
        A1 = A(it, 0);
        lda1 = BLKLDD(descA, it);

        i1--;
        ipiv = &ipiv[(1-i2)*inc];
        for (j = i2-1; j > i1; --j, ipiv+=inc) {
            ip = (*ipiv) - descA.i - 1;
            if ( ip != j )
            {
                it = ip / descA.mb;
                i  = ip % descA.mb;
                lda2 = BLKLDD(descA, it);
                cblas_sswap(descA.n, A1       + j, lda1,
                                     A(it, 0) + i, lda2 );
            }
        }
    }

    return PLASMA_SUCCESS;
}

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 *  CORE_sswptr_ontile apply the slaswp function on a matrix stored in
 *  tile layout, followed by a strsm on the first tile of the panel.
 *
 *******************************************************************************
 *
 *  @param[in,out] descA
 *          The descriptor of the matrix A to permute.
 *
 *  @param[in] i1
 *          The first element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] i2
 *          The last element of IPIV for which a row interchange will
 *          be done.
 *
 *  @param[in] ipiv
 *          The pivot indices; Only the element in position i1 to i2
 *          are accessed. The pivot are offset by A.i.
 *
 *  @param[in] inc
 *          The increment between successive values of IPIV.  If IPIV
 *          is negative, the pivots are applied in reverse order.
 *
 *  @param[in] Akk
 *          The triangular matrix Akk. The leading descA.nb-by-descA.nb
 *          lower triangular part of the array Akk contains the lower triangular matrix, and the
 *          strictly upper triangular part of A is not referenced. The
 *          diagonal elements of A are also not referenced and are assumed to be 1.
 *
 * @param[in] ldak
 *          The leading dimension of the array Akk. ldak >= max(1,descA.nb).
 *
 *******************************************************************************
 *
 * @return
 *         \retval PLASMA_SUCCESS successful exit
 *         \retval <0 if INFO = -k, the k-th argument had an illegal value
 *
 *******************************************************************************
 */
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_sswptr_ontile = PCORE_sswptr_ontile
#define CORE_sswptr_ontile PCORE_sswptr_ontile
#endif
int CORE_sswptr_ontile(PLASMA_desc descA, int i1, int i2, const int *ipiv, int inc,
                       const float *Akk, int ldak)
{
    float zone  = 1.0;
    int lda;
    int m = descA.mt == 1 ? descA.m : descA.mb;

    if ( descA.nt > 1 ) {
        coreblas_error(1, "Illegal value of descA.nt");
        return -1;
    }
    if ( i1 < 1 ) {
        coreblas_error(2, "Illegal value of i1");
        return -2;
    }
    if ( (i2 < i1) || (i2 > m) ) {
        coreblas_error(3, "Illegal value of i2");
        return -3;
    }

    CORE_slaswp_ontile(descA, i1, i2, ipiv, inc);

    lda = BLKLDD(descA, 0);
    cblas_strsm( CblasColMajor, CblasLeft, CblasLower,
                 CblasNoTrans, CblasUnit,
                 m, descA.n, (zone),
                 Akk,     ldak,
                 A(0, 0), lda );

    return PLASMA_SUCCESS;
}

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 *  CORE_slaswpc_ontile apply the slaswp function on a matrix stored in
 *  tile layout
 *
 *******************************************************************************
 *
 *  @param[in,out] descA
 *          The descriptor of the matrix A to permute.
 *
 *  @param[in] i1
 *          The first element of IPIV for which a column interchange will
 *          be done.
 *
 *  @param[in] i2
 *          The last element of IPIV for which a column interchange will
 *          be done.
 *
 *  @param[in] ipiv
 *          The pivot indices; Only the element in position i1 to i2
 *          are accessed. The pivot are offset by A.i.
 *
 *  @param[in] inc
 *          The increment between successive values of IPIV.  If IPIV
 *          is negative, the pivots are applied in reverse order.
 *
 *******************************************************************************
 *
 * @return
 *         \retval PLASMA_SUCCESS successful exit
 *         \retval <0 if INFO = -k, the k-th argument had an illegal value
 *
 *******************************************************************************
 */
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswpc_ontile = PCORE_slaswpc_ontile
#define CORE_slaswpc_ontile PCORE_slaswpc_ontile
#endif
int CORE_slaswpc_ontile(PLASMA_desc descA, int i1, int i2, const int *ipiv, int inc)
{
    int i, j, ip, it;
    float *A1;
    int lda;

    /* Change i1 to C notation */
    i1--;

    /* Check parameters */
    if ( descA.mt > 1 ) {
        coreblas_error(1, "Illegal value of descA.mt");
        return -1;
    }
    if ( i1 < 0 ) {
        coreblas_error(2, "Illegal value of i1");
        return -2;
    }
    if ( (i2 <= i1) || (i2 > descA.n) ) {
        coreblas_error(3, "Illegal value of i2");
        return -3;
    }
    if ( ! ( (i2 - i1 - i1%descA.nb -1) < descA.nb ) ) {
        coreblas_error(2, "Illegal value of i1,i2. They have to be part of the same block.");
        return -3;
    }

    lda = BLKLDD(descA, 0);

    if (inc > 0) {
        it = i1 / descA.nb;
        A1 = A(0, it);

        for (j = i1-1; j < i2; ++j, ipiv+=inc) {
            ip = (*ipiv) - descA.j - 1;
            if ( ip != j )
            {
                it = ip / descA.nb;
                i  = ip % descA.nb;
                cblas_sswap(descA.m, A1       + j*lda, 1,
                                     A(0, it) + i*lda, 1 );
            }
        }
    }
    else
    {
        it = (i2-1) / descA.mb;
        A1 = A(0, it);

        i1--;
        ipiv = &ipiv[(1-i2)*inc];
        for (j = i2-1; j > i1; --j, ipiv+=inc) {
            ip = (*ipiv) - descA.j - 1;
            if ( ip != j )
            {
                it = ip / descA.nb;
                i  = ip % descA.nb;
                cblas_sswap(descA.m, A1       + j*lda, 1,
                                     A(0, it) + i*lda, 1 );
            }
        }
    }

    return PLASMA_SUCCESS;
}


/**
 *
 * @file testing_sgels.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Bilel Hadri
 * @author Hatem Ltaief
 * @date 2010-11-15
 * @generated s Tue Jan  7 11:45:18 2014
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>
#include "testing_smain.h"

#undef COMPLEX
#define REAL

enum blas_order_type {
            blas_rowmajor = 101,
            blas_colmajor = 102 };

enum blas_uplo_type  {
            blas_upper = 121,
            blas_lower = 122 };

enum blas_cmach_type {
            blas_base      = 151,
            blas_t         = 152,
            blas_rnd       = 153,
            blas_ieee      = 154,
            blas_emin      = 155,
            blas_emax      = 156,
            blas_eps       = 157,
            blas_prec      = 158,
            blas_underflow = 159,
            blas_overflow  = 160,
            blas_sfmin     = 161};

enum blas_norm_type {
            blas_one_norm       = 171,
            blas_real_one_norm  = 172,
            blas_two_norm       = 173,
            blas_frobenius_norm = 174,
            blas_inf_norm       = 175,
            blas_real_inf_norm  = 176,
            blas_max_norm       = 177,
            blas_real_max_norm  = 178 };

static void
BLAS_error(char *rname, int err, int val, int x) {
  fprintf( stderr, "%s %d %d %d\n", rname, err, val, x );
  abort();
}

static
void
BLAS_ssy_norm(enum blas_order_type order, enum blas_norm_type norm,
  enum blas_uplo_type uplo, int n, const float *a, int lda, float *res) {
  int i, j; float anorm, v;
  char rname[] = "BLAS_ssy_norm";

  if (order != blas_colmajor) BLAS_error( rname, -1, order, 0 );

  if (norm == blas_inf_norm) {
    anorm = 0.0;
    if (blas_upper == uplo) {
      for (i = 0; i < n; ++i) {
        v = 0.0;
        for (j = 0; j < i; ++j) {
          v += fabsf( a[j + i * lda] );
        }
        for (j = i; j < n; ++j) {
          v += fabsf( a[i + j * lda] );
        }
        if (v > anorm)
          anorm = v;
      }
    } else {
      BLAS_error( rname, -3, norm, 0 );
      return;
    }
  } else {
    BLAS_error( rname, -2, norm, 0 );
    return;
  }

  if (res) *res = anorm;
}

static
void
BLAS_sge_norm(enum blas_order_type order, enum blas_norm_type norm,
  int m, int n, const float *a, int lda, float *res) {
  int i, j; float anorm, v;
  char rname[] = "BLAS_sge_norm";

  if (order != blas_colmajor) BLAS_error( rname, -1, order, 0 );

  if (norm == blas_frobenius_norm) {
    anorm = 0.0f;
    for (j = n; j; --j) {
      for (i = m; i; --i) {
        v = a[0];
        anorm += v * v;
        a++;
      }
      a += lda - m;
    }
    anorm = sqrt( anorm );
  } else if (norm == blas_inf_norm) {
    anorm = 0.0f;
    for (i = 0; i < m; ++i) {
      v = 0.0f;
      for (j = 0; j < n; ++j) {
        v += fabsf( a[i + j * lda] );
      }
      if (v > anorm)
        anorm = v;
    }
  } else {
    BLAS_error( rname, -2, norm, 0 );
    return;
  }

  if (res) *res = anorm;
}

static
float
BLAS_spow_di(float x, int n) {
  float rv = 1.0;

  if (n < 0) {
    n = -n;
    x = 1.0 / x;
  }

  for (; n; n >>= 1, x *= x) {
    if (n & 1)
      rv *= x;
  }

  return rv;
}

static
float
BLAS_sfpinfo(enum blas_cmach_type cmach) {
  float eps = 1.0, r = 1.0, o = 1.0, b = 2.0;
  int t = 53, l = 1024, m = -1021;
  char rname[] = "BLAS_sfpinfo";

  if ((sizeof eps) == sizeof(float)) {
    t = 24;
    l = 128;
    m = -125;
  } else {
    t = 53;
    l = 1024;
    m = -1021;
  }

  /* for (i = 0; i < t; ++i) eps *= half; */
  eps = BLAS_spow_di( b, -t );
  /* for (i = 0; i >= m; --i) r *= half; */
  r = BLAS_spow_di( b, m-1 );

  o -= eps;
  /* for (i = 0; i < l; ++i) o *= b; */
  o = (o * BLAS_spow_di( b, l-1 )) * b;

  switch (cmach) {
    case blas_eps: return eps;
    case blas_sfmin: return r;
    default:
      BLAS_error( rname, -1, cmach, 0 );
      break;
  }
  return 0.0;
}

static int check_orthogonality(int, int, int, float*, float);
static int check_factorization(int, int, float*, float*, int, float*, float);
static int check_solution(int, int, int, float*, int, float*, float*, int, float);

int testing_sgels(int argc, char **argv)
{
    int mode = 0;

    if ( argc < 1 ){
        goto usage;
    } else {
        mode = atoi(argv[0]);
    }

    /* Check for number of arguments*/
    if ( ((mode == 0) && (argc != 6)) ||
         ((mode != 0) && (argc != 7)) ){
      usage:
        USAGE("GELS", "MODE M N LDA NRHS LDB [RH]",
              "   - MODE : 0: flat, 1: tree (RH needed)\n"
              "   - M    : number of rows of the matrix A\n"
              "   - N    : number of columns of the matrix A\n"
              "   - LDA  : leading dimension of the matrix A\n"
              "   - NRHS : number of RHS\n"
              "   - LDB  : leading dimension of the matrix B\n"
              "   - RH   : Size of each subdomains\n");
        return -1;
    }

    int M     = atoi(argv[1]);
    int N     = atoi(argv[2]);
    int LDA   = atoi(argv[3]);
    int NRHS  = atoi(argv[4]);
    int LDB   = atoi(argv[5]);
    int rh;

    int K = min(M, N);
    float eps;
    int info_ortho, info_solution, info_factorization;
    int i,j;
    int LDAxN = LDA*N;
    int LDBxNRHS = LDB*NRHS;

    float *A1 = (float *)malloc(LDA*N*sizeof(float));
    float *A2 = (float *)malloc(LDA*N*sizeof(float));
    float *B1 = (float *)malloc(LDB*NRHS*sizeof(float));
    float *B2 = (float *)malloc(LDB*NRHS*sizeof(float));
    float *Q  = (float *)malloc(LDA*N*sizeof(float));
    PLASMA_desc *T;

    /* Check if unable to allocate memory */
    if ((!A1)||(!A2)||(!B1)||(!B2)||(!Q)){
        printf("Out of Memory \n ");
        return -2;
    }

    if ( mode ) {
        rh = atoi(argv[6]);

        PLASMA_Set(PLASMA_HOUSEHOLDER_MODE, PLASMA_TREE_HOUSEHOLDER);
        PLASMA_Set(PLASMA_HOUSEHOLDER_SIZE, rh);
    }

    PLASMA_Alloc_Workspace_sgels(M, N, &T);
    eps = BLAS_sfpinfo( blas_eps );

    /*----------------------------------------------------------
    *  TESTING SGELS
    */

    /* Initialize A1 and A2 */
    LAPACKE_slarnv_work(IONE, ISEED, LDAxN, A1);
    for (i = 0; i < M; i++)
        for (j = 0; j < N; j++)
            A2[LDA*j+i] = A1[LDA*j+i] ;

    /* Initialize B1 and B2 */
    LAPACKE_slarnv_work(IONE, ISEED, LDBxNRHS, B1);
    for (i = 0; i < M; i++)
        for (j = 0; j < NRHS; j++)
            B2[LDB*j+i] = B1[LDB*j+i] ;

    memset((void*)Q, 0, LDA*N*sizeof(float));
    for (i = 0; i < K; i++)
        Q[LDA*i+i] = 1.0;

    /* PLASMA SGELS */
    PLASMA_sgels(PlasmaNoTrans, M, N, NRHS, A2, LDA, T, B2, LDB);

    /* PLASMA SGELS */
    if (M >= N)
       /* Building the economy-size Q */
       PLASMA_sorgqr(M, N, K, A2, LDA, T, Q, LDA);
    else
       /* Building the economy-size Q */
       PLASMA_sorglq(M, N, K, A2, LDA, T, Q, LDA);

    printf("\n");
    printf("------ TESTS FOR PLASMA SGELS ROUTINE -------  \n");
    printf("            Size of the Matrix %d by %d\n", M, N);
    printf("\n");
    printf(" The matrix A is randomly generated for each test.\n");
    printf("============\n");
    printf(" The relative machine precision (eps) is to be %e \n",eps);
    printf(" Computational tests pass if scaled residuals are less than 60.\n");

    /* Check the orthogonality, factorization and the solution */
    info_ortho = check_orthogonality(M, N, LDA, Q, eps);
    info_factorization = check_factorization(M, N, A1, A2, LDA, Q, eps);
    info_solution = check_solution(M, N, NRHS, A1, LDA, B1, B2, LDB, eps);

    if ((info_solution == 0)&(info_factorization == 0)&(info_ortho == 0)) {
        printf("***************************************************\n");
        printf(" ---- TESTING SGELS ...................... PASSED !\n");
        printf("***************************************************\n");
    }
    else {
        printf("************************************************\n");
        printf(" - TESTING SGELS ... FAILED !\n");
        printf("************************************************\n");
    }

    /*-------------------------------------------------------------
    *  TESTING SGEQRF + SGEQRS or SGELQF + SGELQS
    */

    /* Initialize A1 and A2 */
    LAPACKE_slarnv_work(IONE, ISEED, LDAxN, A1);
    for (i = 0; i < M; i++)
        for (j = 0; j < N; j++)
            A2[LDA*j+i] = A1[LDA*j+i];

    /* Initialize B1 and B2 */
    LAPACKE_slarnv_work(IONE, ISEED, LDBxNRHS, B1);
    for (i = 0; i < M; i++)
        for (j = 0; j < NRHS; j++)
             B2[LDB*j+i] = B1[LDB*j+i];

    memset((void*)Q, 0, LDA*N*sizeof(float));
    for (i = 0; i < K; i++)
        Q[LDA*i+i] = 1.0;

    if (M >= N) {
        printf("\n");
        printf("------ TESTS FOR PLASMA SGEQRF + SGEQRS ROUTINE -------  \n");
        printf("            Size of the Matrix %d by %d\n", M, N);
        printf("\n");
        printf(" The matrix A is randomly generated for each test.\n");
        printf("============\n");
        printf(" The relative machine precision (eps) is to be %e \n", eps);
        printf(" Computational tests pass if scaled residuals are less than 60.\n");

        /* Plasma routines */
        PLASMA_sgeqrf(M, N, A2, LDA, T);
        PLASMA_sorgqr(M, N, K, A2, LDA, T, Q, LDA);
        PLASMA_sgeqrs(M, N, NRHS, A2, LDA, T, B2, LDB);

        /* Check the orthogonality, factorization and the solution */
        info_ortho = check_orthogonality(M, N, LDA, Q, eps);
        info_factorization = check_factorization(M, N, A1, A2, LDA, Q, eps);
        info_solution = check_solution(M, N, NRHS, A1, LDA, B1, B2, LDB, eps);

        if ((info_solution == 0)&(info_factorization == 0)&(info_ortho == 0)) {
            printf("***************************************************\n");
            printf(" ---- TESTING SGEQRF + SGEQRS ............ PASSED !\n");
            printf("***************************************************\n");
        }
        else{
            printf("***************************************************\n");
            printf(" - TESTING SGEQRF + SGEQRS ... FAILED !\n");
            printf("***************************************************\n");
        }
    }
    else  {
        printf("\n");
        printf("------ TESTS FOR PLASMA SGELQF + SGELQS ROUTINE -------  \n");
        printf("            Size of the Matrix %d by %d\n", M, N);
        printf("\n");
        printf(" The matrix A is randomly generated for each test.\n");
        printf("============\n");
        printf(" The relative machine precision (eps) is to be %e \n", eps);
        printf(" Computational tests pass if scaled residuals are less than 60.\n");

        /* Plasma routines */
        PLASMA_sgelqf(M, N, A2, LDA, T);
        PLASMA_sorglq(M, N, K, A2, LDA, T, Q, LDA);
        PLASMA_sgelqs(M, N, NRHS, A2, LDA, T, B2, LDB);

       /* Check the orthogonality, factorization and the solution */
       info_ortho = check_orthogonality(M, N, LDA, Q, eps);
       info_factorization = check_factorization(M, N, A1, A2, LDA, Q, eps);
       info_solution = check_solution(M, N, NRHS, A1, LDA, B1, B2, LDB, eps);

       if ( (info_solution == 0) & (info_factorization == 0) & (info_ortho == 0) ) {
          printf("***************************************************\n");
          printf(" ---- TESTING SGELQF + SGELQS ............ PASSED !\n");
          printf("***************************************************\n");
       }
       else {
          printf("***************************************************\n");
          printf(" - TESTING SGELQF + SGELQS ... FAILED !\n");
          printf("***************************************************\n");
        }
    }

    /*----------------------------------------------------------
    *  TESTING SGEQRF + ZORMQR + STRSM
    */

    /* Initialize A1 and A2 */
    LAPACKE_slarnv_work(IONE, ISEED, LDAxN, A1);
    for (i = 0; i < M; i++)
        for (j = 0; j < N; j++)
            A2[LDA*j+i] = A1[LDA*j+i];

    /* Initialize B1 and B2 */
    memset(B2, 0, LDB*NRHS*sizeof(float));
    LAPACKE_slarnv_work(IONE, ISEED, LDBxNRHS, B1);
    for (i = 0; i < M; i++)
        for (j = 0; j < NRHS; j++)
            B2[LDB*j+i] = B1[LDB*j+i];

    /* PLASMA SGEQRF+ SORMQR + STRSM */
    memset((void*)Q, 0, LDA*N*sizeof(float));
    for (i = 0; i < K; i++)
        Q[LDA*i+i] = 1.0;

    if (M >= N) {
        printf("\n");
        printf("------ TESTS FOR PLASMA SGEQRF + SORMQR + STRSM  ROUTINE -------  \n");
        printf("            Size of the Matrix %d by %d\n", M, N);
        printf("\n");
        printf(" The matrix A is randomly generated for each test.\n");
        printf("============\n");
        printf(" The relative machine precision (eps) is to be %e \n",eps);
        printf(" Computational tests pass if scaled residuals are less than 60.\n");

        PLASMA_sgeqrf(M, N, A2, LDA, T);
        PLASMA_sorgqr(M, N, K, A2, LDA, T, Q, LDA);
        PLASMA_sormqr(PlasmaLeft, PlasmaTrans, M, NRHS, N, A2, LDA, T, B2, LDB);
        PLASMA_strsm(PlasmaLeft, PlasmaUpper, PlasmaNoTrans, PlasmaNonUnit, N, NRHS, 1.0, A2, LDA, B2, LDB);
    }
    else {
        printf("\n");
        printf("------ TESTS FOR PLASMA SGELQF + SORMLQ + STRSM  ROUTINE -------  \n");
        printf("            Size of the Matrix %d by %d\n", M, N);
        printf("\n");
        printf(" The matrix A is randomly generated for each test.\n");
        printf("============\n");
        printf(" The relative machine precision (eps) is to be %e \n",eps);
        printf(" Computational tests pass if scaled residuals are less than 60.\n");

        PLASMA_sgelqf(M, N, A2, LDA, T);
        PLASMA_strsm(PlasmaLeft, PlasmaLower, PlasmaNoTrans, PlasmaNonUnit, M, NRHS, 1.0, A2, LDA, B2, LDB);
        PLASMA_sorglq(M, N, K, A2, LDA, T, Q, LDA);
        PLASMA_sormlq(PlasmaLeft, PlasmaTrans, N, NRHS, M, A2, LDA, T, B2, LDB);
    }

    /* Check the orthogonality, factorization and the solution */
    info_ortho = check_orthogonality(M, N, LDA, Q, eps);
    info_factorization = check_factorization(M, N, A1, A2, LDA, Q, eps);
    info_solution = check_solution(M, N, NRHS, A1, LDA, B1, B2, LDB, eps);

    if ( (info_solution == 0) & (info_factorization == 0) & (info_ortho == 0) ) {
        if (M >= N) {
            printf("***************************************************\n");
            printf(" ---- TESTING SGEQRF + SORMQR + STRSM .... PASSED !\n");
            printf("***************************************************\n");
        }
        else {
            printf("***************************************************\n");
            printf(" ---- TESTING SGELQF + STRSM + SORMLQ .... PASSED !\n");
            printf("***************************************************\n");
        }
    }
    else {
        if (M >= N) {
            printf("***************************************************\n");
            printf(" - TESTING SGEQRF + SORMQR + STRSM ... FAILED !\n");
            printf("***************************************************\n");
        }
        else {
            printf("***************************************************\n");
            printf(" - TESTING SGELQF + STRSM + SORMLQ ... FAILED !\n");
            printf("***************************************************\n");
        }
    }

    free(A1); free(A2); free(B1); free(B2); free(Q);
    PLASMA_Dealloc_Handle_Tile( &T );

    return 0;
}

/*-------------------------------------------------------------------
 * Check the orthogonality of Q
 */

static int check_orthogonality(int M, int N, int LDQ, float *Q, float eps)
{
    float alpha, beta;
    float normQ;
    int info_ortho;
    int i;
    int minMN = min(M, N);

    float *work = (float *)malloc(minMN*sizeof(float));

    alpha = 1.0;
    beta  = -1.0;

    /* Build the idendity matrix USE DLASET?*/
    float *Id = (float *) malloc(minMN*minMN*sizeof(float));
    memset((void*)Id, 0, minMN*minMN*sizeof(float));
    for (i = 0; i < minMN; i++)
        Id[i*minMN+i] = (float)1.0;

    /* Perform Id - Q'Q */
    if (M >= N)
        cblas_ssyrk(CblasColMajor, CblasUpper, CblasTrans, N, M, alpha, Q, LDQ, beta, Id, N);
    else
        cblas_ssyrk(CblasColMajor, CblasUpper, CblasNoTrans, M, N, alpha, Q, LDQ, beta, Id, M);

    BLAS_ssy_norm( blas_colmajor, blas_inf_norm, blas_upper, minMN, Id, minMN, &normQ );

    printf("============\n");
    printf("Checking the orthogonality of Q \n");
    printf("||Id-Q'*Q||_oo / (N*eps) = %e \n", normQ/(minMN*eps));

    if ( isnan(normQ / (minMN * eps)) || isinf(normQ / (minMN * eps)) || (normQ / (minMN * eps) > 60.0) ) {
        printf("-- Orthogonality is suspicious ! \n");
        info_ortho=1;
    }
    else {
        printf("-- Orthogonality is CORRECT ! \n");
        info_ortho=0;
    }

    free(work); free(Id);

    return info_ortho;
}

/*------------------------------------------------------------
 *  Check the factorization QR
 */

static int check_factorization(int M, int N, float *A1, float *A2, int LDA, float *Q, float eps )
{
    float Anorm, Rnorm;
    float alpha, beta;
    int info_factorization;
    int i,j;

    float *Ql       = (float *)malloc(M*N*sizeof(float));
    float *Residual = (float *)malloc(M*N*sizeof(float));
    float *work              = (float *)malloc(max(M,N)*sizeof(float));

    alpha=1.0;
    beta=0.0;

    if (M >= N) {
        /* Extract the R */
        float *R = (float *)malloc(N*N*sizeof(float));
        memset((void*)R, 0, N*N*sizeof(float));
        LAPACKE_slacpy_work(LAPACK_COL_MAJOR,'u', M, N, A2, LDA, R, N);

        /* Perform Ql=Q*R */
        memset((void*)Ql, 0, M*N*sizeof(float));
        cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, M, N, N, (alpha), Q, LDA, R, N, (beta), Ql, M);
        free(R);
    }
    else {
        /* Extract the L */
        float *L = (float *)malloc(M*M*sizeof(float));
        memset((void*)L, 0, M*M*sizeof(float));
        LAPACKE_slacpy_work(LAPACK_COL_MAJOR,'l', M, N, A2, LDA, L, M);

    /* Perform Ql=LQ */
        memset((void*)Ql, 0, M*N*sizeof(float));
        cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, M, N, M, (alpha), L, M, Q, LDA, (beta), Ql, M);
        free(L);
    }

    /* Compute the Residual */
    for (i = 0; i < M; i++)
        for (j = 0 ; j < N; j++)
            Residual[j*M+i] = A1[j*LDA+i]-Ql[j*M+i];

    BLAS_sge_norm( blas_colmajor, blas_inf_norm, M, N, Residual, M, &Rnorm );
    BLAS_sge_norm( blas_colmajor, blas_inf_norm, M, N, A2, LDA, &Anorm );

    if (M >= N) {
        printf("============\n");
        printf("Checking the QR Factorization \n");
        printf("-- ||A-QR||_oo/(||A||_oo.N.eps) = %e \n",Rnorm/(Anorm*N*eps));
    }
    else {
        printf("============\n");
        printf("Checking the LQ Factorization \n");
        printf("-- ||A-LQ||_oo/(||A||_oo.N.eps) = %e \n",Rnorm/(Anorm*N*eps));
    }

    if (isnan(Rnorm / (Anorm * N *eps)) || isinf(Rnorm / (Anorm * N *eps)) || (Rnorm / (Anorm * N * eps) > 60.0) ) {
        printf("-- Factorization is suspicious ! \n");
        info_factorization = 1;
    }
    else {
        printf("-- Factorization is CORRECT ! \n");
        info_factorization = 0;
    }

    free(work); free(Ql); free(Residual);

    return info_factorization;
}

/*--------------------------------------------------------------
 * Check the solution
 */

static int check_solution(int M, int N, int NRHS, float *A, int LDA, float *B, float *X, int LDB, float eps)
{
    int info_solution;
    float Rnorm, Anorm, Xnorm, Bnorm;
    float zone, mzone, zzero;
    float result;
    float *work = (float *)malloc(max(M, N)* sizeof(float));

    zone  =  1.0;
    mzone = -1.0;
    zzero =  0.0;

    BLAS_sge_norm( blas_colmajor, blas_inf_norm, M, N,    A, LDA, &Anorm );
    BLAS_sge_norm( blas_colmajor, blas_inf_norm, M, NRHS, B, LDB, &Bnorm );
    BLAS_sge_norm( blas_colmajor, blas_inf_norm, N, NRHS, X, LDB, &Xnorm );

    /* Compute Ax - b */
    cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, M, NRHS, N, (zone), A, LDA, X, LDB, (mzone), B, LDB);

    /* Compute A' * (Ax - b) */
    cblas_sgemm(CblasColMajor, CblasTrans, CblasNoTrans, N, NRHS, M, (zone), A, LDA, B, LDB, (zzero), X, LDB);
    BLAS_sge_norm( blas_colmajor, blas_inf_norm, N, NRHS, X, LDB, &Rnorm );

    if (getenv("PLASMA_TESTING_VERBOSE"))
       printf( "||A||_oo=%f\n||X||_oo=%f\n||B||_oo=%f\n||A X - B||_oo=%e\n", Anorm, Xnorm, Bnorm, Rnorm );

    result = Rnorm / ( (Anorm*Xnorm+Bnorm)*N*eps ) ;
    printf("============\n");
    printf("Checking the Residual of the solution \n");
    printf("-- ||Ax-B||_oo/((||A||_oo||x||_oo+||B||_oo).N.eps) = %e \n", result);

    if (  isnan(Xnorm) || isinf(Xnorm) || isnan(result) || isinf(result) || (result > 60.0) ) {
        printf("-- The solution is suspicious ! \n");
        info_solution = 1;
     }
    else{
        printf("-- The solution is CORRECT ! \n");
        info_solution = 0;
    }
    free(work);
    return info_solution;
}


#include <string.h>
#include <mpi.h>
#include <math.h>
#include <alloca.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_integration.h>

#include <fastpm/libfastpm.h>
#include <fastpm/logging.h>
#include <fastpm/string.h>

#include "pmpfft.h"

void
fastpm_powerspectrum_init(FastPMPowerSpectrum * ps, const size_t size)
{
    fastpm_funck_init(&ps->base, size);
    ps->pm = NULL;
    ps->edges = malloc(sizeof(ps->edges[0]) * (ps->base.size + 1));
    ps->Nmodes = malloc(sizeof(ps->Nmodes[0]) * ps->base.size);
}

void
fastpm_powerspectrum_init_from(FastPMPowerSpectrum * ps, const FastPMPowerSpectrum * other)
{
    fastpm_powerspectrum_init(ps, other->base.size);
    memcpy(ps->base.k, other->base.k, sizeof(ps->base.k[0]) * ps->base.size);
    memcpy(ps->base.f, other->base.f, sizeof(ps->base.f[0]) * ps->base.size);
    memcpy(ps->edges, other->edges, sizeof(ps->edges[0]) * (ps->base.size + 1));
    memcpy(ps->Nmodes, other->Nmodes, sizeof(ps->Nmodes[0]) * ps->base.size);
}

void
fastpm_powerspectrum_init_from_delta(FastPMPowerSpectrum * ps, PM * pm, const FastPMFloat * delta1_k, const FastPMFloat * delta2_k)
{
    /* This function measures powerspectrum from two overdensity or 1+overdensity fields */
    /* normalize them with fastpm_apply_normalize_transfer if needed before using this function.*/
    /* N is used to store metadata -- the shot-noise level. */
    fastpm_powerspectrum_init(ps, pm_nmesh(pm)[0] / 2);

    ps->pm = pm;
    double Volume = 1.0;
    int d;
    double k0 = 2 * M_PI / pm_boxsize(ps->pm)[0];
    for (d = 0; d < 3; d ++) {
        Volume *= pm_boxsize(pm)[d];
    }
    ps->Volume = Volume;
    ps->k0 = k0;

    memset(ps->base.f, 0, sizeof(ps->base.f[0]) * ps->base.size);
    memset(ps->base.k, 0, sizeof(ps->base.k[0]) * ps->base.size);
    memset(ps->edges, 0, sizeof(ps->edges[0]) * (ps->base.size + 1));
    memset(ps->Nmodes, 0, sizeof(ps->Nmodes[0]) * ps->base.size);

    int i;
    for(i = 0; i < ps->base.size + 1; i ++) {
        ps->edges[i] = i * k0;
    }

#pragma omp parallel
    {
        PMKIter kiter;
        for(pm_kiter_init(ps->pm, &kiter);
            !pm_kiter_stop(&kiter);
            pm_kiter_next(&kiter)) {
            int d;
            ptrdiff_t kk = 0.;
            for(d = 0; d < 3; d++) {
                double ik = kiter.iabs[d];
                if(ik > pm->Nmesh[d] / 2) ik -= pm->Nmesh[d];
                kk += ik * ik;
            }

            ptrdiff_t ind = kiter.ind;

            ptrdiff_t bin = ((ptrdiff_t)floor(sqrt(kk))) - 2;
            if(bin < 0) bin = 0;
            while((bin + 1) * (bin + 1) <= kk) {
                bin ++;
            }

            double k = sqrt(kk) * k0;

            if(bin >= 0 && bin < ps->base.size) {
                double real1 = delta1_k[ind + 0];
                double imag1 = delta1_k[ind + 1];
                double real2 = delta2_k[ind + 0];
                double imag2 = delta2_k[ind + 1];
                double value = real1 * real2 + imag1 * imag2;
                int w = 2;

                if(kiter.i[2] == 0 || kiter.i[2] == pm->Nmesh[2] / 2) w = 1;

                if(kiter.iabs[0] == 0 &&
                   kiter.iabs[1] == 0 &&
                   kiter.iabs[2] == 0) {

                } else {
                    #pragma omp atomic
                    ps->Nmodes[bin] += w;
                    #pragma omp atomic
                    ps->base.f[bin] += w * value; /// cic;
                    #pragma omp atomic
                    ps->base.k[bin] += w * k;
                }
            }
        }
    }

    MPI_Allreduce(MPI_IN_PLACE, ps->base.f, ps->base.size, MPI_DOUBLE, MPI_SUM, ps->pm->Comm2D);
    MPI_Allreduce(MPI_IN_PLACE, ps->Nmodes, ps->base.size, MPI_DOUBLE, MPI_SUM, ps->pm->Comm2D);
    MPI_Allreduce(MPI_IN_PLACE, ps->base.k, ps->base.size, MPI_DOUBLE, MPI_SUM, ps->pm->Comm2D);

    ptrdiff_t ind;
    for(ind = 0; ind < ps->base.size; ind++) {
        if(ps->Nmodes[ind] == 0) continue;
        ps->base.k[ind] /= ps->Nmodes[ind];
        ps->base.f[ind] /= ps->Nmodes[ind];
        ps->base.f[ind] *= ps->Volume;
    }
}

void
fastpm_transferfunction_init(FastPMPowerSpectrum * ps, PM * pm, FastPMFloat * src_k, FastPMFloat * dest_k)
{
    FastPMPowerSpectrum * ps2 = alloca(sizeof(*ps2));

    fastpm_powerspectrum_init_from_delta(ps, pm, src_k, src_k);
    fastpm_powerspectrum_init_from_delta(ps2, pm, dest_k, dest_k);

    ptrdiff_t i;
    for(i = 0; i < ps->base.size; i ++) {
        ps->base.f[i] = sqrt(ps2->base.f[i] / ps->base.f[i]);
    }

    fastpm_powerspectrum_destroy(ps2);
}

void
fastpm_powerspectrum_destroy(FastPMPowerSpectrum * ps) {
    free(ps->edges);
    free(ps->Nmodes);
    fastpm_funck_destroy(&ps->base);
}

void
fastpm_powerspectrum_write(FastPMPowerSpectrum * ps, char * filename, double N)
{
    FILE * fp = fopen(filename, "w");
    int i;
    fprintf(fp, "# k p N \n");
    for(i = 0; i < ps->base.size; i ++) {
        fprintf(fp, "%g %g %g\n", ps->base.k[i], ps->base.f[i], ps->Nmodes[i]);
    }
    double * BoxSize = pm_boxsize(ps->pm);
    fprintf(fp, "# metadata 7\n");
    fprintf(fp, "# volume %g float64\n", ps->Volume);
    fprintf(fp, "# shotnoise %g float64\n", ps->Volume / N);
    fprintf(fp, "# N1 %g int\n", N);
    fprintf(fp, "# N2 %g int\n", N);
    fprintf(fp, "# Lz %g float64\n", BoxSize[2]);
    fprintf(fp, "# Lx %g float64\n", BoxSize[0]);
    fprintf(fp, "# Ly %g float64\n", BoxSize[1]);
    fclose(fp);
}

double
fastpm_powerspectrum_large_scale(FastPMPowerSpectrum * ps, int Nmax)
{
    double kmax = Nmax * ps->k0;
    ptrdiff_t i;
    double Plin = 0;
    double Nmodes = 0;
    /* ignore zero mode ! */
    for(i = 0; (i == 0) || (i < ps->base.size && ps->base.k[i] <= kmax); i ++) {
        Plin += ps->base.f[i] * ps->Nmodes[i];
        Nmodes += ps->Nmodes[i];
    }
    Plin /= Nmodes;
    return Plin;
}

/* inverted calling signature for callbacks */
double
fastpm_powerspectrum_eval2(double k, FastPMPowerSpectrum * ps)
{
    return fastpm_powerspectrum_eval(ps, k);
}

double
fastpm_powerspectrum_eval(FastPMPowerSpectrum * ps, double k)
{
    return fastpm_funck_eval(&ps->base, k);
}

double
fastpm_powerspectrum_get(FastPMPowerSpectrum * ps, double k)
{
    if(k == 0) return 1;

    /* ignore the 0 mode */

    int l = 0;
    int r = ps->base.size;

    while(r - l > 1) {
        int m = (r + l) / 2;
        /* if we are on the exact k, return value from there.*/
        if(k <= ps->edges[m])
            r = m;
        else
            l = m;
    }

    return ps->base.f[l];
}

double
fastpm_powerspectrum_get2(double k, FastPMPowerSpectrum * ps)
{
    return fastpm_powerspectrum_get(ps, k);
}

struct sigma2_int {
    FastPMPowerSpectrum * ps;
    double R;
};
static
double sigma2_int(double k, struct sigma2_int * param)
{
    double kr, kr3, kr2, w, x;
    double r_tophat = param->R;

    kr = r_tophat * k;
    kr2 = kr * kr;
    kr3 = kr2 * kr;

    if(kr < 1e-8)
        return 0;

    w = 3 * (sin(kr) / kr3 - cos(kr) / kr2);
    x = 4 * M_PI * k * k * w * w * fastpm_powerspectrum_eval(param->ps, k);

    return x / pow(2 * M_PI, 3);
}

double
fastpm_powerspectrum_sigma(FastPMPowerSpectrum * ps, double R)
{
    const int WORKSIZE = 81920;

    double result, abserr;
    gsl_integration_workspace *workspace;
    gsl_function F;

    struct sigma2_int param;
    param.ps = ps;
    param.R = R;

    workspace = gsl_integration_workspace_alloc(WORKSIZE);

    F.function = (void*) &sigma2_int;
    F.params = &param;

    void * handler = gsl_set_error_handler_off ();
    //
    // note: 500/R is here chosen as (effectively) infinity integration boundary
    gsl_integration_qag(&F, 0, 500.0 * 1 / R,
            0, 1.0e-4, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);

    // high precision caused error
    gsl_integration_workspace_free(workspace);
    gsl_set_error_handler (handler);

    return sqrt(result);
}

void
fastpm_powerspectrum_scale(FastPMPowerSpectrum * ps, double factor)
{
    ptrdiff_t i;
    /* neglect the zero mode */
    for(i = 1; i < ps->base.size; i ++) {
        ps->base.f[i] *= factor;
    }
}

void
fastpm_powerspectrum_rebin(FastPMPowerSpectrum * ps, size_t newsize)
{
    /* this doesn't work */
    double * k1 = malloc(newsize * sizeof(k1[0]));
    double * p1 = malloc(newsize * sizeof(p1[0]));
    double * Nmodes1 = malloc(newsize * sizeof(Nmodes1[0]));
    double * edges1 = malloc((newsize + 1) * sizeof(edges1[0]));

    ptrdiff_t i;

    for(i = 0; i < newsize; i ++) {
        ptrdiff_t j1 = (i    ) * ps->base.size / newsize;
        ptrdiff_t j2 = (i + 1) * ps->base.size / newsize;
        ptrdiff_t j;
        k1[i] = 0;
        p1[i] = 0;
        Nmodes1[i] = 0;
        edges1[i] = ps->edges[j1];
        edges1[i + 1] = ps->edges[j2];
        for(j = j1; j < j2; j ++) {
            k1[i] += ps->base.k[j] * ps->Nmodes[j];
            p1[i] += ps->base.f[j] * ps->Nmodes[j];
            Nmodes1[i] += ps->Nmodes[j];
        }
        if(Nmodes1[i] > 0) {
            k1[i] /= Nmodes1[i];
            p1[i] /= Nmodes1[i];
        }
    }
    free(ps->base.k);
    free(ps->base.f);
    free(ps->Nmodes);
    free(ps->edges);
    ps->base.k = k1;
    ps->base.f = p1;
    ps->Nmodes = Nmodes1;
    ps->edges = edges1;
    ps->base.size = newsize;
}

int
fastpm_powerspectrum_init_from_string(FastPMPowerSpectrum * ps, const char * string)
{
    int r = fastpm_funck_init_from_string(&ps->base, string);
    ps->edges = malloc(sizeof(ps->edges[0]) * (ps->base.size + 1));
    ps->Nmodes = malloc(sizeof(ps->Nmodes[0]) * ps->base.size);
    return r;
}

void
fastpm_funck_init(FastPMFuncK * fk, const size_t size)
{
    fk->size = size;
    fk->k = malloc(sizeof(fk->k[0]) * fk->size);
    fk->f = malloc(sizeof(fk->f[0]) * fk->size);
}

int
fastpm_funck_init_from_string(FastPMFuncK * fk, const char * string)
{
    char ** list = fastpm_strsplit(string, "\n");
    char ** line;
    ptrdiff_t i;
    int pass = 0;
    /* two pass parsing, first pass for counting */
    /* second pass for assignment */
    while(pass < 2) {
        i = 0;
        for (line = list; *line; line++) {
            double k, f;
            if(2 == sscanf(*line, "%lg\t%lg", &k, &f)) {   // note tab. Could make more general?
                if(pass == 1) {
                    fk->k[i] = k;
                    fk->f[i] = f;
                }
                i ++;
            }
        }
        if(pass == 0) {
            fastpm_funck_init(fk, i);
        }
        pass ++;
    }

    free(list);
    if(fk->size == 0) {
        /* Nothing is savagable in the file.*/
        return 0;
    }
    return 0;
}

/* inverted calling signature for callbacks */
double
fastpm_funck_eval2(double k, FastPMFuncK * fk)
{
    return fastpm_funck_eval(fk, k);
}

double
fastpm_funck_eval(FastPMFuncK * fk, double k)
{

    /* ignore the 0 mode */
    if(k == 0) return 1;

    int l = 0;
    int r = fk->size - 1;

    /* determine the right and left values around k */
    while(r - l > 1) {
        int m = (r + l) / 2;
        if(k < fk->k[m])
            r = m;
        else
            l = m;
    }
    double k2 = fk->k[r],
           k1 = fk->k[l];
    double f2 = fk->f[r],
           f1 = fk->f[l];

    if(l == r) {
        return fk->f[l];
    }

    if(f1 <= 0 || f2 <= 0 || k1 == 0 || k2 == 0) {
        /* if any of the f is zero, or negative, use linear interpolation */
        double f = (k - k1) * f2 + (k2 - k) * f1;
        f /= (k2 - k1);
        return f;
    } else {
        k = log(k);
        f1 = log(f1);
        f2 = log(f2);
        k1 = log(k1);
        k2 = log(k2);
        double f = (k - k1) * f2 + (k2 - k) * f1;
        f /= (k2 - k1);
        return exp(f);
    }
}

void
fastpm_funck_destroy(FastPMFuncK * fk)
{
    free(fk->f);
    free(fk->k);
}


#pragma once

#include <array>

#include <gsl/span>

#include "Quiver/Animation/Rect.h"

namespace qvr {

struct ViewBuffer {
	int viewCount = 0;
	std::array<Animation::Rect, 8> views;

	// TODO: Consider making initial value of viewCount == 1.
	// TODO: Consider making viewCount < 1 illegal.
};

// TODO: Consider making these public member functions of ViewBuffer and
// making the data members private.

inline void SetView(
	ViewBuffer& vb,
	const Animation::Rect& singleView)
{
	vb.viewCount = 1;
	vb.views[0] = singleView;
}

inline void SetViews(
	ViewBuffer& target,
	const gsl::span<const Animation::Rect> newViews)
{
	target.viewCount =
		std::min(
		(int)newViews.length(),
			(int)target.views.max_size());

	std::copy(
		std::begin(newViews),
		std::begin(newViews) + target.viewCount,
		std::begin(target.views));
}

inline auto GetViews(const ViewBuffer& vb) -> gsl::span<const Animation::Rect> {
	return gsl::make_span(&vb.views[0], vb.viewCount);
}

inline const Animation::Rect& CalculateView(
	const ViewBuffer& vb,
	const float objectAngle,
	const float viewAngle)
{
	assert(vb.viewCount > 0);

	const float Tau = 6.28318530718f;

	const int viewIndex =
		(int)((fmodf((objectAngle - viewAngle + Tau), Tau) / Tau) * (vb.viewCount));

	// Make sure my maths is right.
	assert(viewIndex >= 0);
	assert(viewIndex < vb.viewCount);

	return vb.views[viewIndex];
}

}


// Copyright (c) 2021 Stig Rune Sellevag
//
// This file is distributed under the MIT License. See the accompanying file
// LICENSE.txt or http://www.opensource.org/licenses/mit-license.php for terms
// and conditions.

#ifndef SCILIB_LINALG_LAPACK_TYPES_H
#define SCILIB_LINALG_LAPACK_TYPES_H

//------------------------------------------------------------------------------
// Define integer type for use with BLAS, LAPACK and Intel MKL.

#ifdef USE_MKL
#include <mkl.h>
#define BLAS_INT MKL_INT
#else
#include <cblas.h>
#ifdef blasint
#define BLAS_INT blasint
#else
#define BLAS_INT int
#endif
#endif

//------------------------------------------------------------------------------
// Define complex type for use with LAPACK.

#ifndef USE_MKL
#include <complex>
#define lapack_complex_float std::complex<float>
#define lapack_complex_double std::complex<double>
#endif

#endif // SCILIB_LINALG_LAPACK_TYPES_H


#ifndef UTILS_H
#define UTILS_H

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <memory.h>
#include <time.h>

#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_permutation.h>
#include <gsl/gsl_sf_psi.h>
#include <gsl/gsl_sf_gamma.h>

#include <vector>
using namespace std;

double log_sum(double log_a, double log_b);
double log_normalize(double * array, int nlen);
double log_normalize(vector<double> & vec, int nlen);
double log_subtract(double log_a, double log_b);
double log_factorial(int n, double a);
double similarity(const int* v1, const int* v2, int n);

bool   file_exists(const char * filename);
bool   dir_exists(const char * directory);

template <typename T> void free_vec_ptr(vector <T* > & v)
{
    int size = v.size();
    T* p = NULL;
    for (int i = 0; i < size; i ++)
    {
        p = v[i];
        delete [] p;
    }
    v.clear();
}

// find the max and argmax in an array
template <typename T> T max(const T * x, int n, int* argmax)
{
    *argmax = 0;
    T max_val = x[0];
    for (int i = 1; i < n; i++)
        if (x[i] > max_val)
        {
            max_val = x[i];
            *argmax = i;
        }
    return max_val;
}

// find the max and argmax in an vector
template <typename T> T max_vec(vector<T> & v, int n, int* argmax)
{
    *argmax = 0;
    T max_val = v[0];
    for (int i = 1; i < n; i++)
        if (v[i] > max_val)
        {
            max_val = v[i];
            *argmax = i;
        }
    return max_val;
}

// set a value to the entire array
template <typename T> void set_array(T * array, int size, T value)
{
    for (int i = 0; i < size; i++) array[i] = value;
}

// swap two elements in an array
template < typename T > void swap_array_element(T * array, int i, int j)
{
    if (i == j) return;
    T a = array[i];
    array[i] = array[j];
    array[j] = a;
}

// set a value to a entrie vector
template <typename T> void set_vector(vector<T> & v, T value)
{
    int size = v.size();
    for (int i = 0; i < size; i++) v[i] = value;
}

// swap two elements in vector
template < typename T > void swap_vec_element(vector<T> & v, int i, int j)
{
    if (i == j) return; // no need to swap
    T a = v[i];
    v[i] = v[j];
    v[j] = a;
}

/// gsl_wrappers
//double lgamma(double x); // linux has this
unsigned int rmultinomial(const double* p, int n, double tot_p=-1);
double rgamma(double a, double b);
double rbeta(double a, double b);
unsigned int rbernoulli(double p);
double runiform();
void rshuffle (void* base, size_t n, size_t size);
unsigned long int runiform_int(unsigned long int n);

#endif


/**
 *
 * @file core_strdalg1.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Azzam Haidar
 * @date 2013-07-04
 * @generated s Tue Jan  7 11:44:50 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 * @ingroup CORE_float
 *
 *  CORE_strdalg1 is a part of the tridiagonal reduction algorithm (bulgechasing)
 *  It correspond to a local driver of the kernels that should be executed on a
 *  single core.
 *
 *******************************************************************************
 *
 * @param[in] n
 *          The order of the matrix A. n >= 0.
 *
 * @param[in] nb
 *          The size of the Bandwidth of the matrix A,
 *          which correspond to the tile size. nb >= 0.
 *
 * @param[in,out] A
 *          float array, dimension (lda,n)
 *          On entry, the (nb+1)-by-n band lower hermetian matrix to
 *          be reduced to tridiagonal.
 *          On exit, the diagonal and first subdiagonal of A are over-
 *          written by the corresponding elements of the tridiagonal.
 *
 * @param[in] lda
 *          (input) INTEGER
 *          The leading dimension of the array A.  LDA >= max(1,nb+1).
 *
 * @param[out] V
 *          float array, dimension (n) if wantz=0
 *          or ldv*Vblksiz*blkcnt if wantz>0.
 *          The scalar elementary reflectors are written in this
 *          array.
 *
 * @param[out] TAU
 *          float array, dimension (n) if wantz=0
 *          or Vblksiz*Vblksiz*blkcnt if wantz>0.
 *          The scalar factors of the elementary reflectors are written
 *          in this array.
 *
 * @param[in] Vblksiz
 *          Local parameter to Plasma. It correspond to the local bloccking
 *          of the applyQ2 used to apply the orthogonal matrix Q2.
 *
 * @param[in] wantz
 *          integer tobe 0 or 1. if wantz=0 the V and TAU are not stored on
 *          only they are kept for next step then overwritten.
 *
 * @param[in] i
 *          Integer that refer to the current sweep. (outer loop).
 *
 * @param[in] sweepid
 *          Integer that refer to the sweep to chase.(inner loop).
 *
 * @param[in] m
 *          Integer that refer to a sweep step, to ensure order dependencies.
 *
 * @param[in] grsiz
 *          Integer that refer to the size of a group.
 *          group mean the number of kernel that should be executed sequentially
 *          on the same core.
 *          group size is a trade-off between locality (cache reuse) and parallelism.
 *          a small group size increase parallelism while a large group size increase
 *          cache reuse.
 *
 * @param[in] work
 *          Workspace of size nb. Used by the core_ssbtype[123]cb.
 *
 ******************************************************************************/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_strdalg1 = PCORE_strdalg1
#define CORE_strdalg1 PCORE_strdalg1
#endif
void CORE_strdalg1( int n, int nb,
                    float *A, int lda,
                    float *V,
                    float *TAU,
                    int Vblksiz, int wantz,
                    int i, int sweepid, int m, int grsiz,
                    float *work)
{
    int k, shift=3;
    int myid, colpt, stind, edind, blklastind, stepercol;

    k = shift / grsiz;
    stepercol = (k*grsiz == shift) ? k : k+1;
    for (k = 0; k < grsiz; k++){
        myid = (i-sweepid)*(stepercol*grsiz) +(m-1)*grsiz + k+1;
        if(myid%2 ==0) {
            colpt      = (myid/2) * nb + 1 + sweepid - 1;
            stind      = colpt - nb + 1;
            edind      = min(colpt, n);
            blklastind = colpt;
        } else {
            colpt      = ((myid+1)/2)*nb + 1 +sweepid -1 ;
            stind      = colpt-nb+1;
            edind      = min(colpt,n);
            if( (stind>=edind-1) && (edind==n) )
                blklastind = n;
            else
                blklastind = 0;
        }

        if( myid == 1 )
           CORE_ssbtype1cb(n, nb, A, lda, V, TAU, stind-1, edind-1, sweepid-1, Vblksiz, wantz, work);
        else if(myid%2 == 0)
           CORE_ssbtype2cb(n, nb, A, lda, V, TAU, stind-1, edind-1, sweepid-1, Vblksiz, wantz, work);
        else /*if(myid%2 == 1)*/
           CORE_ssbtype3cb(n, nb, A, lda, V, TAU, stind-1, edind-1, sweepid-1, Vblksiz, wantz, work);

        if(blklastind >= (n-1))  break;
    }
}


#include <gsl/gsl_math.h>
#include "gsl_cblas.h"
#include "cblas.h"

void
cblas_drotmg (double *d1, double *d2, double *b1, const double b2, double *P)
{
#define BASE double
#include "source_rotmg.h"
#undef BASE
}


#pragma once

#include <cstdint>
#include <gsl/gsl>
#include "utils.h"
#include "halley/text/halleystring.h"

struct XXH64_state_s;
typedef struct XXH64_state_s XXH64_state_t;

namespace Halley {
    class Hash {
    public:
        static uint64_t hash(const Bytes& bytes);
        static uint64_t hash(gsl::span<const gsl::byte> bytes);

    	template <typename T>
    	static uint64_t hashValue(const T& v)
    	{
    		return hash(gsl::as_bytes(gsl::span<const T>(&v, 1)));
    	}

		static uint32_t compressTo32(uint64_t value);

		class Hasher
		{
		public:
			Hasher();
			~Hasher();

			void feed(const String& string)
			{
				feedBytes(gsl::as_bytes(gsl::span<const char>(string.c_str(), string.length())));
			}

			template<typename T>
			void feed(const T& data)
			{
				feedBytes(gsl::as_bytes(gsl::span<const T>(&data, 1)));
			}

			void feedBytes(gsl::span<const gsl::byte> bytes);

			[[nodiscard]] uint64_t digest();

		private:
			bool ready;
			XXH64_state_t* data;
		};
    };
}


/*
 * Copyright (c) 2011, Alex Krizhevsky (akrizhevsky@gmail.com)
 * All rights reserved.
 *
 * 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.
 */

#ifndef MATRIX_H_
#define MATRIX_H_

#include <matrix_funcs.h>
#ifdef NUMPY_INTERFACE
#include <Python.h>
#include <arrayobject.h>
#endif
#include <limits>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#if defined(_WIN64) || defined(_WIN32)
#include <float.h>
#define isnan(_X) (_isnan(_X))
#define isinf(_X) (!_finite(_X))
#define uint unsigned int
double sqrt(int _X);
double log(int _X);
#endif

#ifdef USE_MKL
#include <mkl.h>
#include <mkl_cblas.h>
#include <mkl_vsl.h>
#include <mkl_vml.h>

#define IS_MKL true

#ifdef DOUBLE_PRECISION
#define MKL_UNIFORM vdRngUniform
#define MKL_NORMAL vdRngGaussian
#define MKL_UNIFORM_RND_METHOD VSL_METHOD_DUNIFORM_STD_ACCURATE
#define MKL_GAUSSIAN_RND_METHOD VSL_METHOD_DGAUSSIAN_BOXMULLER
#define MKL_EXP vdExp
#define MKL_RECIP vdInv
#define MKL_SQUARE vdSqr
#define MKL_TANH vdTanh
#define MKL_LOG vdLn
#define MKL_VECMUL vdMul
#define MKL_VECDIV vdDiv
#else
#define MKL_UNIFORM vsRngUniform
#define MKL_NORMAL vsRngGaussian
#define MKL_UNIFORM_RND_METHOD VSL_METHOD_SUNIFORM_STD_ACCURATE
#define MKL_GAUSSIAN_RND_METHOD VSL_METHOD_SGAUSSIAN_BOXMULLER
#define MKL_EXP vsExp
#define MKL_RECIP vsInv
#define MKL_SQUARE vsSqr
#define MKL_TANH vsTanh
#define MKL_LOG vsLn
#define MKL_VECMUL vsMul
#define MKL_VECDIV vsDiv
#endif /* DOUBLE_PRECISION */

#else
extern "C" {
#include <cblas.h>
}
#define IS_MKL false
#endif /* USE_MKL */

#ifdef DOUBLE_PRECISION
#define CBLAS_GEMM cblas_dgemm
#define CBLAS_SCAL cblas_dscal
#define CBLAS_AXPY cblas_daxpy
#else
#define CBLAS_GEMM cblas_sgemm
#define CBLAS_SCAL cblas_sscal
#define CBLAS_AXPY cblas_saxpy
#endif /* DOUBLE_PRECISION */

#define MTYPE_MAX numeric_limits<MTYPE>::max()

class Matrix {
private:
    MTYPE* _data;
    bool _ownsData;
    long int _numRows, _numCols;
    long int _numElements;
    CBLAS_TRANSPOSE _trans;

    void _init(MTYPE* data, long int numRows, long int numCols, bool transpose, bool ownsData);
    void _tileTo2(Matrix& target) const;
    void _copyAllTo(Matrix& target) const;
    MTYPE _sum_column(long int col) const;
    MTYPE _sum_row(long int row) const;
    MTYPE _aggregate(MTYPE(*agg_func)(MTYPE, MTYPE), MTYPE initialValue) const;
    void _aggregate(long int axis, Matrix& target, MTYPE(*agg_func)(MTYPE, MTYPE), MTYPE initialValue) const;
    MTYPE _aggregateRow(long int row, MTYPE(*agg_func)(MTYPE, MTYPE), MTYPE initialValue) const;
    MTYPE _aggregateCol(long int row, MTYPE(*agg_func)(MTYPE, MTYPE), MTYPE initialValue) const;
    void _updateDims(long int numRows, long int numCols);
    void _applyLoop(MTYPE(*func)(MTYPE));
    void _applyLoop(MTYPE (*func)(MTYPE), Matrix& target);
    void _applyLoop2(const Matrix& a, MTYPE(*func)(MTYPE, MTYPE), Matrix& target) const;
    void _applyLoop2(const Matrix& a, MTYPE (*func)(MTYPE,MTYPE, MTYPE), MTYPE scalar, Matrix& target) const;
    void _applyLoopScalar(const MTYPE scalar, MTYPE(*func)(MTYPE, MTYPE), Matrix& target) const;
    void _checkBounds(long int startRow, long int endRow, long int startCol, long int endCol) const;
    void _divideByVector(const Matrix& vec, Matrix& target);
    inline long int _getNumColsBackEnd() const {
        return _trans == CblasNoTrans ? _numCols : _numRows;
    }
public:
    enum FUNCTION {
        TANH, RECIPROCAL, SQUARE, ABS, EXP, LOG, ZERO, ONE, LOGISTIC1, LOGISTIC2, SIGN
    };
    Matrix();
    Matrix(long int numRows, long int numCols);
#ifdef NUMPY_INTERFACE
    Matrix(const PyArrayObject *src);
#endif
    Matrix(const Matrix &like);
    Matrix(MTYPE* data, long int numRows, long int numCols);
    Matrix(MTYPE* data, long int numRows, long int numCols, bool transpose);
    ~Matrix();

    inline MTYPE& getCell(long int i, long int j) const {
        assert(i >= 0 && i < _numRows);
        assert(j >= 0 && j < _numCols);
        if (_trans == CblasTrans) {
            return _data[j * _numRows + i];
        }
        return _data[i * _numCols + j];
    }

    MTYPE& operator()(long int i, long int j) const {
        return getCell(i, j);
    }

    inline MTYPE* getData() const {
        return _data;
    }

    inline bool isView() const {
        return !_ownsData;
    }

    inline long int getNumRows() const {
        return _numRows;
    }

    inline long int getNumCols() const {
        return _numCols;
    }

    inline long int getNumDataBytes() const {
        return _numElements * sizeof(MTYPE);
    }

    inline long int getNumElements() const {
        return _numElements;
    }

    inline long int getLeadingDim() const {
        return _trans == CblasTrans ? _numRows : _numCols;
    }

    inline long int getFollowingDim() const {
        return _trans == CblasTrans ? _numCols : _numRows;
    }

    inline CBLAS_TRANSPOSE getBLASTrans() const {
        return _trans;
    }

    inline bool isSameDims(const Matrix& a) const {
        return a.getNumRows() == getNumRows() && a.getNumCols() == getNumCols();
    }

    inline bool isTrans() const {
        return _trans == CblasTrans;
    }

    /*
     * Only use if you know what you're doing!
     * Does not update any dimensions. Just flips the _trans flag.
     *
     * Use transpose() if you want to get the transpose of this matrix.
     */
    inline void setTrans(bool trans) {
        assert(isTrans() == trans || !isView());
        _trans = trans ? CblasTrans : CblasNoTrans;
    }

    void apply(FUNCTION f);
    void apply(Matrix::FUNCTION f, Matrix& target);
    void subtractFromScalar(MTYPE scalar);
    void subtractFromScalar(MTYPE scalar, Matrix &target) const;
    void biggerThanScalar(MTYPE scalar);
    void smallerThanScalar(MTYPE scalar);
    void equalsScalar(MTYPE scalar);
    void biggerThanScalar(MTYPE scalar, Matrix& target) const;
    void smallerThanScalar(MTYPE scalar, Matrix& target) const;
    void equalsScalar(MTYPE scalar, Matrix& target) const;
    void biggerThan(Matrix& a);
    void biggerThan(Matrix& a, Matrix& target) const;
    void smallerThan(Matrix& a);
    void smallerThan(Matrix& a, Matrix& target) const;
    void minWith(Matrix &a);
    void minWith(Matrix &a, Matrix &target) const;
    void maxWith(Matrix &a);
    void maxWith(Matrix &a, Matrix &target) const;
    void equals(Matrix& a);
    void equals(Matrix& a, Matrix& target) const;
    void notEquals(Matrix& a) ;
    void notEquals(Matrix& a, Matrix& target) const;
    void add(const Matrix &m);
    void add(const Matrix &m, MTYPE scale);
    void add(const Matrix &m, Matrix& target);
    void add(const Matrix &m, MTYPE scale, Matrix& target);
    void subtract(const Matrix &m);
    void subtract(const Matrix &m, Matrix& target);
    void subtract(const Matrix &m, MTYPE scale);
    void subtract(const Matrix &m, MTYPE scale, Matrix& target);
    void addVector(const Matrix& vec, MTYPE scale);
    void addVector(const Matrix& vec, MTYPE scale, Matrix& target);
    void addVector(const Matrix& vec);
    void addVector(const Matrix& vec, Matrix& target);
    void addScalar(MTYPE scalar);
    void addScalar(MTYPE scalar, Matrix& target) const;
    void maxWithScalar(MTYPE scalar);
    void maxWithScalar(MTYPE scalar, Matrix &target) const;
    void minWithScalar(MTYPE scalar);
    void minWithScalar(MTYPE scalar, Matrix &target) const;
    void eltWiseMultByVector(const Matrix& vec);
    void eltWiseMultByVector(const Matrix& vec, Matrix& target);
    void eltWiseDivideByVector(const Matrix& vec);
    void eltWiseDivideByVector(const Matrix& vec, Matrix& target);
    void resize(long int newNumRows, long int newNumCols);
    void resize(const Matrix& like);
    Matrix& slice(long int startRow, long int endRow, long int startCol, long int endCol) const;
    void slice(long int startRow, long int endRow, long int startCol, long int endCol, Matrix &target) const;
    Matrix& sliceRows(long int startRow, long int endRow) const;
    void sliceRows(long int startRow, long int endRow, Matrix& target) const;
    Matrix& sliceCols(long int startCol, long int endCol) const;
    void sliceCols(long int startCol, long int endCol, Matrix& target) const;
    void rightMult(const Matrix &b, MTYPE scale);
    void rightMult(const Matrix &b, Matrix &target) const;
    void rightMult(const Matrix &b);
    void rightMult(const Matrix &b, MTYPE scaleAB, Matrix &target) const;
    void addProduct(const Matrix &a, const Matrix &b, MTYPE scaleAB, MTYPE scaleThis);
    void addProduct(const Matrix& a, const Matrix& b);
    void eltWiseMult(const Matrix& a);
    void eltWiseMult(const Matrix& a, Matrix& target) const;
    void eltWiseDivide(const Matrix& a);
    void eltWiseDivide(const Matrix& a, Matrix &target) const;
    Matrix& transpose() const;
    Matrix& transpose(bool hard) const;
    Matrix& tile(long int timesY, long int timesX) const;
    void tile(long int timesY, long int timesX, Matrix& target) const;
    void copy(Matrix &dest, long int srcStartRow, long int srcEndRow, long int srcStartCol, long int srcEndCol, long int destStartRow, long int destStartCol) const;
    Matrix& copy() const;
    void copy(Matrix& target) const;
    Matrix& sum(long int axis) const;
    void sum(long int axis, Matrix &target) const;
    MTYPE sum() const;
    MTYPE max() const;
    Matrix& max(long int axis) const;
    void max(long int axis, Matrix& target) const;
    MTYPE min() const;
    Matrix& min(long int axis) const;
    void min(long int axis, Matrix& target) const;
    MTYPE norm() const;
    MTYPE norm2() const;
    void scale(MTYPE scale);
    void scale(MTYPE alpha, Matrix& target);
    void reshape(long int numRows, long int numCols);
    Matrix& reshaped(long int numRows, long int numCols);
    void printShape(const char* name) const;
    bool hasNan() const;
    bool hasInf() const;
#ifdef USE_MKL
    void randomizeNormal(VSLStreamStatePtr stream, MTYPE mean, MTYPE stdev);
    void randomizeUniform(VSLStreamStatePtr stream);
    void randomizeNormal(VSLStreamStatePtr stream);
#else
    void randomizeNormal(MTYPE mean, MTYPE stdev);
    void randomizeUniform();
    void randomizeNormal();
#endif
    void print() const;
    void print(long int startRow,long int rows, long int startCol,long int cols) const;
    void print(long int rows, long int cols) const;
};

#endif /* MATRIX_H_ */


/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016-Present Couchbase, Inc.
 *
 *   Use of this software is governed by the Business Source License included
 *   in the file licenses/BSL-Couchbase.txt.  As of the Change Date specified
 *   in that file, in accordance with the Business Source License, use of this
 *   software will be governed by the Apache License, Version 2.0, included in
 *   the file licenses/APL2.txt.
 */
#pragma once

#include <gsl/gsl-lite.hpp>
#include <nlohmann/json_fwd.hpp>
#include <cstdint>
#include <string>

namespace cb::crypto {
enum class Algorithm { MD5, SHA1, SHA256, SHA512 };

bool isSupported(Algorithm algorithm);

const int MD5_DIGEST_SIZE = 16;
const int SHA1_DIGEST_SIZE = 20;
const int SHA256_DIGEST_SIZE = 32;
const int SHA512_DIGEST_SIZE = 64;

/**
 * Generate a HMAC digest of the key and data by using the given
 * algorithm
 *
 * @throws std::invalid_argument - unsupported algorithm
 *         std::runtime_error - Failures generating the HMAC
 */
std::string HMAC(Algorithm algorithm,
                 std::string_view key,
                 std::string_view data);

/**
 * Generate a PBKDF2_HMAC digest of the key and data by using the given
 * algorithm
 *
 * @throws std::invalid_argument - unsupported algorithm
 *         std::runtime_error - Failures generating the HMAC
 */
std::string PBKDF2_HMAC(Algorithm algorithm,
                        const std::string& pass,
                        std::string_view salt,
                        unsigned int iterationCount);

/**
 * Generate a digest by using the requested algorithm
 */
std::string digest(Algorithm algorithm, std::string_view data);

enum class Cipher { AES_256_cbc };

Cipher to_cipher(const std::string& str);

/**
 * Encrypt the specified data by using a given cipher
 *
 * @param cipher The cipher to use
 * @param key The key used for encryption
 * @param ivec The IV to use for encryption
 * @param data The Pointer to the data to encrypt
 * @return The encrypted data
 */
std::string encrypt(Cipher cipher,
                    std::string_view key,
                    std::string_view iv,
                    std::string_view data);

/**
 * Encrypt the specified data
 *
 * {
 *    "cipher" : "name of the cipher",
 *    "key" : "base64 of the raw key",
 *    "iv" : "base64 of the raw iv"
 * }
 *
 * @param meta the json description of the encryption to use
 * @param data Pointer to the data to encrypt
 * @param length the length of the data to encrypt
 * @return The encrypted data
 */
std::string encrypt(const nlohmann::json& json, std::string_view data);

/**
 * Decrypt the specified data by using a given cipher
 *
 * @param cipher The cipher to use
 * @param key The key used for encryption
 * @param ivec The IV to use for encryption
 * @param data The data to decrypt
 * @return The decrypted data
 */
std::string decrypt(Cipher cipher,
                    std::string_view key,
                    std::string_view iv,
                    std::string_view data);

/**
 * Decrypt the specified data
 *
 * {
 *    "cipher" : "name of the cipher",
 *    "key" : "base64 of the raw key",
 *    "iv" : "base64 of the raw iv"
 * }
 *
 * @param meta the json description of the encryption to use
 * @param data Pointer to the data to decrypt
 * @param length the length of the data to decrypt
 * @return The decrypted data
 */
std::string decrypt(const nlohmann::json& json, std::string_view data);

} // namespace cb::crypto


// align.h
//
// prototypes for align.cc
//
// dw, 12/12/3
#ifndef _PTALIGN_H_
#define _PTALIGN_H_

#include <gsl/gsl_matrix.h>

#include <vector>
#include <cmath>
#include <map>
#include "pdb.h"

typedef double T3Matrix[3][3];

// find rigid body transformation z=Rx+t minimizing
// least squares error ||y-z||**2
bool ptalign(gsl_matrix_const_view , gsl_matrix_const_view, gsl_matrix *, gsl_vector *, double *);

bool ptalign(vector<vector<double> >&, vector<vector<double> >&, double*);

#endif