/*  
    -- micMAGMA (version 0.1) --
       Univ. of Tennessee, Knoxville
       Univ. of California, Berkeley
       Univ. of Colorado, Denver
       April 2012

       @generated c Fri Nov 22 14:21:14 2013

*/

#include <stdio.h>
#include "common_magma.h"

magma_int_t
magma_cgels_mic( magma_trans_t trans, magma_int_t m, magma_int_t n, magma_int_t nrhs,
                 magmaFloatComplex_ptr dA, size_t dA_offset,  magma_int_t ldda, 
                 magmaFloatComplex_ptr dB, size_t dB_offset,  magma_int_t lddb, 
                 magmaFloatComplex *hwork, magma_int_t lwork, 
                 magma_int_t *info, magma_queue_t queue )
{
/*  -- micMagma (version 0.1) --
       Univ. of Tennessee, Knoxville
       Univ. of California, Berkeley
       Univ. of Colorado, Denver
       April 2012

    Purpose
    =======
    Solves the overdetermined, least squares problem
           min || A*X - C ||
    using the QR factorization A.
    The underdetermined problem (m < n) is not currently handled.


    Arguments
    =========
    TRANS   (input) CHARACTER*1
            = 'N': the linear system involves A.
            Only trans='N' is currently handled.

    M       (input) INTEGER
            The number of rows of the matrix A. M >= 0.

    N       (input) INTEGER
            The number of columns of the matrix A. M >= N >= 0.

    NRHS    (input) INTEGER
            The number of columns of the matrix C. NRHS >= 0.

    A       (input/output) COMPLEX array, dimension (LDA,N)
            On entry, the M-by-N matrix A.
            On exit, A is overwritten by details of its QR
            factorization as returned by CGEQRF.

    LDDA    (input) INTEGER
            The leading dimension of the array A, LDDA >= M.

    DB      (input/output) COMPLEX array on the MIC, dimension (LDDB,NRHS)
            On entry, the M-by-NRHS matrix C.
            On exit, the N-by-NRHS solution matrix X.

    LDDB    (input) INTEGER
            The leading dimension of the array DB. LDDB >= M.

    HWORK   (workspace/output) COMPLEX array, dimension MAX(1,LWORK).
            On exit, if INFO = 0, HWORK(1) returns the optimal LWORK.

    LWORK   (input) INTEGER
            The dimension of the array HWORK, LWORK >= max(1,NRHS).
            For optimum performance LWORK >= (M-N+NB)*(NRHS + 2*NB), where 
            NB is the blocksize given by magma_get_cgeqrf_nb( M ).

            If LWORK = -1, then a workspace query is assumed; the routine
            only calculates the optimal size of the HWORK array, returns
            this value as the first entry of the HWORK array.

    INFO    (output) INTEGER
            = 0:  successful exit
            < 0:  if INFO = -i, the i-th argument had an illegal value
    =====================================================================    */

   	#define a_ref(a_1,a_2) dA, (dA_offset+(a_1)+(a_2)*(ldda))

	magmaFloatComplex_ptr dT;
	magmaFloatComplex *tau;
	magma_int_t k;

	magma_int_t nb     = magma_get_cgeqrf_nb(m);
	magma_int_t lwkopt = (m - n + nb) * (nrhs + nb) + nrhs * nb;
	int lquery = (lwork == -1);

	hwork[0] = MAGMA_C_MAKE( (float)lwkopt, 0. );

	*info = 0;
	/* For now, N is the only case working */
	if ( trans != MagmaNoTrans )
		*info = -1;
	else if (m < 0)
		*info = -2;
	else if (n < 0 || m < n) /* LQ is not handle for now*/
		*info = -3;
	else if (nrhs < 0)
		*info = -4;
	else if (ldda < max(1,m))
		*info = -6;
	else if (lddb < max(1,m))
		*info = -8;
	else if (lwork < lwkopt && ! lquery) {
		*info = -10;
	}

	if (*info != 0) {
		magma_xerbla( __func__, -(*info) );
        return *info;
	}
	else if (lquery)
		return *info;

	k = min(m,n);
	if (k == 0) {
		hwork[0] = MAGMA_C_ONE;
		return *info;
	}

	/*
	 * Allocate temporary buffers
	 */
	int ldtwork = ( 2*k + ((n+31)/32)*32 )*nb;
	if (nb < nrhs)
		ldtwork = ( 2*k + ((n+31)/32)*32 )*nrhs;
	if (MAGMA_SUCCESS != magma_cmalloc( &dT, ldtwork )) {
		*info = MAGMA_ERR_DEVICE_ALLOC;
		return *info;
	}
    
	tau = (magmaFloatComplex*) malloc( k * sizeof(magmaFloatComplex) );
	if( tau == NULL ) {
		magma_free( dT );
		*info = MAGMA_ERR_HOST_ALLOC;
		return *info;
	}

	size_t dT_offset = 0;
	magma_cgeqrf_mic( m, n, dA, dA_offset, ldda, tau, dT, dT_offset, info, queue );

	if ( *info == 0 ) {
		magma_cgeqrs_mic( m, n, nrhs, 
                          dA, dA_offset, ldda, tau, dT, dT_offset, 
                          dB, dB_offset, lddb, hwork, lwork, info, queue );
	}
    
	magma_free( dT );
	free(tau);
	return *info;
}

#undef a_ref

