#ifndef  DEF_MULTI_DIM
#define  DEF_MULTI_DIM

/* ----------------------------------------------------------------------------
@COPYRIGHT  :
              Copyright 1993,1994,1995 David MacDonald,
              McConnell Brain Imaging Centre,
              Montreal Neurological Institute, McGill University.
              Permission to use, copy, modify, and distribute this
              software and its documentation for any purpose and without
              fee is hereby granted, provided that the above copyright
              notice appear in all copies.  The author and McGill University
              make no representations about the suitability of this
              software for any purpose.  It is provided "as is" without
              express or implied warranty.
@VERSION    : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/multidim.h,v 1.7 2005/05/19 21:19:27 bert Exp $
---------------------------------------------------------------------------- */

/* ----------------------------- MNI Header -----------------------------------
@NAME       : multidim.h
@INPUT      : 
@OUTPUT     : 
@RETURNS    : 
@DESCRIPTION: Multidimensional variable type arrays.
@METHOD     : 
@GLOBALS    : 
@CALLS      : 
@CREATED    : Aug. 14, 1995   David MacDonald
@MODIFIED   : 
---------------------------------------------------------------------------- */

#define  VIO_MAX_DIMENSIONS     5

/* -------------------------- Data_types ------------------------- */

typedef  enum  { NO_DATA_TYPE,
                 UNSIGNED_BYTE,
                 SIGNED_BYTE,
                 UNSIGNED_SHORT,
                 SIGNED_SHORT,
                 UNSIGNED_INT,
                 SIGNED_INT,
                 FLOAT,
                 DOUBLE,
                 MAX_DATA_TYPE }   VIO_Data_types;

typedef  struct
{
    int                     n_dimensions;
    int                     sizes[VIO_MAX_DIMENSIONS];
    VIO_Data_types          data_type;
    void                    *data;
} VIO_multidim_array;

/* ------------------------- set value ---------------------------- */

/* --- private macros */

#define  SET_ONE( array, type, asterisks, subscripts, value )   \
         (((type asterisks) ((array).data))  subscripts = (type) (value))

#define  SET_GIVEN_DIM( array, asterisks, subscripts, value )   \
         switch( (array).data_type )  \
         {  \
         case UNSIGNED_BYTE:  \
             SET_ONE( array, unsigned char, asterisks, subscripts, value);\
             break;  \
         case SIGNED_BYTE:  \
             SET_ONE( array, signed char, asterisks, subscripts, value);\
             break;  \
         case UNSIGNED_SHORT:  \
             SET_ONE( array, unsigned short, asterisks, subscripts, value);\
             break;  \
         case SIGNED_SHORT:  \
             SET_ONE( array, signed short, asterisks, subscripts, value);\
             break;  \
         case UNSIGNED_INT:  \
             SET_ONE( array, unsigned int, asterisks, subscripts, value);\
             break;  \
         case SIGNED_INT:  \
             SET_ONE( array, signed int, asterisks, subscripts, value);\
             break;  \
         case FLOAT:  \
             SET_ONE( array, float, asterisks, subscripts, value);\
             break;  \
         case DOUBLE:  \
             SET_ONE( array, double, asterisks, subscripts, value);\
             break;  \
         }

#define  GET_MULTIDIM_TYPE_1D( array, type, v0 )   \
         (((type *) ((array).data)) [v0])

#define  GET_MULTIDIM_TYPE_2D( array, type, v0, v1 )   \
         (((type **) ((array).data)) [v0][v1])

#define  GET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2 )   \
         (((type ***) ((array).data)) [v0][v1][v2])

#define  GET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3 )   \
         (((type ****) ((array).data)) [v0][v1][v2][v3])

#define  GET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4 )   \
         (((type *****) ((array).data)) [v0][v1][v2][v3][v4])

#define  SET_MULTIDIM_TYPE_1D( array, type, v0, value )   \
           (GET_MULTIDIM_TYPE_1D( array, type, v0 ) = (type) (value))

#define  SET_MULTIDIM_TYPE_2D( array, type, v0, v1, value )   \
           (GET_MULTIDIM_TYPE_2D( array, type, v0, v1 ) = (type) (value))

#define  SET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2, value )   \
           (GET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2 ) = (type) (value))

#define  SET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3, value )   \
        (GET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3 ) = (type) (value))

#define  SET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4, value )   \
      (GET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4 ) = (type) (value))

/* --- public macros to set the [x][y]... voxel of 'array' to 'value' */

#define  SET_MULTIDIM_1D( array, x, value )       \
           SET_GIVEN_DIM( array, *, [x], value )

#define  SET_MULTIDIM_2D( array, x, y, value )       \
           SET_GIVEN_DIM( array, **, [x][y], value )

#define  SET_MULTIDIM_3D( array, x, y, z, value )       \
           SET_GIVEN_DIM( array, ***, [x][y][z], value )

#define  SET_MULTIDIM_4D( array, x, y, z, t, value )       \
           SET_GIVEN_DIM( array, ****, [x][y][z][t], value )

#define  SET_MULTIDIM_5D( array, x, y, z, t, v, value )       \
           SET_GIVEN_DIM( array, *****, [x][y][z][t][v], value )

/* --- same as previous, but don't have to know dimensions of volume */

#define  SET_MULTIDIM( array, x, y, z, t, v, value )       \
         switch( (array).n_dimensions ) \
         { \
         case 1:  SET_MULTIDIM_1D( array, x, value );              break; \
         case 2:  SET_MULTIDIM_2D( array, x, y, value );           break; \
         case 3:  SET_MULTIDIM_3D( array, x, y, z, value );        break; \
         case 4:  SET_MULTIDIM_4D( array, x, y, z, t, value );     break; \
         case 5:  SET_MULTIDIM_5D( array, x, y, z, t, v, value );  break; \
         }

/* ------------------------- get multidim value ------------------------ */

/* --- private macros */

#define  GET_ONE( value, vtype, array, type, asterisks, subscripts )   \
         (value) = vtype (((type asterisks) ((array).data))  subscripts)

#define  GET_GIVEN_DIM( value, vtype, array, asterisks, subscripts )   \
         switch( (array).data_type )  \
         {  \
         case UNSIGNED_BYTE:  \
             GET_ONE( value, vtype, array, unsigned char, asterisks, subscripts );\
             break;  \
         case SIGNED_BYTE:  \
             GET_ONE( value, vtype, array, signed char, asterisks, subscripts );\
             break;  \
         case UNSIGNED_SHORT:  \
             GET_ONE( value, vtype, array, unsigned short, asterisks, subscripts );\
             break;  \
         case SIGNED_SHORT:  \
             GET_ONE( value, vtype, array, signed short, asterisks, subscripts );\
             break;  \
         case UNSIGNED_INT:  \
             GET_ONE( value, vtype, array, unsigned int, asterisks, subscripts );\
             break;  \
         case SIGNED_INT:  \
             GET_ONE( value, vtype, array, signed int, asterisks, subscripts );\
             break;  \
         case FLOAT:  \
             GET_ONE( value, vtype, array, float, asterisks, subscripts );\
             break;  \
         case DOUBLE:  \
             GET_ONE( value, vtype, array, double, asterisks, subscripts );\
             break;  \
         }

/* --- public macros to place the [x][y]...'th voxel of 'array' in 'value' */

#define  GET_MULTIDIM_1D( value, vtype, array, x )       \
           GET_GIVEN_DIM( value, vtype, array, *, [x] )

#define  GET_MULTIDIM_2D( value, vtype, array, x, y )       \
           GET_GIVEN_DIM( value, vtype, array, **, [x][y] )

#define  GET_MULTIDIM_3D( value, vtype, array, x, y, z )       \
           GET_GIVEN_DIM( value, vtype, array, ***, [x][y][z] )

#define  GET_MULTIDIM_4D( value, vtype, array, x, y, z, t )       \
           GET_GIVEN_DIM( value, vtype, array, ****, [x][y][z][t] )

#define  GET_MULTIDIM_5D( value, vtype, array, x, y, z, t, v )       \
           GET_GIVEN_DIM( value, vtype, array, *****, [x][y][z][t][v] )

/* --- same as previous, but no need to know array dimensions */

#define  GET_MULTIDIM( value, vtype, array, x, y, z, t, v )       \
         switch( (array).n_dimensions ) \
         { \
         case 1:  GET_MULTIDIM_1D( value, vtype, array, x );              break; \
         case 2:  GET_MULTIDIM_2D( value, vtype, array, x, y );           break; \
         case 3:  GET_MULTIDIM_3D( value, vtype, array, x, y, z );        break; \
         case 4:  GET_MULTIDIM_4D( value, vtype, array, x, y, z, t );     break; \
         case 5:  GET_MULTIDIM_5D( value, vtype, array, x, y, z, t, v );  break; \
         }

/* ------------------------- get multidim ptr ------------------------ */

/* --- private macros */

#define  GET_ONE_PTR( ptr, array, type, asterisks, subscripts )   \
         (ptr) = (void *) (&(((type asterisks) ((array).data))  subscripts))

#define  GET_GIVEN_DIM_PTR( ptr, array, asterisks, subscripts )   \
         switch( (array).data_type )  \
         {  \
         case UNSIGNED_BYTE:  \
             GET_ONE_PTR( ptr, array, unsigned char, asterisks, subscripts );\
             break;  \
         case SIGNED_BYTE:  \
             GET_ONE_PTR( ptr, array, signed char, asterisks, subscripts );\
             break;  \
         case UNSIGNED_SHORT:  \
             GET_ONE_PTR( ptr, array, unsigned short, asterisks, subscripts );\
             break;  \
         case SIGNED_SHORT:  \
             GET_ONE_PTR( ptr, array, signed short, asterisks, subscripts );\
             break;  \
         case UNSIGNED_INT:  \
             GET_ONE_PTR( ptr, array, unsigned int, asterisks, subscripts );\
             break;  \
         case SIGNED_INT:  \
             GET_ONE_PTR( ptr, array, signed int, asterisks, subscripts );\
             break;  \
         case FLOAT:  \
             GET_ONE_PTR( ptr, array, float, asterisks, subscripts );\
             break;  \
         case DOUBLE:  \
             GET_ONE_PTR( ptr, array, double, asterisks, subscripts );\
             break;  \
         }

/* --- public macros to return a pointer to the [x][y]'th voxel of the
       'array', and place it in 'ptr' */

#define  GET_MULTIDIM_PTR_1D( ptr, array, x )       \
           GET_GIVEN_DIM_PTR( ptr, array, *, [x] )

#define  GET_MULTIDIM_PTR_2D( ptr, array, x, y )       \
           GET_GIVEN_DIM_PTR( ptr, array, **, [x][y] )

#define  GET_MULTIDIM_PTR_3D( ptr, array, x, y, z )       \
           GET_GIVEN_DIM_PTR( ptr, array, ***, [x][y][z] )

#define  GET_MULTIDIM_PTR_4D( ptr, array, x, y, z, t )       \
           GET_GIVEN_DIM_PTR( ptr, array, ****, [x][y][z][t] )

#define  GET_MULTIDIM_PTR_5D( ptr, array, x, y, z, t, v )       \
           GET_GIVEN_DIM_PTR( ptr, array, *****, [x][y][z][t][v] )

/* --- same as previous, but no need to know array dimensions */

#define  GET_MULTIDIM_PTR( ptr, array, x, y, z, t, v )       \
         switch( (array).n_dimensions ) \
         { \
         case 1:  GET_MULTIDIM_PTR_1D( ptr, array, x );              break; \
         case 2:  GET_MULTIDIM_PTR_2D( ptr, array, x, y );           break; \
         case 3:  GET_MULTIDIM_PTR_3D( ptr, array, x, y, z );        break; \
         case 4:  GET_MULTIDIM_PTR_4D( ptr, array, x, y, z, t );     break; \
         case 5:  GET_MULTIDIM_PTR_5D( ptr, array, x, y, z, t, v );  break; \
         }

#if !VIO_PREFIX_NAMES
typedef VIO_multidim_array multidim_array;
typedef VIO_Data_types Data_types;
#define MAX_DIMENSIONS VIO_MAX_DIMENSIONS
#endif /* !VIO_PREFIX_NAMES */

#endif
