/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

#ifndef _MATRIX_F32_H_
#define _MATRIX_F32_H_

/*! \brief  Contains public interface to various functions related
 *          to the matrix object using float32_t.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
 * the defines
 ******************************************************************************/

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief Defines the initial parameters for matrix object
 */
typedef struct _MatrixF32Type_
{
    float   *pContainer; /*!< Pointer to the matrix table container */
    uint16_t rowNum;     /*!< Total row number of the container */
    uint16_t columnNum;  /*!< Total column number of the container */
} MatrixF32Type;

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the function prototypes
 ******************************************************************************/

/*! \brief  Initialize the MATRIX_F32 object.
 *
 * This function initialize the MATRIX_F32 object.
 * 
 * \note The container should be the 2-D array
 *
 * \param[in] obj : pointer to Matrix_f32 instance
 * \param[in] pContainer : pointer to the container
 * \param[in] rowNum : the row number of the 2-D array
 * \param[in] columnNum : the column number of the 2-D array
 */
static inline void MatrixF32_Init(MatrixF32Type *obj, float *pContainer, uint16_t rowNum, uint16_t columnNum)
{
    obj->pContainer = pContainer;
    obj->rowNum     = rowNum;
    obj->columnNum  = columnNum;
}

/*! \brief  Set the value to the MATRIX_F32 element.
 * 
 * This function sets the value to the specific position.
 * 
 * \note The row and column start with 0
 *       The first element is row 0 and column 0
 *
 * \param[in] obj : pointer to Matrix_f32 instance
 * \param[in] value : the value to be set
 * \param[in] row : the row index to set value
 * \param[in] column : the column index to set value
 * \return the result of the operation
 *         - true : success
 *         - false : failed
 */
static inline bool MatrixF32_SetValue(MatrixF32Type *obj, float value, uint16_t row, uint16_t column)
{
    if(row >= obj->rowNum || column >= obj->columnNum)
    {
        return false;
    }

    (*(obj->pContainer + column + row * obj->columnNum)) = value;

    return true;
}

/*! \brief  Set the "other" matrix to this MATRIX.
 *
 * This function set the whole matrix values with another matrix
 *
 * \param[in] obj : pointer to Matrix_f32 instance
 * \param[in] other : the pointer to the other matrix
 * \return the result of the operation
 *         - true : success
 *         - false : failed
 */
static inline bool MatrixF32_SetMatrix(MatrixF32Type *obj, const MatrixF32Type *other)
{
    if(obj->rowNum != other->rowNum || obj->columnNum != other->columnNum)
    {
        return false;
    }

    uint16_t i;
    uint16_t j;
    for(i = 0; i < obj->rowNum; i++)
    {
        for(j = 0; j < obj->columnNum; j++)
        {
            (*(obj->pContainer + j + i * obj->columnNum)) = (*(other->pContainer + j + i * obj->columnNum));
        }
    }

    return true;
}

/*! \brief Get the value of the MATRIX_F32 element.
 * 
 * This function gets the value of the specific position
 * 
 * \note The row and column start with 0
 *       The first element is row 0 and column 0
 *
 * \param[in] obj : pointer to Matrix_f32 instance
 * \param[out] pValue : pointer to the output value
 * \param[in] row : the row index
 * \param[in] column : the column index
 * \return the result of the operation
 *         - true : success
 *         - false : failed
 */
static inline bool MatrixF32_GetValue(const MatrixF32Type *obj, float *pValue, uint16_t row, uint16_t column)
{
    if(row >= obj->rowNum || column >= obj->columnNum)
    {
        return false;
    }

    *pValue = (*(obj->pContainer + column + row * obj->columnNum));

    return true;
}

/*! \brief multiply 2 matrix
 * 
 * This function multiply 2 matrix
 * 
 * \note The formula is S = LR, return false if mismatch, else return true.
 *
 * \param[in] pL : pointer to the left matrix
 * \param[in] pR : pointer to the right matrix
 * \param[out] pS : pointer to the product matrix
 * \return the result of the operation
 *         - true : success
 *         - false : failed
 */
static inline bool MatrixF32_Multiply(const MatrixF32Type *pL, const MatrixF32Type *pR, MatrixF32Type *pS)
{
    uint16_t i;
    uint16_t j;

    if((pL->columnNum != pR->rowNum) || (pR->columnNum > 1))
    {
        return false;
    }

    for(i = 0; i < pL->rowNum; i++)
    {
        *(pS->pContainer + i) = 0;
        for(j = 0; j < pR->rowNum; j++)
        {
            (*(pS->pContainer + i)) += ((*(pL->pContainer + j + i * pR->rowNum)) * (*(pR->pContainer + j)));
        }
    }

    return true;
}

/*! \brief Add 2 matrix
 * 
 * This function add 2 matrix.
 * 
 * \note The formula is S = L+R, return false if mismatch, else return true.
 *
 * \param[in] pL : pointer to the left matrix
 * \param[in] pR : pointer to the right matrix
 * \param[out] pS : pointer to the sum matrix
 * \return the result of the operation
 *         - true : success
 *         - false : failed
 */
static inline bool MatrixF32_Add(const MatrixF32Type *pL, const MatrixF32Type *pR, MatrixF32Type *pS)
{
    uint16_t i;
    uint16_t j;

    if((pL->columnNum != pR->columnNum) && (pL->rowNum != pR->rowNum))
    {
        return false;
    }

    for(i = 0; i < pL->rowNum; i++)
    {
        for(j = 0; j < pL->columnNum; j++)
        {
            (*(pS->pContainer + j + i * pL->columnNum))
                = (*(pL->pContainer + j + i * pL->columnNum)) + (*(pR->pContainer + j + i * pL->columnNum));
        }
    }

    return true;
}

#ifdef __cplusplus
}
#endif /* extern "C" */

#endif /* _MATRIX_F32_H_ */
