/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Functions to handle dynamical multi-dimensional arrays
 * 
 * Tables of pointers are used in order to make easier the access to arrays 
 * elements by a classical indexation method : tab[i][j] or tab[i][j][k]. 
 * If required, the memory allocated for elements is contiguous, the type of 
 * stored elements is specified in the function name. Elements are initialized 
 * to 0. If not, tables of pointers just wrap already assigned arrays.
 */

/* 
 * System Headers
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 
 * Local Headers
 */
#include "amdlib.h"
#include "amdlibProtected.h"

/*
 * Protected functions definition
 */
/**
 * Allocate memory for 2-dimensional array of unsigned char.
 *
 * This function allocates a 2-dimensional array of unsigned char. Elements are
 * initialized to 0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
unsigned char **amdlibAlloc2DArrayUnsignedChar(const int       firstDim, 
                                               const int       secondDim,
                                               amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }
    
    int i = 0;
    /* Create the table of pointers */
    unsigned char **tabOfPtr = 
        (unsigned char **)calloc(secondDim, sizeof(unsigned char *));
    tabOfPtr[0] = 
        (unsigned char *)calloc(firstDim * secondDim, sizeof(unsigned char));
    for (i=1; i < secondDim; ++i)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*firstDim; 
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0], '\0', firstDim * secondDim * sizeof(unsigned char));
    return tabOfPtr;
}

/**
 * Allocate memory for 2-dimensional array of double.
 *
 * This function allocates a 2-dimensional array of double. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
double **amdlibAlloc2DArrayDouble(const int       firstDim, 
                                  const int       secondDim,
                                  amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }
    
    int i = 0;
    /* Create the table of pointers */
    double ** tabOfPtr = (double **)calloc(secondDim, sizeof(double *));
    tabOfPtr[0] = (double *)calloc(firstDim * secondDim, sizeof(double));
    for (i=1; i < secondDim; ++i)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*firstDim; 
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0], '\0', firstDim * secondDim * sizeof(double));
    return tabOfPtr;
}

/**
 * Allocate memory for 3-dimensional array of double.
 *
 * This function allocates a 3-dimensional array of double. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param thirdDim third dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
double ***amdlibAlloc3DArrayDouble(const int       firstDim,
                                   const int       secondDim,
                                   const int       thirdDim,
                                   amdlibERROR_MSG errMsg)
{  
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0 || thirdDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }

    int i = 0;
    int j = 0;
    /* Create the table of pointers */
    double ***tabOfPtr = (double ***)calloc(thirdDim, sizeof(double **));
    tabOfPtr[0] = (double **)calloc(secondDim * thirdDim, sizeof(double *));
    tabOfPtr[0][0] = 
        (double *)calloc(firstDim * secondDim * thirdDim, sizeof(double));
    for(i=0; i<thirdDim; i++)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*secondDim; 
        for(j=0; j<secondDim; j++)
        {
            tabOfPtr[i][j] = tabOfPtr[0][0] + i*firstDim*secondDim + j*firstDim;
        }
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0][0], '\0', 
           firstDim * secondDim * thirdDim * sizeof(double));
    return tabOfPtr;
}

/**
 * Allocate memory for 2-dimensional array of float.
 *
 * This function allocates a 2-dimensional array of float. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
float **amdlibAlloc2DArrayFloat(const int       firstDim, 
                                const int       secondDim,
                                amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }
    
    int i = 0;
    /* Create the table of pointers */
    float **tabOfPtr = (float **)calloc(secondDim, sizeof(float *));
    tabOfPtr[0] = (float *)calloc(firstDim * secondDim, sizeof(float));
    for (i=1; i < secondDim; ++i)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*firstDim; 
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0], '\0', firstDim * secondDim * sizeof(float));
    return tabOfPtr;
}

/**
 * Allocate memory for 3-dimensional array of float.
 *
 * This function allocates a 3-dimensional array of float. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param thirdDim third dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
float ***amdlibAlloc3DArrayFloat(const int       firstDim,
                                 const int       secondDim,
                                 const int       thirdDim,
                                 amdlibERROR_MSG errMsg)
{  
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0 || thirdDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }

    int i = 0;
    int j = 0;

    /* Create the table of pointers */
    float ***tabOfPtr = (float ***)calloc(thirdDim, sizeof(float **));
    tabOfPtr[0] = (float **)calloc(secondDim * thirdDim, sizeof(float *));
    tabOfPtr[0][0] = 
        (float *)calloc(firstDim * secondDim * thirdDim, sizeof(float));
    for(i=0; i<thirdDim; i++)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*secondDim; 
        for(j=0; j<secondDim; j++)
        {
            tabOfPtr[i][j] = tabOfPtr[0][0] + i*firstDim*secondDim + j*firstDim;
        }
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0][0], '\0', 
           firstDim * secondDim * thirdDim * sizeof(float));
    return tabOfPtr;
}

/**
 * Allocate memory for 2-dimensional array of amdlibCOMPLEX.
 *
 * This function allocates a 2-dimensional array of amdlibCOMPLEX. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPLEX **amdlibAlloc2DArrayComplex(const int       firstDim, 
                                          const int       secondDim,
                                          amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }
    
    int i = 0;
    /* Create the table of pointers */
    amdlibCOMPLEX **tabOfPtr = (amdlibCOMPLEX **)calloc(secondDim, 
                                                        sizeof(amdlibCOMPLEX*));
    tabOfPtr[0] = (amdlibCOMPLEX *)calloc(firstDim * secondDim, 
                                          sizeof(amdlibCOMPLEX));
    for (i=1; i < secondDim; ++i)
    {
        tabOfPtr[i] = tabOfPtr[0] + i * firstDim; 
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0], '\0', firstDim * secondDim * sizeof(amdlibCOMPLEX));
    return tabOfPtr;
}

/**
 * Allocate memory for 3-dimensional array of amdlibCOMPLEX.
 *
 * This function allocates a 3-dimensional array of amdlibCOMPLEX. Elements are
 * initialized to 0.0.
 * 
 * @param firstDim first dimension of allocated array.
 * @param secondDim second dimension of allocated array.
 * @param thirdDim third dimension of allocated array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return
 * pointer on amdlibCOMPLEX 3-dimensional array on successful completion. 
 * Otherwise NULL is returned.
 */
amdlibCOMPLEX ***amdlibAlloc3DArrayComplex(const int       firstDim, 
                                           const int       secondDim,
                                           const int       thirdDim,
                                           amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0 || thirdDim == 0)
    {
        amdlibSetErrMsg("One of the amdlibCOMPLEX 3D-array dimension is null");
        return NULL;
    }
    
    int i = 0;
    int j = 0;
    
    /* Create the table of pointers */
    amdlibCOMPLEX ***tabOfPtr = (amdlibCOMPLEX ***)calloc(thirdDim, 
                                                        sizeof(amdlibCOMPLEX**));
    tabOfPtr[0] = (amdlibCOMPLEX **)calloc(secondDim * thirdDim, 
                                                        sizeof(amdlibCOMPLEX*));
    tabOfPtr[0][0] = (amdlibCOMPLEX *)calloc(firstDim * secondDim *thirdDim, 
                                          sizeof(amdlibCOMPLEX));
    for(i=0; i<thirdDim; i++)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*secondDim; 
        for(j=0; j<secondDim; j++)
        {
            tabOfPtr[i][j] = tabOfPtr[0][0] + i*firstDim*secondDim + j*firstDim;
        }
    }
    /* Initialize the data structure with 0 */
    memset(tabOfPtr[0][0], '\0', 
           firstDim * secondDim * thirdDim * sizeof(amdlibCOMPLEX));

    return tabOfPtr;
}

/**
 * Wrap a 2D-array.
 *
 * This function enables the wrapping of a 2D array of element by a table of
 * pointers, which allows to use classical indexation in order to access its
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param elemSize size of element in array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
void **amdlibWrap2DArray(void *          array,
                         const int       firstDim, 
                         const int       secondDim,
                         const int       elemSize, 
                         amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }

    /* Check array to wrap is not NULL */
    if (array == NULL)
    {
        amdlibSetErrMsg("Array to wrap NULL");
        return NULL;
    }
    
    int i = 0;
    /* Create the table of pointers */
    void **tabOfPtr = (void **)calloc(secondDim, sizeof(void *));
    /* No allocation of memory for data, it points on the input array */
    tabOfPtr[0] = array;
    for (i=1; i < secondDim; ++i)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*firstDim*elemSize; 
    }
    return tabOfPtr;
}

/**
 * Wrap a 3D-array.
 *
 * This function enables the wrapping of a 3D array of elements by a table of 
 * pointers, which allows to use classical indexation in order to access its 
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param thirdDim third dimension of array.
 * @param elemSize size of element in array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
void ***amdlibWrap3DArray(void *          array,
                          const int       firstDim,
                          const int       secondDim,
                          const int       thirdDim,
                          const int       elemSize, 
                          amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0 || thirdDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }

    /* Check array to wrap is not NULL */
    if (array == NULL)
    {
        amdlibSetErrMsg("Array to wrap NULL");
        return NULL;
    }

    int i = 0;
    int j = 0;
    /* Create the table of pointers */
    void ***tabOfPtr = (void ***)calloc(thirdDim, sizeof(void **));
    tabOfPtr[0] = (void **)calloc(secondDim * thirdDim, sizeof(void *));
    /* No allocation of memory for data, it points on the input array */
    tabOfPtr[0][0] = array;
    for(i=0; i<thirdDim; i++)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*secondDim;
        for(j=0; j<secondDim; j++)
        {
            tabOfPtr[i][j] = tabOfPtr[0][0] + 
                (i*firstDim*secondDim + j*firstDim)*elemSize;
        }
    }
    return tabOfPtr;
}

/**
 * Wrap a 4D-array.
 *
 * This function enables the wrapping of a 3D array of elements by a table of 
 * pointers, which allows to use classical indexation in order to access its 
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param thirdDim third dimension of array.
 * @param fourthDim third dimension of array.
 * @param elemSize size of element in array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
void ****amdlibWrap4DArray(void *          array,
                          const int       firstDim,
                          const int       secondDim,
                          const int       thirdDim,
                          const int       fourthDim,
                          const int       elemSize, 
                          amdlibERROR_MSG errMsg)
{
    /* Check if each dimension is != 0 */
    if (firstDim == 0 || secondDim == 0 || thirdDim == 0 || fourthDim == 0)
    {
        amdlibSetErrMsg("Nil dimension");
        return NULL;
    }

    /* Check array to wrap is not NULL */
    if (array == NULL)
    {
        amdlibSetErrMsg("Array to wrap NULL");
        return NULL;
    }

    int i = 0;
    int j = 0;
    int k =0;
    /* Create the table of pointers */
    void ****tabOfPtr = (void ****)calloc(fourthDim, sizeof(void ***));
    tabOfPtr[0] = (void ***)calloc(thirdDim * fourthDim, sizeof(void **));
    tabOfPtr[0][0] = (void **)calloc(secondDim * thirdDim * fourthDim, sizeof(void *));
    /* No allocation of memory for data, it points on the input array */
    tabOfPtr[0][0][0] = array;

    for(i=0; i<fourthDim; i++)
    {
        tabOfPtr[i] = tabOfPtr[0] + i*thirdDim;
        for(j=0; j<thirdDim; j++)
        {
            tabOfPtr[i][j] = tabOfPtr[0][0] + i*thirdDim*secondDim + j*secondDim;
            for(k=0; k<secondDim; k++)
            {
                tabOfPtr[i][j][k] = tabOfPtr[0][0][0] + 
                (i*firstDim*secondDim*thirdDim + j*firstDim*secondDim + k*firstDim)*elemSize;
            }
        }
    }
    return tabOfPtr;
}


/**
 * Wrap a 2D-array of float.
 *
 * This function enables the wrapping of a 2D array of float by a table of 
 * pointers, which allows to use classical indexation in order to access its
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
float **amdlibWrap2DArrayFloat(float *         array,
                               const int       firstDim, 
                               const int       secondDim,
                               amdlibERROR_MSG errMsg)
{
    return (float **)amdlibWrap2DArray(array, firstDim, secondDim,
                                       sizeof(float), errMsg);
}

/**
 * Wrap a 3D-array of float.
 *
 * This function enables the wrapping of a 3D array of float by a table of 
 * pointers, which allows to use classical indexation in order to access its 
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param thirdDim third dimension of array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
float ***amdlibWrap3DArrayFloat(float *         array,
                                const int       firstDim,
                                const int       secondDim,
                                const int       thirdDim,
                                amdlibERROR_MSG errMsg)
{
    return (float ***)amdlibWrap3DArray(array, firstDim, secondDim, thirdDim, 
                                        sizeof(float), errMsg);
}

/**
 * Wrap a 2D-array of double.
 *
 * This function enables the wrapping of a 2D array of double by a table of 
 * pointers, which allows to use classical indexation in order to access its
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
double **amdlibWrap2DArrayDouble(double *        array,
                                 const int       firstDim, 
                                 const int       secondDim,
                                 amdlibERROR_MSG errMsg)
{
    return (double **)amdlibWrap2DArray(array, firstDim, secondDim,
                                       sizeof(double), errMsg);
}

/**
 * Wrap a 3D-array of double.
 *
 * This function enables the wrapping of a 3D array of double by a table of 
 * pointers, which allows to use classical indexation in order to access its 
 * elements.
 *
 * @warning dimensions are supposed to be correct.
 * 
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param thirdDim third dimension of array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
double ***amdlibWrap3DArrayDouble(double *         array,
                                  const int       firstDim,
                                  const int       secondDim,
                                  const int       thirdDim,
                                  amdlibERROR_MSG errMsg)
{
    double ***tabOfPtr = 
        (double ***)amdlibWrap3DArray(array, firstDim, secondDim, thirdDim, 
                                     sizeof(double), errMsg);
    return tabOfPtr;
}

double ****amdlibWrap4DArrayDouble(double *         array,
                                  const int       firstDim,
                                  const int       secondDim,
                                  const int       thirdDim,
                                  const int       fourthDim,
                                  amdlibERROR_MSG errMsg)
{
    double ****tabOfPtr = 
        (double ****)amdlibWrap4DArray(array, firstDim, secondDim, 
                                       thirdDim, fourthDim,
                                       sizeof(double), errMsg);
    return tabOfPtr;
}

/**
 * Wrap a 2D-array of unsigned char.
 *
 * This function enables the wrapping of a 2D array of unsigned char by a 
 * table of pointers, which allows to use classical indexation in order 
 * to access its elements.
 * 
 * @warning dimensions are supposed to be correct.
 *
 * @param array array to wrap.
 * @param firstDim first dimension of array.
 * @param secondDim second dimension of array.
 * @param errMsg error description message returned if function fails.
 * 
 * @return the wrapping.
 */
unsigned char **amdlibWrap2DArrayUnsignedChar(unsigned char * array,
                                              const int       firstDim, 
                                              const int       secondDim,
                                              amdlibERROR_MSG errMsg)
{
    unsigned char **tabOfPtr = 
        (unsigned char **)amdlibWrap2DArray(array, firstDim, secondDim,
                                            sizeof(unsigned char), errMsg);
    return tabOfPtr;
}

/**
 * Free a dynamically allocated 2-dimensional array of unsigned char.
 *
 * This function frees a dynamically allocated 2-dimensional array of unsigned 
 * char.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree2DArrayUnsignedChar(unsigned char **arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((unsigned char *)arrayToFree[0]);
        free((unsigned char **)arrayToFree);
        arrayToFree = NULL;
    }
}
    
/**
 * Free a dynamically allocated 2-dimensional array of double.
 *
 * This function frees a dynamically allocated 2-dimensional array of double.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree2DArrayDouble(double **arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((double *)arrayToFree[0]);
        free((double **)arrayToFree);
        arrayToFree = NULL;
    }
}

/**
 * Free a dynamically allocated 3-dimensional array of double.
 *
 * This function frees a dynamically allocated 3-dimensional array of double.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree3DArrayDouble(double ***arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((double *)arrayToFree[0][0]);
        free((double **)arrayToFree[0]);
        free((double ***)arrayToFree);
        arrayToFree = NULL;
    }
}
void amdlibFree4DArrayDouble(double ****arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((double *)arrayToFree[0][0][0]);
        free((double **)arrayToFree[0][0]);
        free((double ***)arrayToFree[0]);
        free((double ****)arrayToFree);
        arrayToFree = NULL;
    }
}
/**
 * Free a dynamically allocated 3-dimensional array of complex.
 *
 * This function frees a dynamically allocated 3-dimensional array of complex.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree3DArrayComplex(amdlibCOMPLEX ***arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((amdlibCOMPLEX *)arrayToFree[0][0]);
        free((amdlibCOMPLEX **)arrayToFree[0]);
        free((amdlibCOMPLEX ***)arrayToFree);
        arrayToFree = NULL;
    }
}

/**
 * Free a dynamically allocated 2-dimensional array of float.
 *
 * This function frees a dynamically allocated 2-dimensional array of float.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree2DArrayFloat(float **arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((float *)arrayToFree[0]);
        free((float **)arrayToFree);
        arrayToFree = NULL;
    }
}

/**
 * Free a dynamically allocated 3-dimensional array of float.
 *
 * This function frees a dynamically allocated 3-dimensional array of float.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree3DArrayFloat(float ***arrayToFree)
{  
    if (arrayToFree != NULL)
    {
        free((float *)arrayToFree[0][0]);
        free((float **)arrayToFree[0]);
        free((float ***)arrayToFree);
        arrayToFree = NULL;
    }
}

/**
 * Free a dynamically allocated 2-dimensional array of amdlibCOMPLEX.
 *
 * This function frees a dynamically allocated 2-dimensional array of
 * amdlibCOMPLEX.
 *
 * @param arrayToFree array to free.
 */
void amdlibFree2DArrayComplex(amdlibCOMPLEX **arrayToFree)
{
    if (arrayToFree != NULL)
    {
        free((amdlibCOMPLEX *)arrayToFree[0]);
        free((amdlibCOMPLEX **)arrayToFree);
        arrayToFree = NULL;
    }
}

/**
 * Free a wrapping on a 2-dimensional array.
 *
 * This function frees a wrapping on 2-dimensional array, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree2DArrayWrapping(void **wrappingToFree)
{
    if (wrappingToFree != NULL)
    {
        free((void **)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 3-dimensional array.
 *
 * This function frees a wrapping on 3-dimensional array, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree3DArrayWrapping(void ***wrappingToFree)
{  
    if (wrappingToFree != NULL)
    {
        free((void **)wrappingToFree[0]);
        free((void ***)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 2-dimensional array of float.
 *
 * This function frees a wrapping on 2-dimensional array of float, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree2DArrayFloatWrapping(float **wrappingToFree)
{
    if (wrappingToFree != NULL)
    {
        free((float **)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 3-dimensional array of float.
 *
 * This function frees a wrapping on 3-dimensional array of float, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree3DArrayFloatWrapping(float ***wrappingToFree)
{  
    if (wrappingToFree != NULL)
    {
        free((float **)wrappingToFree[0]);
        free((float ***)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 2-dimensional array of double.
 *
 * This function frees a wrapping on 2-dimensional array of double, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree2DArrayDoubleWrapping(double **wrappingToFree)
{
    if (wrappingToFree != NULL)
    {
        free((double **)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 3-dimensional array of double.
 *
 * This function frees a wrapping on 3-dimensional array of double, without
 * deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree3DArrayDoubleWrapping(double ***wrappingToFree)
{  
    if (wrappingToFree != NULL)
    {
        free((double **)wrappingToFree[0]);
        free((double ***)wrappingToFree);
        wrappingToFree = NULL;
    }
}
void amdlibFree4DArrayDoubleWrapping(double ****wrappingToFree)
{  
    if (wrappingToFree != NULL)
    {
        free((double **)wrappingToFree[0][0]);
        free((double ***)wrappingToFree[0]);
        free((double ****)wrappingToFree);
        wrappingToFree = NULL;
    }
}

/**
 * Free a wrapping on a 2-dimensional array of unsigned char.
 *
 * This function frees a wrapping on 2-dimensional array of unsigned char, 
 * without deleting data themselves.
 *
 * @param wrappingToFree table of pointers to free.
 */
void amdlibFree2DArrayUnsignedCharWrapping(unsigned char **wrappingToFree)
{
    if (wrappingToFree != NULL)
    {
        free((unsigned char **)wrappingToFree);
        wrappingToFree = NULL;
    }
}


/*___oOo___*/
