/* Copyright 2008 BORDEAUX I UNIVERSITY & INRIA 
**
** This file is part of the PaStiX parallel sparse matrix package.
**
** This software is governed by the CeCILL-C license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
** 
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
** 
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
** 
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-C license and that you accept its terms.
*/
/**
 *
 * @file core_zgeadd.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
 * @precisions normal z -> c d s
 *
 **/

#include "common_pastix.h"
#include "core_zgeadd.h"
#include "sopalin_compute.h"
#include "sopalin_define.h"
#include "sopalin_acces.h"
#include "symbol.h"
#include "ftgt.h"
#include "csc.h"
#include "updown.h"
#include "queue.h"
#include "bulles.h"
#include "solver.h"
#include "sopalin_thread.h"
#include "sopalin_time.h"
#include "sopalin3d.h"

#define cblas_zaxpy(m, al, A, lda, B, ldb) do { \
    BLAS_INT i;					\
    for (i=0; i<m; i++) {			\
      (B)[i*ldb] = (B)[i*ldb] + *al*(A)[i*lda];	\
    }						\
  } while(0)

#define cblas_zaxpy_blas(m,al, A, lda, B, ldb) do {  \
        BLAS_INT mym = m;                       \
        BLAS_INT mylda = lda;                   \
        BLAS_INT myldb = LDB;                   \
        AXPY(&mym, al, A, &mylda, B, &myldb);   \
    } while(0)
#define CBLAS_SADDR(a) &(a)
#define z_SolverBlok SolverBlok
#define z_Sopalin_Data_t Sopalin_Data_t
#define PASTIX_SUCCESS NO_ERR
#define pastix_int_t PASTIX_INT
/**
 ******************************************************************************
 *
 * @ingroup pastix_kernel
 *
 *  core_zgeadd adds to matrices together.
 *
 *       B <- alpha * op(A)  + B
 *
 *******************************************************************************
 *
 * @param[in] trans
 *         @arg CblasNoTrans   :  No transpose, op( A ) = A;
 *         @arg CblasTrans     :  Transpose, op( A ) = A';
 *         @arg CblasConjTrans :  Conjugate Transpose, op( A ) = conj(A').
 *
 * @param[in] M
 *          Number of rows of the matrix B.
 *          Number of rows of the matrix A, if trans == CblasNoTrans, number of
 *          columns of A otherwise.
 *
 * @param[in] N
 *          Number of columns of the matrix B.
 *          Number of columns of the matrix A, if trans == CblasNoTrans, number
 *          of rows of A otherwise.
 *
 * @param[in] alpha
 *          Scalar factor of A.
 *
 * @param[in] A
 *          Matrix of size LDA-by-N, if trans == CblasNoTrans, LDA-by-M,
 *          otherwise.
 *
 * @param[in] LDA
 *          Leading dimension of the array A. LDA >= max(1,K).
 *          K = M if trans == CblasNoTrans, K = N otherwise.
 *
 * @param[in,out] B
 *          Matrix of size LDB-by-N.
 *
 * @param[in] LDB
 *          Leading dimension of the array B. LDB >= max(1,M)
 *
 *******************************************************************************
 *
 * @return
 *          \retval PASTIX_SUCCESS successful exit
 *          \retval <0 if -i, the i-th argument had an illegal value
 *          \retval 1, not yet implemented
 *
 ******************************************************************************/
int core_zgeadd(int trans, int M, int N, pastix_complex64_t alpha,
                const pastix_complex64_t *A, int LDA,
                      pastix_complex64_t *B, int LDB)
{
    int j;

#if defined(PASTIX_DEBUG)
    if (M < 0) {
        //coreblas_error(1, "Illegal value of M");
        return -1;
    }
    if (N < 0) {
        //coreblas_error(2, "Illegal value of N");
        return -2;
    }
    if ( (LDA < MAX(1,M)) && (M > 0) ) {
        //coreblas_error(5, "Illegal value of LDA");
        return -5;
    }
    if ( (LDB < MAX(1,M)) && (M > 0) ) {
        //coreblas_error(7, "Illegal value of LDB");
        return -7;
    }
#endif

    if (trans == CblasNoTrans) {
        if (M == LDA && M == LDB)
	  cblas_zaxpy(M*N, CBLAS_SADDR(alpha), A, 1, B, 1);
        else {
            for (j = 0; j < N; j++)
                cblas_zaxpy(M, CBLAS_SADDR(alpha), A + j*LDA, 1, B + j*LDB, 1);
        }
    }
    else if (trans == CblasTrans ) {
        for (j = 0; j < N; j++)
            cblas_zaxpy(M, CBLAS_SADDR(alpha), A + j, LDA, B + j*LDB, 1);

    }
    /* trans == CblasConjTrans */
    else {
        return -10;
    }

    return PASTIX_SUCCESS;
}


/**
 ******************************************************************************
 *
 * @ingroup pastix_kernel
 *
 *  core_zgeaddsp1d adds two column blocks together.
 *
 *       cblk2 <- cblk1  + cblk2
 *
 *******************************************************************************
 *
 * @param[in] cblk1
 *          The pointer to the data structure that describes the panel to add.
 *          Next column blok must be accessible through cblk1[1].
 *
 * @param[in] cblk2
 *          The pointer to the data structure that describes the panel in which
 *          we add.
 *          Next column blok must be accessible through cblk2[1].
 *
 * @param[in] L
 *          The pointer to the lower matrix storing the coefficients of the
 *          panel. Must be of size cblk.stride -by- cblk.width
 *
 * @param[in] Cl
 *          The pointer to the lower matrix storing the coefficients of the
 *          updated panel. Must be of size cblk.stride -by- cblk.width
 *
 * @param[in,out] U
 *          The pointer to the upper matrix storing the coefficients of the
 *          panel. Must be of size cblk.stride -by- cblk.width. Ignored if
 *          NULL.
 *
 *
 * @param[in,out] Cu
 *          The pointer to the upper matrix storing the coefficients of the
 *          updated panel. Must be of size cblk.stride -by- cblk.width
 *
 *******************************************************************************
 *
 * @return
 *          \retval PASTIX_SUCCESS successful exit
 *
 ******************************************************************************/
int
core_zgeaddsp1d(SolverMatrix * datacode,
                z_SolverCblk * cblk1,
                z_SolverCblk * cblk2,
                pastix_complex64_t * L,
                pastix_complex64_t * Cl,
                pastix_complex64_t * U,
                pastix_complex64_t * Cu) {
    z_SolverBlok *iterblok;
    z_SolverBlok *firstblok;
    z_SolverBlok *lastblok;
    z_SolverBlok *fblok;
    pastix_int_t ncol1 = cblk1->lcolnum - cblk1->fcolnum + 1;
    pastix_complex64_t *ga, *gb;

    assert(cblk_isfanin(datacode, cblk1));
    firstblok = datacode->fbloktab[fanin_getclustnum(datacode, cblk1)] + cblk1->bloknum;
    lastblok  = datacode->fbloktab[fanin_getclustnum(datacode, cblk1)] + cblk1[1].bloknum;
    fblok     = datacode->bloktab + cblk2->bloknum;

    assert(cblk1->fcolnum >= cblk2->fcolnum);

    for (iterblok = firstblok; iterblok < lastblok; iterblok++) {
        pastix_int_t nrow;
        /* Find facing bloknum */
        while (!z_is_block_inside_fblock( iterblok, fblok )) {
            fblok++;
            assert( fblok < datacode->bloktab + cblk2[1].bloknum );
        }
        ga = L + iterblok->coefind;
        gb = Cl + cblk2->stride*(cblk1->fcolnum-cblk2->fcolnum) +
            fblok->coefind +
            iterblok->frownum - fblok->frownum;
        nrow = iterblok->lrownum - iterblok->frownum + 1;
        core_zgeadd( CblasNoTrans,
                     nrow, ncol1, 1.0,
		     ga, cblk1->stride,
		     gb, cblk2->stride );
        if (U != NULL) {
            ga = U + iterblok->coefind;
            gb = Cu + cblk2->stride*(cblk1->fcolnum-cblk2->fcolnum) +
                fblok->coefind +
                iterblok->frownum - fblok->frownum;
            core_zgeadd( CblasNoTrans,
                         nrow, ncol1, 1.0,
                         ga, cblk1->stride,
                         gb, cblk2->stride );
        }
    }
    return PASTIX_SUCCESS;
}


/**
 *******************************************************************************
 *
 * @ingroup pastix_starpu_kernel
 *
 * starpu_zgetrfsp1d_geadd_cpu - Computes the addition of a fanin column block
 * into the destination column block.
 *
 *******************************************************************************
 *
 * @param[in, out] buffers
 *          Array of 4 buffers:
 *            -# L : The pointer to the lower matrix storing the coefficients
 *                   of the panel. Must be of size cblk1.stride -by- cblk1.width
 *            -# Cl : The pointer to the lower matrix storing the coefficients
 *                    of the updated panel. Must be of size cblk2.stride -by-
 *                    cblk2.width
 *            -# U : The pointer to the upper matrix storing the coefficients
 *                   of the panel. Must be of size cblk1.stride -by- cblk1.width
 *            -# Cu : The pointer to the upper matrix storing the coefficients
 *                    of the updated panel. Must be of size cblk2.stride -by-
 *                    cblk2.width
 *
 * @param[in, out] _args
 *          Package of arguments to unpack with starpu_codelet_unpack_args():
 *            -# sopalin_data : common sopalin structure.
 *            -# cblk1 : Pointer to the structure representing the panel to
 *                       factorize in the cblktab array. Next column blok must
 *                       be accessible through cblk1[1].
 *            -# cblk2 : Pointer to the structure representing the panel to
 *                       factorize in the cblktab array. Next column blok must
 *                       be accessible through cblk2[1].
 *
 *******************************************************************************/
void
starpu_zgetrfsp1d_geadd_cpu( void * buffers[],
                             void * _args )
{
    z_Sopalin_Data_t     *sopalin_data;
    z_SolverCblk *cblk1, *cblk2;
    pastix_complex64_t *L    = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
    pastix_complex64_t *Cl   = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
    pastix_complex64_t *U    = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[2]);
    pastix_complex64_t *Cu   = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[3]);
    starpu_codelet_unpack_args(_args, &sopalin_data, &cblk1, &cblk2);

    core_zgeaddsp1d(sopalin_data->datacode,
                    cblk1,
                    cblk2,
                    L, Cl,
                    U, Cu);
}
void
starpu_zsytrfsp1d_syadd_cpu( void * buffers[],
                             void * _args )
{
    z_Sopalin_Data_t     *sopalin_data;
    z_SolverCblk *cblk1, *cblk2;
    pastix_complex64_t *L    = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
    pastix_complex64_t *Cl   = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
    pastix_complex64_t *U    = NULL;
    pastix_complex64_t *Cu   = NULL;
    starpu_codelet_unpack_args(_args, &sopalin_data, &cblk1, &cblk2);

    core_zgeaddsp1d(sopalin_data->datacode,
                    cblk1,
                    cblk2,
                    L, Cl,
                    U, Cu);
}
