/**@file
 *
 *      authors: Kunibert G. Siebert
 *               Institut fuer Mathematik
 *               Universitaet Augsburg
 *               Universitaetsstr. 14
 *               D-86159 Augsburg
 *               Germany
 *               siebert@math.uni-augsburg.de
 *
 *               Claus-Justus Heine
 *               Abteilung fuer Angewandte Mathematik                     
 *               Albert-Ludwigs-Universitaet Freiburg
 *               Hermann-Herder-Str. 10
 *               79104 Freiburg
 *               Germany
 *               claus@mathematik.uni-freiburg.de
 *
 *   Copyright (c) by K.G. Siebert (1998-2002),
 *                    C.-J. Heine (2003-2009)
 *
 *
 * Header file for CRS-matrix stuff.
 */
#ifndef _ALBERTA_CRS_MATRIX_H_
#define _ALBERTA_CRS_MATRIX_H_

#include <alberta.h>

/**@addtogroup CRS_MATRIX
 *
 * Support for Compressed Row Storage matrices. For a description see
 * <A HREF="http://www.netlib.org/templates/templates.ps">Templates
 * for the Solution of Linear Systems: Building blocks for Iterative
 * Methods</A>, for example.
 *
 * @{
 */

/** The data-type used for indexing. Because @a DOF is an integer
 * ATM we have 32bit indexing here. 
 */
typedef int CRS_IDX;
/** The data-type to index entries in ::CRS_MATRIX::entries; we try to
 * get away with DOF also, but in principle this might be larger. But
 * then ::CRS_MATRIX_INFO::row would consume twice as much space as it
 * does now.
 */
typedef unsigned int CRS_OFFSET;

/** Data shared across all matrices of the same admin. */
typedef struct crs_matrix_info {
  const FE_SPACE      *row_fe_space; /**< The fe-space we belong to. */
  const DOF_SCHAR_VEC *bound;        /**< Boundary information (rows). */
  bool                free_bound;
  const FE_SPACE      *col_fe_space; /**< Maybe different from
				      * fe_space, NULL if not
				      * needed.
				      */
  CRS_IDX             dim;       /**< The row dimension. */
  CRS_OFFSET          n_entries; /**< This might be larger than (1 << 32). */
  size_t              n_alloc;   /**< This one also. */
  CRS_IDX             *col;      /**< col[i] is the column number of
				  * ::CRS_MATRIX::entries[i]. This
				  * field has size
				  * ::CRS_MATRIX_INFO::n_entries.
				  */
  CRS_OFFSET          *row;      /**< row[k] is the offset of row k
				  * into the field
				  * ::CRS_MATRIX::entries.  This field
				  * has size ::CRS_MATRIX_INFO::dim+1;
				  * the last value is equal to
				  * ::CRS_MATRIX_INFO::n_entries.
				  */
  CRS_IDX             *P, *PI;   /**< permutations to be applied prior
				  * to multiplication
				  */

  DBL_LIST_NODE       matrices;  /**< Linked list of associated matrices. */
} CRS_MATRIX_INFO;

/** Data needed to describe a CRS-matrix. We use a single structure
 * for all kind of CRS-matrices, may they contain integers, REALs or
 * REAL_Ds or whatever. See CRS_MATRIX->entry_size
 */
typedef struct crs_matrix
{
  const CRS_MATRIX_INFO *info;      /**< Matrix info pointer, shared between
				     * structural identical matrices.
				     */
  const char            *name;      /**< A fancy name for this object. */
  void                  *entries;   /**< The matrix entries. */
  size_t                entry_size; /**< The size of a matrix element. */
  size_t                n_alloc;    /**< How many entries are
				     * allocated. This might be larger
				     * than matrix->info->n_entries.
				     */
  DBL_LIST_NODE         node;       /**< List-node for info->matrices. */
} CRS_MATRIX;

/** A macro for easy access to a matrix element. Shoot me. */
#define CRS_VALUE(mat, i, type) (((type *)((mat)->entries))[i])

/** Initialise an entry to 1.0. */
static inline void SET_S_ID(REAL *entries, int where)
{
  entries[where] = 1.0;
}

/** Initialise an entry to a unity block matrix. */
static inline void SET_B_ID(REAL_DD *entries, int where)
{
  int i, j;

  for (i = 0; i < DIM_OF_WORLD; i++) {
    entries[where][i][i] = 1.0;
    for (j = 0; j < i; j++) {
      entries[where][i][j] = entries[where][j][i] = 0.0;
    }
  }
}

/** Check for enough space in an CRS_MATRIX_INFO block. */
static inline void crs_matrix_info_alloc_check(CRS_MATRIX_INFO *info)
{
  if (info->n_alloc == info->n_entries) {
    info->col = MEM_REALLOC(info->col,
			    info->n_alloc,
			    info->n_alloc + info->dim, int);
    info->n_alloc += info->dim;
  }
}

/** Check for enough space in a CRS_MATRIX. */
static inline void crs_matrix_alloc_check(CRS_MATRIX *M)
{
  if (M->n_alloc == M->info->n_entries) {
    M->entries  = MEM_REALLOC(M->entries,
			      M->info->n_alloc*M->entry_size,
			      (M->n_alloc + M->info->dim)*M->entry_size, char);
    M->n_alloc += M->info->dim;
  }
}

extern CRS_MATRIX *crs_matrix_get(const char *name,
				  size_t entry_size,
				  const FE_SPACE *fe_space,
				  const DOF_SCHAR_VEC *bound,
				  const FE_SPACE *col_fe_space,
				  const CRS_MATRIX_INFO *info);
/** Get a scalar CRS-matrix. */
#define crs_s_matrix_get(name, fe_space, bound, flags) \
  get_crs_matrix(name, sizeof(REAL), fe_space, bound, NULL, flags, NULL)
/** Get a DIM_OF_WORLD block CRS-matrix. */
#define crs_b_matrix_get(name, fe_space, bound, flags) \
  get_crs_matrix(name, sizeof(REAL_DD), fe_space, bound, NULL, flags, NULL)
extern void crs_matrix_free(CRS_MATRIX *mat);

extern CRS_MATRIX_INFO *crs_matrix_info_alloc(int dim, int n_alloc);
extern void crs_matrix_info_trim(CRS_MATRIX_INFO *info);
extern void crs_matrix_info_free(CRS_MATRIX_INFO *info);

extern void crs_matrix_print_maple(const CRS_MATRIX *mat);
extern void crs_matrix_print_debug(const CRS_MATRIX *mat);
extern void crs_matrix_print(const CRS_MATRIX *mat);
extern void crs_matrix_optimize_profile(const CRS_MATRIX *A,
					int *P, int *PI,
					int *band_width,
					int64_t *profile,
					int optpro,
					int info_level);

/**@} CRS_MATRIX */

#endif

