/*!
 * @file libharkio3_matrix.c
 *
 * @author Takeshi Mizumoto
 * @version 0.1
 * @brief  harkio_Matrix の API群。 行列の生成、操作、メモリ解放などを行う。
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "libharkio3.h"

/*----------------------------------------------------------------------------*/
/* Generation                                                                 */
/*----------------------------------------------------------------------------*/

/*!
 * @brief 初期化しないコンストラクタ
 * @param[in] type 　　　harkio_Matrix の型。 int32, float32, complex から選べる。
 * @param[in] rows     harkio_Matrix の行サイズ。
 * @param[in] cols     harkio_Matrix の列サイズ。
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_new(char *type, int rows, int cols) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (rows <= 0 || cols <= 0) {
        harkio_Log_printf(harkio_Log_Error, "%s rows[%d] cols[%d]", "rows/cols is negative", rows, cols);
        return NULL;
    }

    if (harkio_CheckType(type) == -1) {
        harkio_Log_printf(harkio_Log_Error, "type is invalid value [%s]\n", type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *m = (harkio_Matrix*) malloc(sizeof(harkio_Matrix));

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Memory allocation failed");
        return NULL;
    }

    switch (harkio_CheckType(type)) {
    case harkio_DataType_INT32:
        m->data.intdata = (int*) malloc(sizeof(int) * rows * cols);

        if (m->data.intdata == NULL) {
            harkio_Log_printf(harkio_Log_Error, "Memory allocation failed");
            free(m);
            return NULL;
        }
        break;

    case harkio_DataType_FLOAT32:
        m->data.floatdata = (float*) malloc(sizeof(float) * rows * cols);

        if (m->data.floatdata == NULL) {
            harkio_Log_printf(harkio_Log_Error, "Memory allocation failed");
            free(m);
            return NULL;
        }
        break;

    case harkio_DataType_COMPLEX:
        m->data.complexdata = (COMPLEX_TYPE*) malloc(sizeof(COMPLEX_TYPE) * rows * cols);

        if (m->data.complexdata == NULL) {
            harkio_Log_printf(harkio_Log_Error, "Memory allocation failed");
            free(m);
            return NULL;
        }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is invalid");
        free(m);
        return NULL;
    }

    strcpy(m->tag, HARKIO_TAG);
    strcpy(m->type, type);
    m->dim = HARK_DIM;
    m->rows = rows;
    m->cols = cols;
    return m;
}

/*!
 * @brief 要素が全てゼロの行列を生成するコンストラクタ
 * @param[in] type 　　　harkio_Matrix の型。 int32, float32, complex から選べる。
 * @param[in] rows     harkio_Matrix の行サイズ。
 * @param[in] cols     harkio_Matrix の列サイズ。
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_zeros(char *type, int rows, int cols) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (rows <= 0 || cols <= 0) {
        harkio_Log_printf(harkio_Log_Error, "rows or cols is negative : rows[%d] cols[%d]", rows, cols);
        return NULL;
    }

    if (harkio_CheckType(type) == -1) {
        harkio_Log_printf(harkio_Log_Error, "type is invalid [%s]", type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *m = harkio_Matrix_new(type, rows, cols);

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            switch (harkio_CheckType(type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_setValueInt32(m, i, j, 0);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_setValueFloat32(m, i, j, 0);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_setValueComplex(m, i, j, 0);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return NULL;
            }
        }
    }

    return m;
}

/*!
 * @brief 要素が全て1の行列を生成するコンストラクタ
 * @param[in] type 　　　harkio_Matrix の型。 int32, float32, complex から選べる。
 * @param[in] rows     harkio_Matrix の行サイズ。
 * @param[in] cols     harkio_Matrix の列サイズ。
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_ones(char *type, int rows, int cols) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (rows <= 0 || cols <= 0) {
        harkio_Log_printf(harkio_Log_Error, "rows/cols is Illegal value : rows[%d] cols[%d]\n", rows, cols);
        return NULL;
    }

    if (harkio_CheckType(type) == -1) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : type[%s]\n", type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *m = harkio_Matrix_new(type, rows, cols);

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            switch (harkio_CheckType(type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_setValueInt32(m, i, j, 1);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_setValueFloat32(m, i, j, 1);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_setValueComplex(m, i, j, 1);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return NULL;
            }
        }
    }

    return m;
}

/*!
 * @brief srcにstackの内容を縦方向(rows方向)にスタックする。この際、srcの容量はstack分拡張される。
 * @param[in] src vstackする対象のharkio_Matrixインスタンス
 * @param[in] stack vstackに加えるharkio_Matrixのインスタンス
 * @return harkio_Matrix 型のポインタ。srcと同じ
 * @note srcとstackのカラム数は等しくならなければならない
 */
harkio_Matrix* harkio_Matrix_vstack(harkio_Matrix* src, harkio_Matrix* stack) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    if (!src || !stack || src->cols != stack->cols) {
        harkio_Log_printf(harkio_Log_Error, "the shape of src and stack is different.");
        return NULL;
    }
    if (strcmp(src->type, stack->type)) {
        harkio_Log_printf(harkio_Log_Error, "the data type of src and stack is different.");
        return NULL;
    }

    switch (harkio_CheckType(src->type)) {
    case harkio_DataType_INT32: {
        int* ptr;
        if (!(ptr = (int*) realloc(src->data.intdata, sizeof(int) * (src->rows + stack->rows) * src->cols))) {
            harkio_Log_printf(harkio_Log_Error, "failed to reallocate int binary in libharkio_Matrix");
            return NULL;
        }
        memcpy(ptr + src->rows * src->cols, stack->data.intdata, stack->rows * stack->cols * sizeof(int));
        src->data.intdata = ptr;
    }
        break;

    case harkio_DataType_FLOAT32: {
        float* ptr;
        if (!(ptr = (float*) realloc(src->data.floatdata, sizeof(float) * (src->rows + stack->rows) * src->cols))) {
            harkio_Log_printf(harkio_Log_Error, "failed to reallocate float binary in libharkio_Matrix");
            return NULL;
        }
        memcpy(ptr + src->rows * src->cols, stack->data.floatdata, stack->rows * stack->cols * sizeof(float));

        src->data.floatdata = ptr;
    }
        break;

    case harkio_DataType_COMPLEX: {
        COMPLEX_TYPE* ptr;
        if (!(ptr = (COMPLEX_TYPE*) realloc(src->data.complexdata,
                sizeof(COMPLEX_TYPE) * (src->rows + stack->rows) * src->cols))) {
            harkio_Log_printf(harkio_Log_Error, "failed to reallocate complex binary in libharkio_Matrix");
            return NULL;
        }
        memcpy(ptr + src->rows * src->cols, stack->data.complexdata, stack->rows * stack->cols * sizeof(COMPLEX_TYPE));
        src->data.complexdata = ptr;
    }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return NULL;
    }

    src->rows = src->rows + stack->rows;
    return src;
}

/*!
 * @brief 単位行列生成するコンストラクタ
 * @param[in] type 　　　harkio_Matrix の型。 int32, float32, complex から選べる。
 * @param[in] size     harkio_Matrix のサイズ。
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 単位行列は正方行列なので、列・行は共に size と等しい。
 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_eye(char *type, int size) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (size <= 0) {
        harkio_Log_printf(harkio_Log_Error, "size is Illegal value : size[%d]\n", size);
        return NULL;
    }

    if (harkio_CheckType(type) == -1) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : type[%s]\n", type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *m = harkio_Matrix_zeros(type, size, size);

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < size; i++) {
        switch (harkio_CheckType(type)) {
        case harkio_DataType_INT32:
            harkio_Matrix_setValueInt32(m, i, i, 1);
            break;

        case harkio_DataType_FLOAT32:
            harkio_Matrix_setValueFloat32(m, i, i, 1);
            break;

        case harkio_DataType_COMPLEX:
            harkio_Matrix_setValueComplex(m, i, i, 1);
            break;

        default:
            harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
            return NULL;
        }
    }

    return m;
}

/*!
 * @brief harkio_Matrix_write で書き込んだファイルを読み込む。
 * @param[in] path  ファイルへのパス。
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_fromFile(char *path) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    char tag[32], type[32];
    int dim, cols, rows;
    FILE *fp;
    int intValue;
    float floatValue;
    COMPLEX_TYPE complexValue;
    harkio_Matrix *m;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (path == NULL || (fp = fopen(path, "r")) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "File Open Failed");
        return NULL;
    }

    fread(tag, sizeof(char), 32, fp);
    if (strncmp(tag, HARKIO_TAG, strlen(HARKIO_TAG)) != 0) {
        harkio_Log_printf(harkio_Log_Error, "tag (%s) is not %s.", tag, HARKIO_TAG);
        fclose(fp);
        return NULL;
    }

    fread(type, sizeof(char), 32, fp);
    switch (harkio_CheckType(type)) {
    case harkio_DataType_INT32:
        strcpy(type, "int32");
        break;
    case harkio_DataType_FLOAT32:
        strcpy(type, "float32");
        break;
    case harkio_DataType_COMPLEX:
        strcpy(type, "complex");
        break;
    default:
        harkio_Log_printf(harkio_Log_Error, "Wrong type: Unknown matrix type (%s)", type);
        fclose(fp);
        return NULL;
    }

    fread(&dim, sizeof(int), 1, fp);

    if (dim != HARK_DIM) {
        harkio_Log_printf(harkio_Log_Error, "dim is Illegal value");
        fclose(fp);
        return NULL;
    }

    fread(&cols, sizeof(int), 1, fp);
    fread(&rows, sizeof(int), 1, fp);

    //new matrix
    m = harkio_Matrix_zeros(type, cols, rows);

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        fclose(fp);
        return NULL;
    }

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            switch (harkio_CheckType(type)) {
            case harkio_DataType_INT32:
                fread(&intValue, sizeof(int), 1, fp);
                harkio_Matrix_setValueInt32(m, i, j, intValue);
                break;

            case harkio_DataType_FLOAT32:
                fread(&floatValue, sizeof(float), 1, fp);
                harkio_Matrix_setValueFloat32(m, i, j, floatValue);
                break;

            case harkio_DataType_COMPLEX:
                fread(&complexValue, sizeof(COMPLEX_TYPE), 1, fp);
                harkio_Matrix_setValueComplex(m, i, j, complexValue);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                fclose(fp);
                return NULL;
            }
        }
    }

    fclose(fp);
    // harkio_Matrix_print(m);
    return m;
}

/*!
 * @brief [内部用] zip ファイル内のharkio_Matrix_write で書き込んだファイルを読み込む。
 * @param[in] handle  Zip ファイルへのハンドル
 * @param[in] path    対象ファイルの Zip ファイル内のパス
 * @return harkio_Matrix 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_Matrix_delete でメモリ解放を行う。
 */
harkio_Matrix* harkio_Matrix_fromZip(struct zip *handle, char *path) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    char tag[32], type[32];
    int dim, cols, rows;
    int intValue;
    float floatValue;
    COMPLEX_TYPE complexValue;
    harkio_Matrix *m;

    struct zip_file *fp;   // file pointer for a file in zip

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (path == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Path is NULL");
        return NULL;
    }

    // file open
    if ((fp = zip_fopen(handle, path, 0)) == NULL) {
        harkio_Log_FileOpenFailed(path);
        return NULL;
    }

    // tag check
    zip_fread(fp, tag, sizeof(char) * 32);
    if (strncmp(tag, HARKIO_TAG, strlen(HARKIO_TAG)) != 0) {
        harkio_Log_printf(harkio_Log_Error, "tag (%s) is not %s.", tag, HARKIO_TAG);
        zip_fclose(fp);
        return NULL;
    }

    // type check
    zip_fread(fp, type, sizeof(char) * 32);
    switch (harkio_CheckType(type)) {
    case harkio_DataType_INT32:
        strcpy(type, "int32");
        break;
    case harkio_DataType_FLOAT32:
        strcpy(type, "float32");
        break;
    case harkio_DataType_COMPLEX:
        strcpy(type, "complex");
        break;
    default:
        harkio_Log_printf(harkio_Log_Error, "Wrong type: Unknown matrix type (%s)", type);
        zip_fclose(fp);
        return NULL;
    }

    // dim check
    zip_fread(fp, &dim, sizeof(int));
    if (dim != HARK_DIM) {
        harkio_Log_printf(harkio_Log_Error, "dim is Illegal value");
        zip_fclose(fp);
        return NULL;
    }

    zip_fread(fp, &cols, sizeof(int));
    zip_fread(fp, &rows, sizeof(int));

    //new matrix
    m = harkio_Matrix_zeros(type, cols, rows);

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        zip_fclose(fp);
        return NULL;
    }

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            switch (harkio_CheckType(type)) {
            case harkio_DataType_INT32:
                zip_fread(fp, &intValue, sizeof(int));
                harkio_Matrix_setValueInt32(m, i, j, intValue);
                break;

            case harkio_DataType_FLOAT32:
                zip_fread(fp, &floatValue, sizeof(float));
                harkio_Matrix_setValueFloat32(m, i, j, floatValue);
                break;

            case harkio_DataType_COMPLEX:
                zip_fread(fp, &complexValue, sizeof(COMPLEX_TYPE));
                harkio_Matrix_setValueComplex(m, i, j, complexValue);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                zip_fclose(fp);
                return NULL;
            }
        }
    }

    zip_fclose(fp);
    // harkio_Matrix_print(m);
    return m;
}

/*!
 * @brief デストラクタ。
 * @param[in] m  解放したい harkio_Matrix オブジェクト
 */
void harkio_Matrix_delete(harkio_Matrix **m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL || *m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    switch (harkio_CheckType((*m)->type)) {
    case harkio_DataType_INT32:
        if ((*m)->data.intdata != NULL) {
            free((*m)->data.intdata);
            (*m)->data.intdata = NULL;
        }
        break;

    case harkio_DataType_FLOAT32:
        if ((*m)->data.floatdata != NULL) {
            free((*m)->data.floatdata);
            (*m)->data.floatdata = NULL;
        }
        break;

    case harkio_DataType_COMPLEX:
        if ((*m)->data.complexdata != NULL) {
            free((*m)->data.complexdata);
            (*m)->data.complexdata = NULL;
        }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
    }

    free(*m);
    *m = NULL;
}

/*!
 * @brief harkio_Matrix をコピーする。
 * @param[in] m  コピーしたい harkio_Matrix オブジェクト
 * @return 内容をコピーした harkio_Matrix オブジェクト。
 */
harkio_Matrix* harkio_Matrix_copy(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int size[2];

    harkio_Matrix_size(m, size);
    return harkio_Matrix_getValues(m, 0, size[0] - 1, 0, size[1] - 1);
}

/*----------------------------------------------------------------------------*/
/* Operation                                                                  */
/*----------------------------------------------------------------------------*/

/*!
 * @brief 2 つの harkio_Matrix の和を求める。
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return  要素ごとの和を要素に持つ新しい harkio_Matrix オブジェクト。失敗すれば NULL
 */
harkio_Matrix* harkio_Matrix_add(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    harkio_Matrix* result;
    int intValuea, intValueb;
    float floatValuea, floatValueb;
    COMPLEX_TYPE complexValuea, complexValueb;
    // COMPLEX_TYPE  calcValue;
    // int   retValue;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (a->rows != b->rows || a->cols != b->cols || strcmp(a->type, b->type)) {
        harkio_Log_printf(harkio_Log_Error,
                "rows/cols/type is Illegal value : a->rows[%d] : b->rows[%d] : a->cols[%d] : b->cols[%d] : a->type[%s] : b->type[%s]\n",
                a->rows, b->rows, a->cols, b->cols, a->type, b->type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    result = harkio_Matrix_new(a->type, a->rows, a->cols);

    if (result == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < a->rows; i++) {
        for (j = 0; j < a->cols; j++) {
            switch (harkio_CheckType(a->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(a, i, j, &intValuea);
                harkio_Matrix_getValueInt32(b, i, j, &intValueb);
                harkio_Matrix_setValueInt32(result, i, j, intValuea + intValueb);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(a, i, j, &floatValuea);
                harkio_Matrix_getValueFloat32(b, i, j, &floatValueb);
                harkio_Matrix_setValueFloat32(result, i, j, floatValuea + floatValueb);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(a, i, j, &complexValuea);
                harkio_Matrix_getValueComplex(b, i, j, &complexValueb);
                harkio_Matrix_setValueComplex(result, i, j, complexValuea + complexValueb);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is wrong (%s)", a->type);
                harkio_Matrix_delete(&result);
                return NULL;
            }
        }
    }

    return result;
}

/*!
 * @brief 2 つの harkio_Matrix の和を求める。(高速版)
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @details 要素ごとの a と b の和を harkio_Matrix オブジェクト a に代入する。
 メモリを確保しないため速度は速いが、変数 a の元の内容は保存されない。
 * @return 引数 a へのポインタ。失敗すれば NULL
 */
harkio_Matrix* harkio_Matrix_add_fast(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    //harkio_Matrix* result;
    int intValuea, intValueb;
    float floatValuea, floatValueb;
    COMPLEX_TYPE complexValuea, complexValueb;
    //COMPLEX_TYPE  calcValue;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "The first matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "The second matrix is NULL");
        return NULL;
    }

    if (a->rows != b->rows || a->cols != b->cols || strcmp(a->type, b->type)) {
        harkio_Log_printf(harkio_Log_Error,
                "rows/cols/type is Illegal value : a->rows[%d] : b->rows[%d] : a->cols[%d] : b->cols[%d] : a->type[%s] : b->type[%s]\n",
                a->rows, b->rows, a->cols, b->cols, a->type, b->type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    //result = harkio_Matrix_new(a->type, a->rows, a->cols);

    //if( result == NULL ) {
    //    harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
    //    return NULL;
    //}

    for (i = 0; i < a->rows; i++) {
        for (j = 0; j < a->cols; j++) {
            switch (harkio_CheckType(a->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(a, i, j, &intValuea);
                harkio_Matrix_getValueInt32(b, i, j, &intValueb);
                harkio_Matrix_setValueInt32(a, i, j, intValuea + intValueb);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(a, i, j, &floatValuea);
                harkio_Matrix_getValueFloat32(b, i, j, &floatValueb);
                harkio_Matrix_setValueFloat32(a, i, j, floatValuea + floatValueb);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(a, i, j, &complexValuea);
                harkio_Matrix_getValueComplex(b, i, j, &complexValueb);
                harkio_Matrix_setValueComplex(a, i, j, complexValuea + complexValueb);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is wrong (%s)", a->type);
                //harkio_Matrix_delete(&result);
                return NULL;
            }
        }
    }

    return a;
}

/*!
 * @brief 2 つの harkio_Matrix の差を求める。
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return  要素ごとの差 (a-b) を要素に持つ新しい harkio_Matrix オブジェクト。失敗すれば NULL
 */
harkio_Matrix* harkio_Matrix_sub(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    // int retValue;
    int intValuea, intValueb;
    float floatValuea, floatValueb;
    COMPLEX_TYPE complexValuea, complexValueb;
    // COMPLEX_TYPE calcValue;
    harkio_Matrix* result;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (a->rows != b->rows || a->cols != b->cols) {
        harkio_Log_printf(harkio_Log_Error,
                "rows/cols is Illegal value : a->rows[%d] b->rows[%d]  a->cols[%d] b->cols[%d]\n", a->rows, b->rows,
                a->cols, b->cols);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    result = harkio_Matrix_new(a->type, a->rows, a->cols);

    if (result == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < a->rows; i++) {
        for (j = 0; j < a->cols; j++) {
            switch (harkio_CheckType(a->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(a, i, j, &intValuea);
                harkio_Matrix_getValueInt32(b, i, j, &intValueb);
                harkio_Matrix_setValueInt32(result, i, j, intValuea - intValueb);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(a, i, j, &floatValuea);
                harkio_Matrix_getValueFloat32(b, i, j, &floatValueb);
                harkio_Matrix_setValueFloat32(result, i, j, floatValuea - floatValueb);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(a, i, j, &complexValuea);
                harkio_Matrix_getValueComplex(b, i, j, &complexValueb);
                harkio_Matrix_setValueComplex(result, i, j, complexValuea - complexValueb);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is wrong (%s)", a->type);
                harkio_Matrix_delete(&result);
                return NULL;
            }
        }
    }
    return result;
}

/*!
 * @brief 2 つの harkio_Matrix の積を求める。
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return a と b の行列の積をもつ harkio_Matrix オブジェクト。失敗すれば NULL
 @details a のサイズが (A1, A2), b のサイズが (B1, B2) の場合、 A2 = B1 でなければ失敗する。
 サイズが合っている場合、サイズ (A1, B2) の新しい行列ができる。
 */
harkio_Matrix* harkio_Matrix_mul(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j, k;
    int cnt;
    int intValuea;
    int intValueb;
    float floatValuea;
    float floatValueb;
    COMPLEX_TYPE complexValuea;
    COMPLEX_TYPE complexValueb;
    // COMPLEX_TYPE  calcValue;
    // int            retValue;
    int sum;
    float fsum;
    COMPLEX_TYPE csum;
    harkio_Matrix *new_matrix;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if ((strcmp(a->type, b->type) != 0) || (a->cols != b->rows)) {
        harkio_Log_printf(harkio_Log_Error, "matrix value is Illegal value");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    new_matrix = harkio_Matrix_zeros(a->type, a->rows, b->cols);

    if (new_matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    switch (harkio_CheckType(a->type)) {
    case harkio_DataType_INT32:
        cnt = 0;

        //受け取った２つの行列の掛け算を行う。
        for (i = 0; i < a->rows; i++) { //a row {
            for (j = 0; j < b->cols; j++) { //bのcol分 {
                sum = 0;
                for (k = 0; k < a->cols; k++) { //m0 col分 {
                    harkio_Matrix_getValueInt32(a, i, k, &intValuea);
                    harkio_Matrix_getValueInt32(b, k, j, &intValueb);

                    sum = sum + intValuea * intValueb;
                }

                new_matrix->data.intdata[cnt] = sum;
                cnt++;
            }
        }
        break;

    case harkio_DataType_FLOAT32:
        cnt = 0;
        for (i = 0; i < a->rows; i++) {
            for (j = 0; j < b->cols; j++) { //m1のcol分
                fsum = 0;
                for (k = 0; k < a->cols; k++) { //m0 col分
                    harkio_Matrix_getValueFloat32(a, i, k, &floatValuea);
                    harkio_Matrix_getValueFloat32(b, k, j, &floatValueb);

                    fsum = fsum + floatValuea * floatValueb;
                }

                new_matrix->data.floatdata[cnt] = fsum;
                cnt++;
            }
        }
        break;

    case harkio_DataType_COMPLEX:
        cnt = 0;
        for (i = 0; i < a->rows; i++) {
            for (j = 0; j < b->cols; j++) { //m1のcol分
                csum = 0;
                for (k = 0; k < a->cols; k++) { //m0 col分
                    harkio_Matrix_getValueComplex(a, i, k, &complexValuea);
                    harkio_Matrix_getValueComplex(b, k, j, &complexValueb);

                    csum = csum + complexValuea * complexValueb;
                }

                new_matrix->data.complexdata[cnt] = csum;
                cnt++;
            }
        }

        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        harkio_Matrix_delete(&new_matrix);
        return NULL;
    }

    return new_matrix;
}

/*!
 * @brief 2 つの harkio_Matrix の要素ごとの積を求める。
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return  要素ごとの積を要素に持つ新しい harkio_Matrix オブジェクト。失敗すれば NULL
 */
harkio_Matrix* harkio_Matrix_emul(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;
    int matrix_size;
    // COMPLEX_TYPE  complexValuea;
    // COMPLEX_TYPE  complexValueb;
    // COMPLEX_TYPE  calcValue;
    harkio_Matrix *new_matrix;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if ((strcmp(a->type, b->type) != 0) || (a->cols != b->cols) || (a->rows != b->rows)) {
        harkio_Log_printf(harkio_Log_Error, "Matrix value is Illegal value");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    new_matrix = harkio_Matrix_zeros(a->type, a->rows, a->cols);

    if (new_matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    matrix_size = a->rows * a->cols;

    switch (harkio_CheckType(a->type)) {
    case harkio_DataType_INT32:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.intdata[i] = a->data.intdata[i] * b->data.intdata[i];
        }
        break;

    case harkio_DataType_FLOAT32:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.floatdata[i] = a->data.floatdata[i] * b->data.floatdata[i];
        }
        break;

    case harkio_DataType_COMPLEX:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.complexdata[i] = a->data.complexdata[i] * b->data.complexdata[i];
        }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        harkio_Matrix_delete(&new_matrix);
        return NULL;
    }

    return new_matrix;

}

/*!
 * @brief 複数 harkio_Matrix の要素ごとの総和を求める
 * @param[in] matrix  harkio_Matrix オブジェクト のポインタ配列
 * @param[in] num     matrix の長さ
 * @return matrix の要素ごとの全ての和を要素に持つ新しい harkio_Matrix オブジェクト。失敗すれば NULL
 * @details matrix 配列が差す harkio_Matrix オブジェクトは全て同じサイズ、型でなければならない。
 */
harkio_Matrix* harkio_Matrix_sum(harkio_Matrix **matrix, int num) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    int matrix_size;
    // COMPLEX_TYPE  calcValue;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    for (i = 1; i < num; i++) {
        if (matrix[i] == NULL) {
            harkio_Log_printf(harkio_Log_Error, "Matrix is NULL : i=[%d]\n", i);
            return NULL;
        }

        if ((strcmp(matrix[0]->type, matrix[i]->type) != 0) || (matrix[0]->cols != matrix[i]->cols)
                || (matrix[0]->rows != matrix[i]->rows)) {
            harkio_Log_printf(harkio_Log_Error,
                    "Matrix value is Illegal value : matrix[0]->type:[%s] matrix[%d]->type:[%s]  matrix[0]->cols:[%d]  matrix[%d]->cols:[%d]\n",
                    matrix[0]->type, i, matrix[i]->type, matrix[0]->cols, i, matrix[i]->cols);
            return NULL;
        }
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    // new matrix + data 0set
    harkio_Matrix *new_matrix = harkio_Matrix_zeros(matrix[0]->type, matrix[0]->rows, matrix[0]->cols);

    if (new_matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    matrix_size = matrix[0]->rows * matrix[0]->cols;

    switch (harkio_CheckType(matrix[0]->type)) {
    case harkio_DataType_INT32:
        for (i = 0; i < matrix_size; i++) {
            for (j = 0; j < num; j++) {
                new_matrix->data.intdata[i] += matrix[j]->data.intdata[i];
            }
        }

        break;

    case harkio_DataType_FLOAT32:
        for (i = 0; i < matrix_size; i++) {
            for (j = 0; j < num; j++) {
                new_matrix->data.floatdata[i] += matrix[j]->data.floatdata[i];
            }
        }

        break;

    case harkio_DataType_COMPLEX:
        for (i = 0; i < matrix_size; i++) {
            for (j = 0; j < num; j++) {
                new_matrix->data.complexdata[i] += matrix[j]->data.complexdata[i];
            }
        }

        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is wrong (%s)", matrix[0]->type);
        harkio_Matrix_delete(&new_matrix);
        return NULL;
    }

    return new_matrix;
}

/*!
 * @brief int32 型の harkio_Matrix の値を取り出す
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  取り出したい値の 行位置 (0 から開始)
 * @param[in] col  取り出したい値の 列位置 (0 から開始)
 * @param[out] value 取り出した値を格納する変数へのポインタ
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_getValueInt32(harkio_Matrix *m, int row, int col, int *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row/col is Illegal value :  row[%d]  m->rows[%d] col[%d] m->cols[%d]", row,
                m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (harkio_CheckType(m->type) != harkio_DataType_INT32) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value : type[%s]\n", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    *value = m->data.intdata[col + m->cols * row];
    return EXIT_SUCCESS;
}

/*!
 * @brief int32 型の harkio_Matrix の値を設定する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  設定したい値の 行位置 (0 から開始)
 * @param[in] col  設定したい値の 列位置 (0 から開始)
 * @param[out] value 設定しい値
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_setValueInt32(harkio_Matrix *m, int row, int col, int value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row/col is Illegal value :  row[%d]  m->rows[%d] col[%d] m->cols[%d]", row,
                m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (strcmp(m->type, "int32") != 0) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value: m->type[%s]", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    m->data.intdata[col + m->cols * row] = value;
    return EXIT_SUCCESS;
}

/*!
 * @brief float32 型の harkio_Matrix の値を取り出す
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  取り出したい値の 行位置 (0 から開始)
 * @param[in] col  取り出したい値の 列位置 (0 から開始)
 * @param[out] value 取り出した値を格納する変数へのポインタ
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_getValueFloat32(harkio_Matrix *m, int row, int col, float *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row/col is Illegal value : row[%d] m->rows[%d] col[%d] m->cols[%d]", row,
                m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (strcmp(m->type, "float32") != 0) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value m->type[%s]", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    *value = m->data.floatdata[col + m->cols * row];
    return EXIT_SUCCESS;
}

/*!
 * @brief float32 型の harkio_Matrix の値を設定する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  設定したい値の 行位置 (0 から開始)
 * @param[in] col  設定したい値の 列位置 (0 から開始)
 * @param[out] value 設定しい値
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_setValueFloat32(harkio_Matrix *m, int row, int col, float value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row/col is Illegal value : row[%d]  m->rows[%d] col[%d] m->cols[%d]", row,
                m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (strcmp(m->type, "float32") != 0) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value : m->type[%s]", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    m->data.floatdata[col + m->cols * row] = value;

    return EXIT_SUCCESS;
}

/*!
 * @brief complex 型の harkio_Matrix の値を設定する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  設定したい値の 行位置 (0 から開始)
 * @param[in] col  設定したい値の 列位置 (0 から開始)
 * @param[out] value 設定しい値
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_setValueComplex(harkio_Matrix *m, int row, int col, COMPLEX_TYPE value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row or col is Illegal value : row[%d] m->rows[%d] col[%d] m->cols[%d]",
                row, m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (strcmp(m->type, "complex") != 0) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value : m->type[%s]\n", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    m->data.complexdata[col + m->cols * row] = value;

    return EXIT_SUCCESS;
}

/*!
 * @brief complex 型の harkio_Matrix の値を取り出す
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] row  取り出したい値の 行位置 (0 から開始)
 * @param[in] col  取り出したい値の 列位置 (0 から開始)
 * @param[out] value 取り出した値を格納する変数へのポインタ
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_getValueComplex(harkio_Matrix *m, int row, int col, COMPLEX_TYPE *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (row < 0 || m->rows <= row || col < 0 || m->cols <= col) {
        harkio_Log_printf(harkio_Log_Error, "row or col is Illegal value : row[%d] m->rows[%d] col[%d] m->cols[%d]",
                row, m->rows, col, m->cols);
        return EXIT_FAILURE;
    }

    if (strcmp(m->type, "complex") != 0) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value : m->type[%s]\n", m->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    *value = m->data.complexdata[col + m->cols * row];
    return EXIT_SUCCESS;
}

/*!
 * @brief harkio_Matrix の部分行列を取り出す
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] rowFrom  取り出したい値の 行開始位置 (0 から開始)
 * @param[in] rowTo    取り出したい値の 行終了位置 (0 から開始)
 * @param[in] colFrom  取り出したい値の 列開始位置 (0 から開始)
 * @param[in] colTo    取り出したい値の 列終了位置 (0 から開始)
 * @return  取り出しに成功すれば部分行列を含む harkio_Matrix オブジェクト
 失敗すれば NULL ポインタ。
 */
harkio_Matrix* harkio_Matrix_getValues(harkio_Matrix *m, int rowFrom, int rowTo, int colFrom, int colTo) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int len;
    int i;
    int rowLength;
    int colLength;
    harkio_Matrix *new_matrix;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if (rowFrom > rowTo || colFrom > colTo) {
        harkio_Log_printf(harkio_Log_Error,
                "row or col is Illegal value :  rowFrom[%d] rowTo[%d] colFrom[%d] colTo[%d]", rowFrom, rowTo, colFrom,
                colTo);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    rowLength = (rowTo - rowFrom) + 1;
    colLength = (colTo - colFrom) + 1;

    new_matrix = harkio_Matrix_zeros(m->type, rowLength, colLength);

    if (new_matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    switch (harkio_CheckType(m->type)) {
    case harkio_DataType_INT32:
        len = colLength * sizeof(int);

        for (i = 0; i < rowLength; i++) {
            memcpy((void*) &(new_matrix->data.intdata[(new_matrix->cols * i)]),
                    (void*) &(m->data.intdata[(m->cols * (i + rowFrom)) + colFrom]), len);
        }

        break;

    case harkio_DataType_FLOAT32:
        len = colLength * sizeof(float);

        for (i = 0; i < rowLength; i++) {
            memcpy((void*) &(new_matrix->data.floatdata[(new_matrix->cols * i)]),
                    (void*) &(m->data.floatdata[(m->cols * (i + rowFrom)) + colFrom]), len);
        }
        break;

    case harkio_DataType_COMPLEX:
        len = colLength * sizeof(COMPLEX_TYPE);
        for (i = 0; i < rowLength; i++) {
            memcpy((void*) &(new_matrix->data.complexdata[(new_matrix->cols * i)]),
                    (void*) &(m->data.complexdata[(m->cols * (i + rowFrom)) + colFrom]), len);
        }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is wrong (%s)", m->type);
        return NULL;
    }

    return new_matrix;
}

/*!
 * @brief harkio_Matrix の一部を別の harkio_Matrix で上書きする
 * @param[in] m1  値を設定したい先の harkio_Matrix オブジェクト
 * @param[in] m2  値を設定したい元の harkio_Matrix オブジェクト
 * @param[in] row  設定したい値の 行位置 (0 から開始)
 * @param[in] col  設定したい値の 列位置 (0 から開始)
 * @return  取り出しに成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE
 * @details m1 より m2 のサイズが小さくなければならない。また、はみ出ない row/col の値でなければならない。
 複数の値をまとめて保存するときに使用
 */
int harkio_Matrix_setValues(harkio_Matrix *m1, harkio_Matrix *m2, int row, int col) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int len;
    int i;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m1 == NULL) {
        harkio_Log_printf(harkio_Log_Error, "m1 is NULL");
        return EXIT_FAILURE;
    }

    if (m2 == NULL) {
        harkio_Log_printf(harkio_Log_Error, "m2 is NULL");
        return EXIT_FAILURE;
    }

    if (strcmp(m1->type, m2->type) != 0) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : m1->type[%s] m2->type[%s]", m1->type, m2->type);
        return EXIT_FAILURE;
    }

    if (row < 0 || col < 0 || m1->rows < m2->rows || m1->cols < m2->cols) {
        harkio_Log_printf(harkio_Log_Error,
                "row or col is Illegal value : row[%d] col[%d]  m1->rows[%d] m2->rows[%d] m1->cols[%d] m2->cols[%d]",
                row, col, m1->rows, m2->rows, m1->cols, m2->cols);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    switch (harkio_CheckType(m1->type)) {
    case harkio_DataType_INT32:
        len = m2->cols * sizeof(int);

        for (i = 0; i < m2->rows; i++) {
            memcpy((void*) &(m1->data.intdata[(m1->cols * (i + row)) + col]),
                    (void*) &(m2->data.intdata[(m2->cols * i)]), len);
        }
        break;

    case harkio_DataType_FLOAT32:
        len = m2->cols * sizeof(float);

        for (i = 0; i < m2->rows; i++) {
            memcpy((void*) &(m1->data.floatdata[(m1->cols * (i + row)) + col]),
                    (void*) &(m2->data.floatdata[(m2->cols * i)]), len);
        }
        break;

    case harkio_DataType_COMPLEX:
        len = m2->cols * sizeof(COMPLEX_TYPE);

        for (i = 0; i < m2->rows; i++) {
            memcpy((void*) &(m1->data.complexdata[(m1->cols * (i + row)) + col]),
                    (void*) &(m2->data.complexdata[(m2->cols * i)]), len);
        }

        break;

    default:

        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

/*!
 * @brief harkio_Matrix のタグを取得する
 * @param[in] m  harkio_Matrix オブジェクト
 * @return  取り出しに成功すれば 変数へのポインタ。失敗すれば NULL
 * @details ポインタそのものを返すので、受け取った先で Free すると m の tag が破壊されるので注意。
 */
char* harkio_Matrix_getTag(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return "0";
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    return m->tag;
}

/*----------------------------------------------------------------------------*/
/* Utility                                                                  */
/*----------------------------------------------------------------------------*/

/*!
 * @brief harkio_Matrix を表示する
 * @param[in] m  harkio_Matrix オブジェクト
 */
void harkio_Matrix_print(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *buffer;

    buffer = harkio_Matrix_printBuffer(m);
    if (buffer)
        fprintf(stderr, "%s", buffer);
    free(buffer);
}

/*!
 * @brief 文字列を追記する。
 * @param[in] buffer 追記元の文字列アドレス
 * @param[in] str 追記する文字列
 * @param[in] bufsize bufferが確保している長さ
 * @return  buffer の実際の長さ
 */
int harkio_Matrix_addstring(char **buffer, char *str, int bufsize) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int ret = bufsize;
    if (strlen(*buffer) + strlen(str) + 1 >= bufsize) {
        int step = 4192;
        char* tmp = (char*) realloc(*buffer, bufsize + step);
        *buffer = tmp;
        ret += step;
    }

    strncat(*buffer, str, ret);
    return ret;
}

/*!
 * @brief harkio_Matrix の文字列表現を格納したバッファを返す。
 * @param[in] m  harkio_Matrix オブジェクト
 * @return       char *  (利用者によるfree が必要)
 */
char* harkio_Matrix_printBuffer(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j, err;
    int intValue;
    float floatValue;
    COMPLEX_TYPE complexValue;

    char *buffer;
    char tmp[MSG_LEN];
    int buflen = MSG_LEN;
    buffer = (char *) malloc(MSG_LEN * sizeof(char));  // 4kB
    for (i = 0; i < MSG_LEN; i++)
        buffer[i] = '\0';

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        free(buffer);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    sprintf(buffer, "Matrix\n"
            "   type: [%s]\n"
            "   rows: [%d]\n"
            "   cols: [%d]\n", m->type, m->rows, m->cols);
    err = 0;
    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(m, i, j, &intValue);
                sprintf(tmp, "%3d ", intValue);
                buflen = harkio_Matrix_addstring(&buffer, tmp, buflen);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(m, i, j, &floatValue);
                sprintf(tmp, "%.2f ", floatValue);
                buflen = harkio_Matrix_addstring(&buffer, tmp, buflen);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(m, i, j, &complexValue);
                sprintf(tmp, "%.2f %.2f ", crealf(complexValue), cimagf(complexValue));
                buflen = harkio_Matrix_addstring(&buffer, tmp, buflen);
                break;

            default:
                buflen = harkio_Matrix_addstring(&buffer, "Non ", buflen);
                err = -1;
            }
        }
        buflen = harkio_Matrix_addstring(&buffer, "\n", buflen);
    }

    if (err == -1) {
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
    }

    return buffer;
}

/*!
 * @brief ２つの harkio_Matrix オブジェクトが等しいかどうかを判定する
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return  等しければ EXIT_SUCCESS, 等しくなければ EXIT_FAILURE
 * @details 型、サイズ、すべての値が同じでなければならない。
 */
int harkio_Matrix_isEqual(harkio_Matrix *a, harkio_Matrix *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    int intValuea;
    int intValueb;
    float floatValuea;
    float floatValueb;
    COMPLEX_TYPE complexValuea;
    COMPLEX_TYPE complexValueb;
    // COMPLEX_TYPE  calcValue;
    float epsiron = 0.0001;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    // size check
    if (a->rows != b->rows) {
        harkio_Log_printf(harkio_Log_Error, "Matrix rows do not match (%d vs %d)", a->rows, b->rows);
        return EXIT_FAILURE;
    }
    if (a->cols != b->cols) {
        harkio_Log_printf(harkio_Log_Error, "Matrix cols do not match (%d vs %d)", a->cols, b->cols);
        return EXIT_FAILURE;
    }
    if (harkio_CheckType(a->type) != harkio_CheckType(b->type)) {
        harkio_Log_printf(harkio_Log_Error, "Matrix types do not match (%s vs %s)", a->type, b->type);
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    for (i = 0; i < a->rows; i++) {
        for (j = 0; j < a->cols; j++) {
            switch (harkio_CheckType(a->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(a, i, j, &intValuea);
                harkio_Matrix_getValueInt32(b, i, j, &intValueb);

                if (intValuea != intValueb) {
                    harkio_Log_printf(harkio_Log_Error, "a[%d, %d] = %d but b[%d, %d] = %d", i, j, intValuea, i, j,
                            intValueb);
                    return EXIT_FAILURE;
                }
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(a, i, j, &floatValuea);
                harkio_Matrix_getValueFloat32(b, i, j, &floatValueb);

                // fprintf(stderr, "[%d, %d] = %f, %f\n", i, j, floatValuea, floatValueb);
                if (fabsf(floatValuea - floatValueb) > epsiron) {
                    harkio_Log_printf(harkio_Log_Error, "a[%d, %d] = %f but b[%d, %d] = %f", i, j, floatValuea, i, j,
                            floatValueb);
                    return EXIT_FAILURE;
                }
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(a, i, j, &complexValuea);
                harkio_Matrix_getValueComplex(b, i, j, &complexValueb);

                // fprintf(stderr, "[%d, %d] = %f + %f, %f + %fi\n", i, j   , crealf(complexValuea)
                //      , cimagf(complexValuea)
                //      , crealf(complexValueb)
                //      , cimagf(complexValueb) );

                if (cabsf(complexValuea - complexValueb) > epsiron) {
                    harkio_Log_printf(harkio_Log_Error, "complex does not the same");
                    return EXIT_FAILURE;
                }
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return EXIT_FAILURE;
            }
        }
    }

    return EXIT_SUCCESS;
}

/*!
 * @brief harkio_Matrix のサイズを取得する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] size 行列のサイズが格納されるポインタ。 長さ 2 のメモリが確保された int 配列である必要がある。
 * @return  行列全体のサイズ
 * @details size は呼び出し前にメモリ確保が必要であることに注意。
 */
size_t harkio_Matrix_size(harkio_Matrix *m, int *size) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    size_t singleSize, totalsize;

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return -1;
    }

    switch (harkio_CheckType(m->type)) {
    case harkio_DataType_INT32:
        singleSize = sizeof(int);
        break;

    case harkio_DataType_FLOAT32:
        singleSize = sizeof(float);
        break;

    case harkio_DataType_COMPLEX:
        singleSize = sizeof(COMPLEX_TYPE);
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return EXIT_FAILURE;
    }
    totalsize = sizeof(char) * (32 + 32) + // tag, type
            sizeof(int) * 3 +          // dim, rows, cols,
            singleSize * m->rows * m->cols;

    if (size != NULL) {
        size[0] = m->rows;
        size[1] = m->cols;
    }

    return totalsize;
}

/*!
 * @brief harkio_Matrix をファイルに書き込む
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] path 書き込むファイルへのパス
 * @return  書き込みに成功すれば EXIT_SUCCESS、失敗すれば EXIT_FAILURE
 */
int harkio_Matrix_write(harkio_Matrix *m, char *path) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    FILE *fp;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return EXIT_FAILURE;
    }

    if (path == NULL || (fp = fopen(path, "wb")) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "File Open Failed");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    size_t size;
    char* buf = harkio_Matrix_writeBuffer(m, &size);
    if (!buf) {
        fclose(fp);
        return EXIT_FAILURE;
    }

    fwrite(buf, size, 1, fp);
    free(buf);
    fclose(fp);
    return EXIT_SUCCESS;
}

/*!
 * @brief harkio_Matrix のファイルフォーマットを書き込んだバッファを返す。
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[out] size  戻り値が指すバッファのサイズ
 * @return harkio_Matrix のファイルフォーマットと同じデータが格納されたバッファへのポインタ。
 */
char* harkio_Matrix_writeBuffer(harkio_Matrix *m, size_t *size) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *tag = HARKIO_TAG;
    int dim = HARK_DIM; // for matrix;
    int i, j;
    int intValue;
    float floatValue;
    COMPLEX_TYPE complexValue;
    float realf;
    float imagf;

    size_t singleSize;
    char *buf;
    int pointer = 0;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    switch (harkio_CheckType(m->type)) {
    case harkio_DataType_INT32:
        singleSize = sizeof(int);
        break;

    case harkio_DataType_FLOAT32:
        singleSize = sizeof(float);
        break;

    case harkio_DataType_COMPLEX:
        singleSize = sizeof(COMPLEX_TYPE);
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return NULL;
    }

    buf = (char *) malloc(sizeof(char) * (32 + 32) + sizeof(int) * 3 + singleSize * m->rows * m->cols);

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    // tag
    sprintf(buf + pointer, "%-32s", tag);
    pointer += 32;

    // type
    sprintf(buf + pointer, "%-32s", m->type);
    pointer += 32;

    // dimension = 2; cols, rows
    memcpy(buf + pointer, &dim, sizeof(int));
    pointer += sizeof(int);
    memcpy(buf + pointer, &m->rows, sizeof(int));
    pointer += sizeof(int);
    memcpy(buf + pointer, &m->cols, sizeof(int));
    pointer += sizeof(int);

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(m, i, j, &intValue);
                memcpy(buf + pointer, &intValue, sizeof(int));
                pointer += sizeof(int);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(m, i, j, &floatValue);
                memcpy(buf + pointer, &floatValue, sizeof(float));
                pointer += sizeof(float);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(m, i, j, &complexValue);

                realf = crealf(complexValue);
                imagf = cimagf(complexValue);
                memcpy(buf + pointer, &realf, sizeof(float));
                pointer += sizeof(float);
                memcpy(buf + pointer, &imagf, sizeof(float));
                pointer += sizeof(float);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                free(buf);
                return NULL;
            }
        }
    }
    // fprintf(stderr, "buffer:%s-\nsize:%d\n", buf, pointer);
    *size = (size_t) pointer;
    return buf;
}

/*!
 * @brief [内部用] harkio_Matrix の型判定
 * @param[in] type  型の文字列
 */
int harkio_CheckType(char *type) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    if (strncmp(type, "int32", 5) == 0) {
        return harkio_DataType_INT32;
    } else if (strncmp(type, "float32", 7) == 0) {
        return harkio_DataType_FLOAT32;
    } else if (strncmp(type, "complex", 7) == 0) {
        return harkio_DataType_COMPLEX;
    }

    return (-1);
}

/*!
 * @brief 2 つの harkio_Matrix の要素ごとの商を求める。
 * @param[in] a  harkio_Matrix オブジェクト
 * @param[in] b  harkio_Matrix オブジェクト
 * @return  要素ごとの商 (a / b) を要素に持つ新しい harkio_Matrix オブジェクト。失敗すれば NULL
 */
harkio_Matrix* harkio_Matrix_ediv(harkio_Matrix *a, harkio_Matrix* b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;
    int matrix_size;
    // COMPLEX_TYPE  complexValuea;
    // COMPLEX_TYPE  complexValueb;
    // COMPLEX_TYPE  calcValue;
    harkio_Matrix *new_matrix;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL) {
        harkio_Log_printf(harkio_Log_Error, "The first Matrix is NULL");
        return NULL;
    }

    if (b == NULL) {
        harkio_Log_printf(harkio_Log_Error, "The second Matrix is NULL");
        return NULL;
    }

    if ((strcmp(a->type, b->type) != 0) || (a->cols != b->cols) || (a->rows != b->rows)) {
        harkio_Log_printf(harkio_Log_Error, "Matrix sizes do not match");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    new_matrix = harkio_Matrix_zeros(a->type, a->rows, a->cols);

    if (new_matrix == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    matrix_size = a->rows * a->cols;
    switch (harkio_CheckType(a->type)) {
    case harkio_DataType_INT32:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.intdata[i] = a->data.intdata[i] / b->data.intdata[i];
        }
        break;

    case harkio_DataType_FLOAT32:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.floatdata[i] = a->data.floatdata[i] / b->data.floatdata[i];
        }
        break;

    case harkio_DataType_COMPLEX:
        for (i = 0; i < matrix_size; i++) {
            new_matrix->data.complexdata[i] = a->data.complexdata[i] / b->data.complexdata[i];
        }
        break;

    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is not supported");
        harkio_Matrix_delete(&new_matrix);
        return NULL;
    }

    return new_matrix;

}

/*!
 * @brief 対角成分を取り出す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return m の対角成分を要素に持つ (mのサイズ, 1) の行列へのポインタ
 * @details m は正方行列でなければならない。
 */
harkio_Matrix* harkio_Matrix_getDiag(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix *ret;
    int i;

    if (m->rows != m->cols) {
        harkio_Log_printf(harkio_Log_Error, "Matrix must be square");
        return NULL;
    }

    ret = harkio_Matrix_new(m->type, m->rows, 1);
    for (i = 0; i < m->rows; i++) {
        int itmp;
        float ftmp;
        COMPLEX_TYPE ctmp;
        switch (harkio_CheckType(m->type)) {
        case harkio_DataType_INT32:
            harkio_Matrix_getValueInt32(m, i, i, &itmp);
            harkio_Matrix_setValueInt32(ret, i, 0, itmp);
            break;
        case harkio_DataType_FLOAT32:
            harkio_Matrix_getValueFloat32(m, i, i, &ftmp);
            harkio_Matrix_setValueFloat32(ret, i, 0, ftmp);
            break;
        case harkio_DataType_COMPLEX:
            harkio_Matrix_getValueComplex(m, i, i, &ctmp);
            harkio_Matrix_setValueComplex(ret, i, 0, ctmp);
            break;
        default:
            harkio_Log_printf(harkio_Log_Error, "Matrix type is not supported");
            harkio_Matrix_delete(&ret);
            return NULL;
        }
    }

    return ret;
}

/*!
 * @brief 列ベクトルを対角成分にもつ正方行列を返す
 * @param[in] m  harkio_Matrix オブジェクト。サイズは (X, 1)
 * @return m の対角成分を要素に持つ (X, X) の行列へのポインタ
 * @details m は列ベクトル (row = 1) でなければならない。
 */
harkio_Matrix* harkio_Matrix_makeDiag(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix *ret;
    int i;

    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "Matrix is NULL");
        return NULL;
    }

    if (m->rows != 1) {
        harkio_Log_printf(harkio_Log_Error, "Matrix must have only one row");
        return NULL;
    }

    ret = harkio_Matrix_zeros(m->type, m->cols, m->cols);
    for (i = 0; i < m->cols; i++) {
        int itmp;
        float ftmp;
        COMPLEX_TYPE ctmp;
        switch (harkio_CheckType(m->type)) {
        case harkio_DataType_INT32:
            harkio_Matrix_getValueInt32(m, 0, i, &itmp);
            harkio_Matrix_setValueInt32(ret, i, i, itmp);
            break;
        case harkio_DataType_FLOAT32:
            harkio_Matrix_getValueFloat32(m, 0, i, &ftmp);
            harkio_Matrix_setValueFloat32(ret, i, i, ftmp);
            break;
        case harkio_DataType_COMPLEX:
            harkio_Matrix_getValueComplex(m, 0, i, &ctmp);
            harkio_Matrix_setValueComplex(ret, i, i, ctmp);
            break;
        default:
            harkio_Log_printf(harkio_Log_Error, "Matrix type is not supported");
            harkio_Matrix_delete(&ret);
            return NULL;
        }
    }

    return ret;
}

/*!
 * @brief 要素ごとに自乗した行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに自乗した行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int isquare(int i) {
    return i * i;
}
float fsquare(float i) {
    return i * i;
}
COMPLEX_TYPE csquare(COMPLEX_TYPE i) {
    return i * i;
}
harkio_Matrix* harkio_Matrix_esquare(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps square = { isquare, // int32
            fsquare, // float32
            csquare  // complex
            };

    return harkio_Matrix_emap(m, &square);
}

/*!
 * @brief 要素ごとの絶対値を要素に持つ行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとの絶対値を持つ行列
 * @details harkio_Matrix_emap を元に実装している。
 * cabsf は float を返すので型を COMPLEX_TYPE で保つための wrapper を用意している。
 */
float floatabs(float i) {
    return fabsf(i);
}
COMPLEX_TYPE complexabs(COMPLEX_TYPE i) {
    return (COMPLEX_TYPE) cabsf(i);
}
harkio_Matrix* harkio_Matrix_eabs(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps square = { abs, // int32
            floatabs, // float32
            complexabs  // complex
            };

    return harkio_Matrix_emap(m, &square);
}

/*!
 * @brief 要素ごとに複素共役を求めた行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに複素共役を求めた行列
 * @details harkio_Matrix_emap を元に実装している。
 complex 型以外は影響なし。
 */
int intconj(int i) {
    return i;
}
float floatconj(float i) {
    return i;
}
harkio_Matrix* harkio_Matrix_econj(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_conj = { intconj, // int32
            floatconj, // float32
            conjf  // complex
            };

    return harkio_Matrix_emap(m, &map_conj);
}

/*!
 * @brief 要素ごとに双曲線正接値を求めた行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに双曲線正接値を求めた行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int inttanh(int i) {
    return (int) tanhf((float) i);
}
harkio_Matrix* harkio_Matrix_etanh(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_tanh = { inttanh, // int32
            tanhf, // float32
            ctanhf  // complex
            };

    return harkio_Matrix_emap(m, &map_tanh);
}

/*!
 * @brief 要素ごとに双曲線余弦値を求めた行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに双曲線余弦値を求めた行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int intcosh(int i) {
    return (int) coshf((float) i);
}
harkio_Matrix* harkio_Matrix_ecosh(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_cosh = { intcosh, // int32
            coshf, // float32
            ccoshf  // complex
            };

    return harkio_Matrix_emap(m, &map_cosh);
}

/*!
 * @brief 要素ごとに自然対数を求めた行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに自然対数を求めた行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int intlog(int i) {
    return (int) logf((float) i);
}
harkio_Matrix* harkio_Matrix_elog(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_log = { intlog, // int32
            logf, // float32
            clogf  // complex
            };

    return harkio_Matrix_emap(m, &map_log);
}

/*!
 * @brief 要素ごとに常用対数を求めた行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに常用対数を求めた行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int intlog10(int i) {
    return (int) (log10f((float) i));
}
COMPLEX_TYPE complexlog10(COMPLEX_TYPE i) {
    return (clogf(i) / logf(10.0));
}
harkio_Matrix* harkio_Matrix_elog10(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_log10 = { intlog10, // int32
            log10f, // float32
            complexlog10  // complex
            };

    return harkio_Matrix_emap(m, &map_log10);
}

/*!
 * @brief 要素ごとに実数部をとる行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに実数部をとる行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int intreal(int i) {
    return i;
}
float floatreal(float i) {
    return i;
}
COMPLEX_TYPE complexreal(COMPLEX_TYPE i) {
    return ((COMPLEX_TYPE) crealf(i));
}
harkio_Matrix* harkio_Matrix_ereal(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_real = { intreal, // int32
            floatreal, // float32
            complexreal  // complex
            };

    return harkio_Matrix_emap(m, &map_real);
}

/*!
 * @brief 要素ごとに虚数部をとる行列を返す
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 要素ごとに虚数部をとる行列
 * @details harkio_Matrix_emap を元に実装している。
 */
int intimag(int i) {
    return 0;
}
float floatimag(float i) {
    return 0.0;
}
COMPLEX_TYPE compleximag(COMPLEX_TYPE i) {
    return ((COMPLEX_TYPE) cimagf(i));
}
harkio_Matrix* harkio_Matrix_eimag(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_Matrix_maps map_imag = { intimag, // int32
            floatimag, // float32
            compleximag  // complex
            };

    return harkio_Matrix_emap(m, &map_imag);
}

/*!
 * @brief 行列を定数倍する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 定数係数
 * @return 定数倍された行列
 */
int iimul(int i1, int i2) {
    return (i1 * i2);
}
harkio_Matrix* harkio_Matrix_emulCInt32(harkio_Matrix *m, int value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCInt32(m, value, iimul);
}

/*!
 * @brief 行列を定数倍する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 定数係数
 * @return 定数倍された行列
 */
float ffmul(float i1, float i2) {
    return (i1 * i2);
}
harkio_Matrix* harkio_Matrix_emulCFloat32(harkio_Matrix *m, float value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCFloat32(m, value, ffmul);
}

/*!
 * @brief 行列を定数倍する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 定数係数
 * @return 定数倍された行列
 */
COMPLEX_TYPE ccmul(COMPLEX_TYPE i1, COMPLEX_TYPE i2) {
    return (i1 * i2);
}
harkio_Matrix* harkio_Matrix_emulCComplex(harkio_Matrix *m, COMPLEX_TYPE value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCComplex(m, value, ccmul);
}

/*!
 * @brief 行列を定数で除算する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 除算したい値
 * @return 定数で除算された行列
 */
int iidiv(int i1, int i2) {
    return (i1 / i2);
}
harkio_Matrix* harkio_Matrix_edivCInt32(harkio_Matrix *m, int value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCInt32(m, value, iidiv);
}

/*!
 * @brief 行列を定数で除算する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 除算したい値
 * @return 定数で除算された行列
 */
float ffdiv(float i1, float i2) {
    return (i1 / i2);
}
harkio_Matrix* harkio_Matrix_edivCFloat32(harkio_Matrix *m, float value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCFloat32(m, value, ffdiv);
}

/*!
 * @brief 行列を定数で除算する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 除算したい値
 * @return 定数で除算された行列
 */
COMPLEX_TYPE ccdiv(COMPLEX_TYPE i1, COMPLEX_TYPE i2) {
    return (i1 / i2);
}
harkio_Matrix* harkio_Matrix_edivCComplex(harkio_Matrix *m, COMPLEX_TYPE value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCComplex(m, value, ccdiv);
}

/*!
 * @brief 行列を定数でべき乗する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 指数
 * @return 指数でべき乗された行列
 */
int iipower(int i1, int i2) {
    return (powf((float) i1, (float) i2));
}
harkio_Matrix* harkio_Matrix_epowerCInt32(harkio_Matrix *m, int value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCInt32(m, value, iipower);
}

/*!
 * @brief 行列を定数でべき乗する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 指数
 * @return 指数でべき乗された行列
 */
harkio_Matrix* harkio_Matrix_epowerCFloat32(harkio_Matrix *m, float value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCFloat32(m, value, powf);
}

/*!
 * @brief 行列を定数でべき乗する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] value 指数
 * @return 指数でべき乗された行列
 */
harkio_Matrix* harkio_Matrix_epowerCComplex(harkio_Matrix *m, COMPLEX_TYPE value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    return harkio_Matrix_ecalcCComplex(m, value, cpowf);
}

/*!
 * @brief 行列の各要素を関数で写像する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] map 型ごとの関数ポインタ
 * @return m の各要素に対して、引数 map で定義した関数を施して、
 * 施した結果の新しい行列を返す。
 */
harkio_Matrix* harkio_Matrix_emap(harkio_Matrix *m, harkio_Matrix_maps *map) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;

    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->rows, m->cols);

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            int itmp;
            float ftmp;
            COMPLEX_TYPE ctmp;

            // fprintf(stderr, "emap type(%s %d [int:%d, float:%d, complex:%d] %d %d)\n",
            //     m->type, harkio_CheckType(m->type), harkio_DataType_INT32, harkio_DataType_FLOAT32, harkio_DataType_COMPLEX, i, j);

            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(m, i, j, &itmp);
                harkio_Matrix_setValueInt32(ret, i, j, (map->imap)(itmp));
                break;
            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(m, i, j, &ftmp);
                harkio_Matrix_setValueFloat32(ret, i, j, (map->fmap)(ftmp));
                break;
            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(m, i, j, &ctmp);
                harkio_Matrix_setValueComplex(ret, i, j, (map->cmap)(ctmp));
                break;
            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is not supported");
                harkio_Matrix_delete(&ret);
                return NULL;
            }
        }
    }
    return ret;
}

/*!
 * @brief 行列の各要素を関数で写像する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] map 型ごとの関数ポインタ
 * @return m の各要素に対して、引数 map で定義した関数を施して、
 * 施した結果の新しい行列を返す。
 */
harkio_Matrix* harkio_Matrix_ecalcCInt32(harkio_Matrix *m, int value, harkio_Matrix_calcfuncInt32 func) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    int itmp;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (harkio_CheckType(m->type) != harkio_DataType_INT32) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : m->type[%s]\n", m->type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->rows, m->cols);

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            // fprintf(stderr, "emap type(%s %d [int:%d, float:%d, complex:%d] %d %d)\n",
            //     m->type, harkio_CheckType(m->type), harkio_DataType_INT32, harkio_DataType_FLOAT32, harkio_DataType_COMPLEX, i, j);
            harkio_Matrix_getValueInt32(m, i, j, &itmp);
            harkio_Matrix_setValueInt32(ret, i, j, func(itmp, value));
        }
    }
    return ret;
}

/*!
 * @brief 行列の各要素を関数で写像する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] map 型ごとの関数ポインタ
 * @return m の各要素に対して、引数 map で定義した関数を施して、
 * 施した結果の新しい行列を返す。
 */
harkio_Matrix* harkio_Matrix_ecalcCFloat32(harkio_Matrix *m, float value, harkio_Matrix_calcfuncFloat32 func) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    float ftmp;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (harkio_CheckType(m->type) != harkio_DataType_FLOAT32) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : m->type[%s]\n", m->type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->rows, m->cols);

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            // fprintf(stderr, "emap type(%s %d [int:%d, float:%d, complex:%d] %d %d)\n",
            //     m->type, harkio_CheckType(m->type), harkio_DataType_INT32, harkio_DataType_FLOAT32, harkio_DataType_COMPLEX, i, j);
            harkio_Matrix_getValueFloat32(m, i, j, &ftmp);
            harkio_Matrix_setValueFloat32(ret, i, j, func(ftmp, value));
        }
    }
    return ret;
}

/*!
 * @brief 行列の各要素を関数で写像する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] map 型ごとの関数ポインタ
 * @return m の各要素に対して、引数 map で定義した関数を施して、
 * 施した結果の新しい行列を返す。
 */
harkio_Matrix* harkio_Matrix_ecalcCComplex(harkio_Matrix *m, COMPLEX_TYPE value, harkio_Matrix_calcfuncComplex func) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    COMPLEX_TYPE ctmp;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (m == NULL) {
        harkio_Log_printf(harkio_Log_Error, "matrix is NULL");
        return NULL;
    }

    if (harkio_CheckType(m->type) != harkio_DataType_COMPLEX) {
        harkio_Log_printf(harkio_Log_Error, "type is Illegal value : m->type[%s]\n", m->type);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->rows, m->cols);

    for (i = 0; i < m->rows; i++) {
        for (j = 0; j < m->cols; j++) {
            // fprintf(stderr, "emap type(%s %d [int:%d, float:%d, complex:%d] %d %d)\n",
            //     m->type, harkio_CheckType(m->type), harkio_DataType_INT32, harkio_DataType_FLOAT32, harkio_DataType_COMPLEX, i, j);
            harkio_Matrix_getValueComplex(m, i, j, &ctmp);
            harkio_Matrix_setValueComplex(ret, i, j, func(ctmp, value));
        }
    }
    return ret;
}

/*!
 * @brief 行列の並び順を変更する
 * @param[in] m  harkio_Matrix オブジェクト
 * @param[in] rows 新しい行数
 * @param[in] cols 新しい列数
 * @return 並び順が変更された行列のオブジェクト
 * @details 行列の配列サイズ自体は変更しないので、元の rows * cols と、新しい rows * cols は等しくなければならない。
 */
harkio_Matrix* harkio_Matrix_reshape(harkio_Matrix *m, int rows, int cols) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    int index;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (rows <= 0 || cols <= 0) {
        harkio_Log_printf(harkio_Log_Error, "rows/cols is Illegal value : rows[%d] cols[%d]\n", rows, cols);
        return NULL;
    }

    if (m->rows * m->cols != rows * cols) {
        harkio_Log_printf(harkio_Log_Error,
                "It is not possible to change the number of elements[%d] : rows[%d] cols[%d]\n", m->rows * m->cols,
                rows, cols);
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(m->type, rows, cols);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    index = 0;
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++, index++) {
            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_setValueInt32(ret, i, j, m->data.intdata[index]);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_setValueFloat32(ret, i, j, m->data.floatdata[index]);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_setValueComplex(ret, i, j, m->data.complexdata[index]);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return NULL;
            }
        }
    }

    return ret;

}

/*!
 * @brief 行列のを転置する
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 転置された行列のオブジェクト
 * @details 行列の配列サイズは (row, col) --> (col, row) となる。
 */
harkio_Matrix* harkio_Matrix_transpose(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    float fvalue;
    COMPLEX_TYPE cvalue;

    int ivalue;

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->cols, m->rows);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < ret->rows; i++) {
        for (j = 0; j < ret->cols; j++) {
            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(m, j, i, &ivalue);
                harkio_Matrix_setValueInt32(ret, i, j, ivalue);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(m, j, i, &fvalue);
                harkio_Matrix_setValueFloat32(ret, i, j, fvalue);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(m, j, i, &cvalue);
                harkio_Matrix_setValueComplex(ret, i, j, cvalue);
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return NULL;
            }
        }
    }

    return ret;
}

/*!
 * @brief 行列を複素共役転置する
 * @param[in] m  harkio_Matrix オブジェクト
 * @return 複素共役転置された行列のオブジェクト
 * @details 行列の配列サイズは (row, col) --> (col, row) となり、
 各要素は複素共役がとられる。
 */
harkio_Matrix* harkio_Matrix_ctranspose(harkio_Matrix *m) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    float fvalue;
    COMPLEX_TYPE cvalue;

    int ivalue;

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(m->type, m->cols, m->rows);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    for (i = 0; i < ret->rows; i++) {
        for (j = 0; j < ret->cols; j++) {
            switch (harkio_CheckType(m->type)) {
            case harkio_DataType_INT32:
                harkio_Matrix_getValueInt32(m, j, i, &ivalue);
                harkio_Matrix_setValueInt32(ret, i, j, ivalue);
                break;

            case harkio_DataType_FLOAT32:
                harkio_Matrix_getValueFloat32(m, j, i, &fvalue);
                harkio_Matrix_setValueFloat32(ret, i, j, fvalue);
                break;

            case harkio_DataType_COMPLEX:
                harkio_Matrix_getValueComplex(m, j, i, &cvalue);
                harkio_Matrix_setValueComplex(ret, i, j, conjf(cvalue));
                break;

            default:
                harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
                return NULL;
            }
        }
    }

    return ret;
}

/***
 * harkio_Matrix_sumRow
 * Row方向に総和
 * (N, M) -> (N, 1)
 **/
harkio_Matrix* harkio_Matrix_sumRow(harkio_Matrix *matrix) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    // accumulate each row

    int i, j;
    int ivalue;
    float fvalue;
    COMPLEX_TYPE cvalue, csumvalue;

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(matrix->type, 1, matrix->cols);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    switch (harkio_CheckType(matrix->type)) {
    case harkio_DataType_INT32:
        for (j = 0; j < matrix->cols; j++) {
            int isumvalue = 0;
            for (i = 0; i < matrix->rows; i++) {
                harkio_Matrix_getValueInt32(matrix, i, j, &ivalue);
                isumvalue += ivalue;
            }
            harkio_Matrix_setValueInt32(ret, 0, j, isumvalue);
        }
        break;
    case harkio_DataType_FLOAT32:
        for (j = 0; j < matrix->cols; j++) {
            float fsumvalue = 0.0;
            for (i = 0; i < matrix->rows; i++) {
                harkio_Matrix_getValueFloat32(matrix, i, j, &fvalue);
                fsumvalue += fvalue;
            }
            harkio_Matrix_setValueFloat32(ret, 0, j, fsumvalue);
        }
        break;
    case harkio_DataType_COMPLEX:
        for (j = 0; j < matrix->cols; j++) {
            csumvalue = 0.0;
            for (i = 0; i < matrix->rows; i++) {
                harkio_Matrix_getValueComplex(matrix, i, j, &cvalue);
                csumvalue += cvalue;
            }
            harkio_Matrix_setValueComplex(ret, 0, j, csumvalue);
        }
        break;
    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return NULL;
    }

    return ret;
}

/***
 * harkio_Matrix_sumCol
 * Col方向に総和
 * (N, M) -> (1, M)
 **/
harkio_Matrix* harkio_Matrix_sumCol(harkio_Matrix *matrix) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    // accumulate each column

    int i, j;
    int ivalue;
    float fvalue;
    COMPLEX_TYPE cvalue, csumvalue;

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(matrix->type, matrix->rows, 1);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    switch (harkio_CheckType(matrix->type)) {
    case harkio_DataType_INT32:
        for (i = 0; i < matrix->rows; i++) {
            int isumvalue = 0;
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueInt32(matrix, i, j, &ivalue);
                isumvalue += ivalue;
            }
            harkio_Matrix_setValueInt32(ret, i, 0, isumvalue);
        }
        break;
    case harkio_DataType_FLOAT32:
        for (i = 0; i < matrix->rows; i++) {
            float fsumvalue = 0.0;
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueFloat32(matrix, i, j, &fvalue);
                fsumvalue += fvalue;
            }
            harkio_Matrix_setValueFloat32(ret, i, 0, fsumvalue);
        }
        break;
    case harkio_DataType_COMPLEX:
        for (i = 0; i < matrix->rows; i++) {
            csumvalue = 0.0;
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueComplex(matrix, i, j, &cvalue);
                csumvalue += cvalue;
            }
            harkio_Matrix_setValueComplex(ret, i, 0, csumvalue);
        }
        break;
    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return NULL;
    }

    return ret;
}

/***
 * harkio_Matrix_sumAll
 * 全要素を総和
 * (N, M) -> (1, 1)
 **/
harkio_Matrix* harkio_Matrix_sumAll(harkio_Matrix *matrix) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    // accumulate all elements

    int i, j;
    int ivalue, isumvalue;
    float fvalue, fsumvalue;
    COMPLEX_TYPE cvalue, csumvalue;

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Matrix *ret = harkio_Matrix_new(matrix->type, 1, 1);

    if (ret == NULL) {
        harkio_Log_printf(harkio_Log_Error, "could not create new Matrix.");
        return NULL;
    }

    switch (harkio_CheckType(matrix->type)) {
    case harkio_DataType_INT32:
        isumvalue = 0;
        for (i = 0; i < matrix->rows; i++) {
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueInt32(matrix, i, j, &ivalue);
                isumvalue += ivalue;
            }
        }
        harkio_Matrix_setValueInt32(ret, 0, 0, isumvalue);
        break;
    case harkio_DataType_FLOAT32:
        fsumvalue = 0.0;
        for (i = 0; i < matrix->rows; i++) {
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueFloat32(matrix, i, j, &fvalue);
                fsumvalue += fvalue;
            }
        }
        harkio_Matrix_setValueFloat32(ret, 0, 0, fsumvalue);
        break;
    case harkio_DataType_COMPLEX:
        csumvalue = 0.0;
        for (i = 0; i < matrix->rows; i++) {
            for (j = 0; j < matrix->cols; j++) {
                harkio_Matrix_getValueComplex(matrix, i, j, &cvalue);
                csumvalue += cvalue;
            }
        }
        harkio_Matrix_setValueComplex(ret, 0, 0, csumvalue);
        break;
    default:
        harkio_Log_printf(harkio_Log_Error, "Matrix type is Illegal value");
        return NULL;
    }

    return ret;
}
