From 5c6df2e2408daaa76d8a33f4af4552db90cc536b Mon Sep 17 00:00:00 2001
From: Raphael Boucherie <raphael.boucherie@inria.fr>
Date: Wed, 17 May 2017 16:13:24 +0200
Subject: [PATCH] added and modified files for testing of pzunmlq_param

---
 compute/CMakeLists.txt        |   2 +
 compute/pzunmlq_param.c       | 105 +++++-----
 compute/zunmlq_param.c        | 371 ++++++++++++++++++++++++++++++++++
 include/morse_z.h             |   3 +
 testing/testing_zgels_param.c |   2 +-
 5 files changed, 428 insertions(+), 55 deletions(-)
 create mode 100644 compute/zunmlq_param.c

diff --git a/compute/CMakeLists.txt b/compute/CMakeLists.txt
index d8bc6b1c5..c44695858 100644
--- a/compute/CMakeLists.txt
+++ b/compute/CMakeLists.txt
@@ -130,6 +130,7 @@ set(ZSRC
     pzungqr_param.c
     pzungqrrh.c
     pzunmlq.c
+    pzunmlq_param.c
     pzunmlqrh.c
     pzunmqr.c
     pzunmqr_param.c
@@ -177,6 +178,7 @@ set(ZSRC
     zungqr.c
     zungqr_param.c
     zunmlq.c
+    zunmlq_param.c
     zunmqr.c
     zunmqr_param.c
     ztpgqrt.c
diff --git a/compute/pzunmlq_param.c b/compute/pzunmlq_param.c
index 180e19c6d..6dbc50711 100644
--- a/compute/pzunmlq_param.c
+++ b/compute/pzunmlq_param.c
@@ -57,8 +57,8 @@ void morse_pzunmlq_param(const libhqr_tree_t *qrtree,
     MORSE_desc_t *D = NULL;
 
     int k, m, n, i, p;
-    int ldam, ldan, ldbm, ldak;
-    int tempnn, tempkmin, tempmm, tempkn;
+    int ldam, ldan, ldbm, ldak, ldbp;
+    int tempnn, tempkmin, tempmm, tempkn, tempkm;
     int ib, K;
     int *tiles;
 
@@ -300,7 +300,7 @@ void morse_pzunmlq_param(const libhqr_tree_t *qrtree,
                             MORSE_TASK_ztsmlq(
                                 &options,
                                 side, trans,
-                                tempmm, B->nb, tempmm, tempnn, tempkn, ib, TS->nb,
+                                tempmm, B->nb, tempmm, tempnn, tempkm, ib, TS->nb,
                                 B( p, n), ldbp,
                                 B( m, n), ldbm,
                                 A( k, n), ldak,
@@ -313,8 +313,8 @@ void morse_pzunmlq_param(const libhqr_tree_t *qrtree,
                             MORSE_TASK_zttmlq(
                                 &options,
                                 side, trans,
-                                tempmm, B->nb, tempmm, tempnn, tempkn, ib, TT->nb,
-                                B( m, p), ldbp,
+                                tempmm, B->nb, tempmm, tempnn, tempkm, ib, TT->nb,
+                                B( p, n), ldbp,
                                 B( m, n), ldbm,
                                 A( k, n), ldak,
                                 TT(k, n), TT->mb);
@@ -322,35 +322,35 @@ void morse_pzunmlq_param(const libhqr_tree_t *qrtree,
                     }
                 }
                 for (i = 0; i < qrtree->getnbgeqrf(qrtree, k); i++) {
-                    n = qrtree->getm(qrtree, k, i);
+                    m = qrtree->getm(qrtree, k, i);
 
-                    tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
-                    tempkmin = chameleon_min(tempnn, tempkn);
-                    ldan = BLKLDD(A, n);
+                    tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
+                    tempkmin = chameleon_min(tempmm, tempkm);
+                    ldbm = BLKLDD(B, m);
 
 #if defined(CHAMELEON_COPY_DIAG)
                     MORSE_TASK_zlacpy(
                         &options,
-                        MorseUpper, tempnn, tempkmin, A->nb,
-                        A(n, k), ldan,
-                        D(n, k), ldan );
+                        MorseUpper, tempkmin, tempkmm, A->nb,
+                        A(k, m), ldak,
+                        D(k, m), ldak );
 #if defined(CHAMELEON_USE_CUDA)
                     MORSE_TASK_zlaset(
                         &options,
-                        MorseLower, tempnn, tempkmin,
+                        MorseLower, tempkmin, tempkmm,
                         0., 1.,
-                        D(n, k), ldan );
+                        D(k, m), ldak );
 #endif
 #endif
-                    for (m = 0; m < B->mt; m++) {
+                    for (n = 0; n < B->nt; n++) {
                         ldbm = BLKLDD(B, m);
-                        tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
+                        tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
                         MORSE_TASK_zunmlq(
                             &options,
                             side, trans,
                             tempmm, tempnn, tempkmin, ib, TS->nb,
-                            D( n, k), ldan,
-                            TS(n, k), TS->mb,
+                            D( k, n), ldak,
+                            TS(k, n), TS->mb,
                             B( m, n), ldbm);
                     }
                 }
@@ -364,77 +364,74 @@ void morse_pzunmlq_param(const libhqr_tree_t *qrtree,
             for (k = 0; k < K; k++) {
                 RUNTIME_iteration_push(morse, k);
 
-                tempkn = k == B->nt-1 ? B->n-k*B->nb : B->nb;
-
+                tempkm = k == B->mt-1 ? B->m-k*B->mb : B->mb;
+                ldak = BLKLDD(A, k);
                 for (i = 0; i < qrtree->getnbgeqrf(qrtree, k); i++) {
-                    n = qrtree->getm(qrtree, k, i);
+                    m = qrtree->getm(qrtree, k, i);
 
-                    tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
-                    tempkmin = chameleon_min(tempnn, tempkn);
-                    ldan = BLKLDD(A, n);
+                    tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
+                    tempkmin = chameleon_min(tempmm, tempkm);
+                    ldbm = BLKLDD(B, m);
 
 #if defined(CHAMELEON_COPY_DIAG)
                     MORSE_TASK_zlacpy(
                         &options,
-                        MorseUpper, tempnn, tempkmin, A->nb,
-                        A(n, k), ldan,
-                        D(n, k), ldan );
+                        MorseUpper, tempkmin, tempkmm, A->nb,
+                        A(k, m), ldak,
+                        D(k, m), ldak );
 #if defined(CHAMELEON_USE_CUDA)
                     MORSE_TASK_zlaset(
                         &options,
-                        MorseLower, tempnn, tempkmin,
+                        MorseLower, tempkmin, tempkmm,
                         0., 1.,
-                        D(n, k), ldan );
+                        D(k, m), ldak );
 #endif
 #endif
-                    for (m = 0; m < B->mt; m++) {
-                        ldbm = BLKLDD(B, m);
-                        tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
+                    for (n = 0; n < B->nt; n++) {
+                        tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
                         MORSE_TASK_zunmlq(
                             &options,
                             side, trans,
                             tempmm, tempnn, tempkmin, ib, TS->nb,
-                            D( n, k), ldan,
-                            TS(n, k), TS->mb,
+                            D( k, m), ldan,
+                            TS(k, m), TS->mb,
                             B( m, n), ldbm);
                     }
                 }
                 /* Setting the order of tiles */
                 libhqr_treewalk(qrtree, k, tiles);
 
-                for (i = k; i < B->nt-1; n++) {
-                    n = tiles[i];
-                    p = qrtree->currpiv(qrtree, k, n);
+                for (i = k; i < B->mt-1; i++) {
+                    m = tiles[i];
+                    p = qrtree->currpiv(qrtree, k, m);
 
-                    tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
-                    ldan = BLKLDD(A, n);
+                    tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
+                    ldbm = BLKLDD(B, m);
                     ldbp = BLKLDD(B, p);
-                    if(qrtree->gettype(qrtree, k, n) == 0){
-                        for (m = 0; m < B->mt; m++) {
-                            tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
-                            ldbm = BLKLDD(B, m);
+                    if(qrtree->gettype(qrtree, k, m) == 0){
+                        for (n = 0; n < B->nt; n++) {
+                            tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
                             MORSE_TASK_ztsmlq(
                                 &options,
                                 side, trans,
-                                tempmm, B->nb, tempmm, tempnn, tempkn, ib, TS->nb,
-                                B( m, p), ldbm,
+                                tempmm, B->nb, tempmm, tempnn, tempkm, ib, TS->nb,
+                                B( p, n), ldbp,
                                 B( m, n), ldbm,
-                                A( n, k), ldan,
-                                TS(n, k), TS->mb);
+                                A( k, n), ldak,
+                                TS(k, n), TS->mb);
                         }
                     }
                     else {
-                        for (m = 0; m < B->mt; m++) {
-                            tempmm = m == B->mt-1 ? B->m-m*B->mb : B->mb;
-                            ldbm = BLKLDD(B, m);
+                        for (n = 0; n < B->nt; n++) {
+                            tempnn = n == B->nt-1 ? B->n-n*B->nb : B->nb;
                             MORSE_TASK_zttmlq(
                                 &options,
                                 side, trans,
-                                tempmm, B->nb, tempmm, tempnn, tempkn, ib, TT->nb,
-                                B( m, p), ldbm,
+                                tempmm, B->nb, tempmm, tempnn, tempkm, ib, TT->nb,
+                                B( p, n), ldbp,
                                 B( m, n), ldbm,
-                                A( n, k), ldan,
-                                TT(n, k), TT->mb);
+                                A( k, n), ldak,
+                                TT(k, n), TT->mb);
                         }
                     }
                 }
diff --git a/compute/zunmlq_param.c b/compute/zunmlq_param.c
new file mode 100644
index 000000000..3c47fa515
--- /dev/null
+++ b/compute/zunmlq_param.c
@@ -0,0 +1,371 @@
+/**
+ *
+ * @copyright (c) 2009-2014 The University of Tennessee and The University
+ *                          of Tennessee Research Foundation.
+ *                          All rights reserved.
+ * @copyright (c) 2012-2014 Inria. All rights reserved.
+ * @copyright (c) 2012-2014 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, Univ. Bordeaux. All rights reserved.
+ *
+ **/
+
+/**
+ *
+ * @file zunmlq_param.c
+ *
+ *  MORSE computational routines
+ *  MORSE is a software package provided by Univ. of Tennessee,
+ *  Univ. of California Berkeley and Univ. of Colorado Denver
+ *
+ * @version 2.5.0
+ * @comment This file has been automatically generated
+ *          from Plasma 2.5.0 for MORSE 1.0.0
+ * @author Hatem Ltaief
+ * @author Jakub Kurzak
+ * @author Dulceneia Becker
+ * @author Mathieu Faverge
+ * @author Emmanuel Agullo
+ * @author Cedric Castagnede
+ * @date 2010-11-15
+ * @precisions normal z -> s d c
+ *
+ **/
+#include "control/common.h"
+
+/**
+ *******************************************************************************
+ *
+ * @ingroup MORSE_Complex64_t
+ *
+ *  MORSE_zunmlq_param - Overwrites the general complex M-by-N matrix C with
+ *
+ *                  SIDE = 'L'     SIDE = 'R'
+ *  TRANS = 'N':      Q * C          C * Q
+ *  TRANS = 'C':      Q**H * C       C * Q**H
+ *
+ *  where Q is a complex unitary matrix defined as the product of k
+ *  elementary reflectors
+ *
+ *        Q = H(1) H(2) . . . H(k)
+ *
+ *  as returned by MORSE_zgeqrf. Q is of order M if SIDE = MorseLeft
+ *  and of order N if SIDE = MorseRight.
+ *
+ *******************************************************************************
+ *
+ * @param[in] side
+ *          Intended usage:
+ *          = MorseLeft:  apply Q or Q**H from the left;
+ *          = MorseRight: apply Q or Q**H from the right.
+ *
+ * @param[in] trans
+ *          Intended usage:
+ *          = MorseNoTrans:   no transpose, apply Q;
+ *          = MorseConjTrans: conjugate transpose, apply Q**H.
+ *
+ * @param[in] M
+ *          The number of rows of the matrix C. M >= 0.
+ *
+ * @param[in] N
+ *          The number of columns of the matrix C. N >= 0.
+ *
+ * @param[in] K
+ *          The number of rows of elementary tile reflectors whose product defines the matrix Q.
+ *          If side == MorseLeft,  M >= K >= 0.
+ *          If side == MorseRight, N >= K >= 0.
+ *
+ * @param[in] A
+ *          Details of the LQ factorization of the original matrix A as returned by MORSE_zgelqf.
+ *
+ * @param[in] LDA
+ *          The leading dimension of the array A. LDA >= max(1,K).
+ *
+ * @param[in] descT
+ *          Auxiliary factorization data, computed by MORSE_zgelqf.
+ *
+ * @param[in,out] C
+ *          On entry, the M-by-N matrix C.
+ *          On exit, C is overwritten by Q*C or Q**H*C.
+ *
+ * @param[in] LDC
+ *          The leading dimension of the array C. LDC >= max(1,M).
+ *
+ *******************************************************************************
+ *
+ * @return
+ *          \retval MORSE_SUCCESS successful exit
+ *          \retval <0 if -i, the i-th argument had an illegal value
+ *
+ *******************************************************************************
+ *
+ * @sa MORSE_zunmlq_param_Tile
+ * @sa MORSE_zunmlq_param_Tile_Async
+ * @sa MORSE_cunmlq
+ * @sa MORSE_dormlq
+ * @sa MORSE_sormlq
+ * @sa MORSE_zgelqf
+ *
+ ******************************************************************************/
+int MORSE_zunmlq_param(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, int M, int N, int K,
+                       MORSE_Complex64_t *A, int LDA,
+                       MORSE_desc_t *descTS, MORSE_desc_t *descTT,
+                       MORSE_Complex64_t *C, int LDC)
+{
+    int NB, An;
+    int status;
+    MORSE_context_t *morse;
+    MORSE_sequence_t *sequence = NULL;
+    MORSE_request_t request = MORSE_REQUEST_INITIALIZER;
+    MORSE_desc_t descA, descC;
+
+    morse = morse_context_self();
+    if (morse == NULL) {
+        morse_fatal_error("MORSE_zunmlq_param", "MORSE not initialized");
+        return MORSE_ERR_NOT_INITIALIZED;
+    }
+
+    if (side == MorseLeft)
+        An = M;
+    else
+        An = N;
+
+    /* Check input arguments */
+    if ((side != MorseLeft) && (side != MorseRight)) {
+        morse_error("MORSE_zunmlq_param", "illegal value of side");
+        return -1;
+    }
+    if ((trans != MorseConjTrans) && (trans != MorseNoTrans)){
+        morse_error("MORSE_zunmlq_param", "illegal value of trans");
+        return -2;
+    }
+    if (M < 0) {
+        morse_error("MORSE_zunmlq_param", "illegal value of M");
+        return -3;
+    }
+    if (N < 0) {
+        morse_error("MORSE_zunmlq_param", "illegal value of N");
+        return -4;
+    }
+    if ((K < 0) || (K > An)) {
+        morse_error("MORSE_zunmlq_param", "illegal value of K");
+        return -5;
+    }
+    if (LDA < chameleon_max(1, K)) {
+        morse_error("MORSE_zunmlq_param", "illegal value of LDA");
+        return -7;
+    }
+    if (LDC < chameleon_max(1, M)) {
+        morse_error("MORSE_zunmlq_param", "illegal value of LDC");
+        return -10;
+    }
+    /* Quick return - currently NOT equivalent to LAPACK's:
+     * CALL DLASET( 'Full', MAX( M, N ), NRHS, ZERO, ZERO, C, LDC ) */
+    if (chameleon_min(M, chameleon_min(N, K)) == 0)
+        return MORSE_SUCCESS;
+
+    /* Tune NB & IB depending on M, N & NRHS; Set NBNB */
+    status = morse_tune(MORSE_FUNC_ZGELS, M, K, N);
+    if (status != MORSE_SUCCESS) {
+        morse_error("MORSE_zunmlq_param", "morse_tune() failed");
+        return status;
+    }
+
+    /* Set MT, NT & NTRHS */
+    NB   = MORSE_NB;
+    morse_sequence_create(morse, &sequence);
+
+/*    if ( MORSE_TRANSLATION == MORSE_OUTOFPLACE ) {*/
+        morse_zooplap2tile( descA, A, NB, NB, LDA, An, 0, 0, K, An, sequence, &request,
+                             morse_desc_mat_free(&(descA)) );
+        morse_zooplap2tile( descC, C, NB, NB, LDC, N, 0, 0, M,  N, sequence, &request,
+                             morse_desc_mat_free(&(descA)); morse_desc_mat_free(&(descC)));
+/*    } else {*/
+/*        morse_ziplap2tile( descA, A, NB, NB, LDA, An, 0, 0, K, An,*/
+/*                            sequence, &request);*/
+/*        morse_ziplap2tile( descC, C, NB, NB, LDC, N, 0, 0, M,  N,*/
+/*                            sequence, &request);*/
+/*    }*/
+
+    /* Call the tile interface */
+    MORSE_zunmlq_param_Tile_Async(
+        qrtree, side, trans, &descA, descTS, descTT, &descC, sequence, &request);
+
+/*    if ( MORSE_TRANSLATION == MORSE_OUTOFPLACE ) {*/
+        morse_zooptile2lap(descC, C, NB, NB, LDC, N,  sequence, &request);
+        morse_sequence_wait(morse, sequence);
+        morse_desc_mat_free(&descA);
+        morse_desc_mat_free(&descC);
+/*    } else {*/
+/*        morse_ziptile2lap( descA, A, NB, NB, LDA, An, sequence, &request);*/
+/*        morse_ziptile2lap( descC, C, NB, NB, LDC, N,  sequence, &request);*/
+/*        morse_sequence_wait(morse, sequence);*/
+/*    }*/
+
+    status = sequence->status;
+    morse_sequence_destroy(morse, sequence);
+    return status;
+}
+
+/**
+ *******************************************************************************
+ *
+ * @ingroup MORSE_Complex64_t_Tile
+ *
+ *  MORSE_zunmlq_param_Tile - overwrites the general M-by-N matrix C with Q*C, where Q is an orthogonal
+ *  matrix (unitary in the complex case) defined as the product of elementary reflectors returned
+ *  by MORSE_zgelqf_Tile Q is of order M.
+ *  All matrices are passed through descriptors. All dimensions are taken from the descriptors.
+ *
+ *******************************************************************************
+ *
+ * @param[in] side
+ *          Intended usage:
+ *          = MorseLeft:  apply Q or Q**H from the left;
+ *          = MorseRight: apply Q or Q**H from the right.
+ *          Currently only MorseLeft is supported.
+ *
+ * @param[in] trans
+ *          Intended usage:
+ *          = MorseNoTrans:   no transpose, apply Q;
+ *          = MorseConjTrans: conjugate transpose, apply Q**H.
+ *          Currently only MorseConjTrans is supported.
+ *
+ * @param[in] A
+ *          Details of the LQ factorization of the original matrix A as returned by MORSE_zgelqf.
+ *
+ * @param[in] T
+ *          Auxiliary factorization data, computed by MORSE_zgelqf.
+ *
+ * @param[in,out] C
+ *          On entry, the M-by-N matrix C.
+ *          On exit, C is overwritten by Q*C or Q**H*C.
+ *
+ *******************************************************************************
+ *
+ * @return
+ *          \retval MORSE_SUCCESS successful exit
+ *
+ *******************************************************************************
+ *
+ * @sa MORSE_zunmlq_param
+ * @sa MORSE_zunmlq_param_Tile_Async
+ * @sa MORSE_cunmlq_Tile
+ * @sa MORSE_dormlq_Tile
+ * @sa MORSE_sormlq_Tile
+ * @sa MORSE_zgelqf_Tile
+ *
+ ******************************************************************************/
+int MORSE_zunmlq_param_Tile(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans,
+                            MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *C)
+{
+    MORSE_context_t *morse;
+    MORSE_sequence_t *sequence = NULL;
+    MORSE_request_t request = MORSE_REQUEST_INITIALIZER;
+    int status;
+
+    morse = morse_context_self();
+    if (morse == NULL) {
+        morse_fatal_error("MORSE_zunmlq_param_Tile", "MORSE not initialized");
+        return MORSE_ERR_NOT_INITIALIZED;
+    }
+    morse_sequence_create(morse, &sequence);
+    MORSE_zunmlq_param_Tile_Async(qrtree, side, trans, A, TS, TT, C, sequence, &request);
+    morse_sequence_wait(morse, sequence);
+    RUNTIME_desc_getoncpu(A);
+        RUNTIME_desc_getoncpu(C);
+
+    status = sequence->status;
+    morse_sequence_destroy(morse, sequence);
+    return status;
+}
+
+/**
+ *******************************************************************************
+ *
+ * @ingroup MORSE_Complex64_t_Tile_Async
+ *
+ *  Non-blocking equivalent of MORSE_zunmlq_param_Tile().
+ *  May return before the computation is finished.
+ *  Allows for pipelining of operations at runtime.
+ *
+ *******************************************************************************
+ *
+ * @param[in] sequence
+ *          Identifies the sequence of function calls that this call belongs to
+ *          (for completion checks and exception handling purposes).
+ *
+ * @param[out] request
+ *          Identifies this function call (for exception handling purposes).
+ *
+ *******************************************************************************
+ *
+ * @sa MORSE_zunmlq_param
+ * @sa MORSE_zunmlq_param_Tile
+ * @sa MORSE_cunmlq_Tile_Async
+ * @sa MORSE_dormlq_Tile_Async
+ * @sa MORSE_sormlq_Tile_Async
+ * @sa MORSE_zgelqf_Tile_Async
+ *
+ ******************************************************************************/
+int MORSE_zunmlq_param_Tile_Async(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans,
+                                  MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *C,
+                                  MORSE_sequence_t *sequence, MORSE_request_t *request)
+{
+    MORSE_context_t *morse;
+
+    morse = morse_context_self();
+    if (morse == NULL) {
+        morse_fatal_error("MORSE_zunmlq_param_Tile", "MORSE not initialized");
+        return MORSE_ERR_NOT_INITIALIZED;
+    }
+    if (sequence == NULL) {
+        morse_fatal_error("MORSE_zunmlq_param_Tile", "NULL sequence");
+        return MORSE_ERR_UNALLOCATED;
+    }
+    if (request == NULL) {
+        morse_fatal_error("MORSE_zunmlq_param_Tile", "NULL request");
+        return MORSE_ERR_UNALLOCATED;
+    }
+    /* Check sequence status */
+    if (sequence->status == MORSE_SUCCESS)
+        request->status = MORSE_SUCCESS;
+    else
+        return morse_request_fail(sequence, request, MORSE_ERR_SEQUENCE_FLUSHED);
+
+    /* Check descriptors for correctness */
+    if (morse_desc_check(A) != MORSE_SUCCESS) {
+        morse_error("MORSE_zunmlq_param_Tile", "invalid first descriptor");
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    if (morse_desc_check(TS) != MORSE_SUCCESS) {
+        morse_error("MORSE_zunmlq_param_Tile", "invalid second descriptor");
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    if (morse_desc_check(TT) != MORSE_SUCCESS) {
+        morse_error("MORSE_zunmlq_param_Tile", "invalid third descriptor");
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    if (morse_desc_check(C) != MORSE_SUCCESS) {
+        morse_error("MORSE_zunmlq_param_Tile", "invalid fourth descriptor");
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    /* Check input arguments */
+    if (A->nb != A->mb || C->nb != C->mb) {
+        morse_error("MORSE_zunmlq_param_Tile", "only square tiles supported");
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    if ((side != MorseLeft) && (side != MorseRight)) {
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    if ((trans != MorseConjTrans) && (trans != MorseNoTrans)){
+        return morse_request_fail(sequence, request, MORSE_ERR_ILLEGAL_VALUE);
+    }
+    /* Quick return - currently NOT equivalent to LAPACK's:
+     * CALL DLASET( 'Full', MAX( M, N ), NRHS, ZERO, ZERO, C, LDC ) */
+/*
+    if (chameleon_min(M, chameleon_min(N, K)) == 0)
+        return MORSE_SUCCESS;
+*/
+    morse_pzunmlq_param(qrtree, side, trans, A, C, TS, TT, sequence, request);
+
+    return MORSE_SUCCESS;
+}
diff --git a/include/morse_z.h b/include/morse_z.h
index 8e3c3b399..b1784bf39 100644
--- a/include/morse_z.h
+++ b/include/morse_z.h
@@ -292,6 +292,9 @@ int MORSE_zgeqrs_param_Tile_Async(const libhqr_tree_t *qrtree, MORSE_desc_t *A,
 int MORSE_zungqr_param(const libhqr_tree_t *qrtree, int M, int N, int K, MORSE_Complex64_t *A, int LDA, MORSE_desc_t *descTS, MORSE_desc_t *descTT, MORSE_Complex64_t *B, int LDB);
 int MORSE_zungqr_param_Tile(const libhqr_tree_t *qrtree, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B);
 int MORSE_zungqr_param_Tile_Async(const libhqr_tree_t *qrtree, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B, MORSE_sequence_t *sequence, MORSE_request_t *request);
+int MORSE_zunmlq_param(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, int M, int N, int K, MORSE_Complex64_t *A, int LDA, MORSE_desc_t *descT, MORSE_desc_t *TT, MORSE_Complex64_t *B, int LDB);
+int MORSE_zunmlq_param_Tile(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B);
+int MORSE_zunmlq_param_Tile_Async(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B, MORSE_sequence_t *sequence, MORSE_request_t *request);
 int MORSE_zunmqr_param(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, int M, int N, int K, MORSE_Complex64_t *A, int LDA, MORSE_desc_t *descTS, MORSE_desc_t *TT, MORSE_Complex64_t *B, int LDB);
 int MORSE_zunmqr_param_Tile(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B);
 int MORSE_zunmqr_param_Tile_Async(const libhqr_tree_t *qrtree, MORSE_enum side, MORSE_enum trans, MORSE_desc_t *A, MORSE_desc_t *TS, MORSE_desc_t *TT, MORSE_desc_t *B, MORSE_sequence_t *sequence, MORSE_request_t *request);
diff --git a/testing/testing_zgels_param.c b/testing/testing_zgels_param.c
index abc8c4449..6df2515fc 100644
--- a/testing/testing_zgels_param.c
+++ b/testing/testing_zgels_param.c
@@ -293,7 +293,7 @@ int testing_zgels_param(int argc, char **argv)
         MORSE_zgelqf_param(&qrtree, M, N, A2, LDA, TS, TT);
         MORSE_ztrsm(MorseLeft, MorseLower, MorseNoTrans, MorseNonUnit, M, NRHS, 1.0, A2, LDA, B2, LDB);
         MORSE_zunglq(M, N, K, A2, LDA, TS, Q, LDA);
-        MORSE_zunmlq(MorseLeft, MorseConjTrans, N, NRHS, M, A2, LDA, TS, B2, LDB);
+        MORSE_zunmlq_param(&qrtree, MorseLeft, MorseConjTrans, N, NRHS, M, A2, LDA, TS, TT, B2, LDB);
     }
 
     /* Check the orthogonality, factorization and the solution */
-- 
GitLab