/* Proposed changes to some core data structures.
 *
 * Using C++ the structures below would just define interface classes
 * which would be reimplemented using templates; the "this" component
 * passed to the "virtual" methods below would not be present in most
 * cases of the "standard" implementations.
 *
 * 
 *
 */

/* A local function, i.e. one which is evaluated at barycentric
 * coordinates on a given mesh element.
 */
typedef struct local_function   LOCAL_FUNCTION;
typedef struct local_function_D LOCAL_FUNCTION_D;

/* A function which is evaluated at the quadrature points, propably
 * using QUAD_FAST structures.
 */
typedef struct quad_function   QUAD_FUNCTION;
typedef struct quad_function_D QUAD_FUNCTION_D;

/* In C++ derived classes would inherit QUAD_FUNCTION and
 * LOCAL_FUNCTION. Using templates a LOCAL_FUNCTION and a
 * QUAD_FUNCTION would just be interfaces. Using C we could implement
 * inheritance like
 *
 * struct my_local_function
 * {
 *   LOCAL_FUNCTION base;
 *   int foo;
 *   REAL bar;
 * };
 */

/* Vector valued basis functions, but with optimizations for the
 * common case where at least part of the local function space is a
 * Cartesian product of DIM_OF_WORLD many scalar function spaces.
 *
 * For this to work DOF_REAL_D_VECs must not be used, instead one
 * needs distinct DOFs for each component of the basis of the
 * Cartesian product. This needs further changes to the DOF_ADMIN:
 * DOFs positions may now have a "stride" of size DIM_OF_WORLD,
 * meaning that a get_dof() should allocate DIM_OF_WORLD many new DOFs
 * for certain basis functions.
 *
 */
typedef struct bas_fcts_d BAS_FCTS_D;

/* A derived class which overloads LOCAL_FUNCTION. Replaces the
 * functionality of the DOF_REAL_VEC.
 */
typedef struct discrete_function DISCRETE_FUNCTION;
typedef struct discrete_function_d DISCRETE_FUNCTION_D;

struct local_function
{
  /** The values of the function. */
  REAL         (*f)(const REAL_B lambda,  
		    const LOCAL_FUNCTION *this);
  /** The gradient in cartesian coordiantes. */
  const REAL   *(*cart_grd_f)(const REAL_B lambda,
			      const REAL_BD Lambda,
			      const LOCAL_FUNCTION *this);
  /** The gradient in barycentric coordiantes. */
  const REAL   *(*bary_grd_f)(const REAL_B lambda,
			      const LOCAL_FUNCTION *this);
  /** The Hesse matrix in cartesian coordiantes. */
  const REAL_D *(*cart_D2_f)(const REAL_B lambda,
			     const REAL_BD Lambda,
			     const LOCAL_FUNCTION *this);
  /** The Hesse matrix in barycentric coordiantes. */
  const REAL_B *(*bary_D2_f)(const REAL_B lambda,
			     const LOCAL_FUNCTION *this);

  /* A LOCAL_FUNCTION must know for itself how to create a "fast"
   * i.e. caching version of itself.
   */
  const QUAD_FUNCTION *quad_function(const QUAD *quad,
				     const LOCAL_FUNCTION *this);

  /* If per element information is needed for the function evaluation
   * (i.e. the coefficients of a finite element function) then the
   * INIT_ELEMENT() method must provide it.
   */
  INIT_ELEMENT_DECL;
};

struct quad_function
{
  /** The values of the function. */
  REAL         (*f)(int iq, const QUAD_FUNCTION *this);
  /** The gradient in cartesian coordiantes. */
  const REAL   *(*cart_grd_f)(int iq, const REAL_BD Lambda,
			      const QUAD_FUNCTION *this);
  /** The gradient in barycentric coordiantes. */
  const REAL   *(*bary_grd_f)(int iq, const QUAD_FUNCTION *this);
  /** The Hesse matrix in cartesian coordiantes. */
  const REAL_D *(*cart_D2_f)(int iq, const REAL_BD Lambda,
			     const QUAD_FUNCTION *this);
  /** The Hesse matrix in barycentric coordiantes. */
  const REAL_B *(*bary_D2_f)(intg iq, const QUAD_FUNCTION *this);

  const LOCAL_FUNCTION *local_function;
  const QUAD *quad;

  INIT_ELEMENT_DECL;
};

struct local_function_d
{
  /** The values of the function. */
  const REAL    *(*f)(const REAL_B lambda,  
		      const LOCAL_FUNCTION_D *this);
  /** The gradient in cartesian coordiantes. */
  const REAL_D  *(*cart_grd_f)(const REAL_B lambda,
			       const REAL_BD Lambda,
			       const LOCAL_FUNCTION_D *this);
  /** The gradient in barycentric coordiantes. */
  const REAL_B  *(*bary_grd_f)(const REAL_B lambda,
			       const LOCAL_FUNCTION_D *this);
  /** The Hesse matrix in cartesian coordiantes. */
  const REAL_DD *(*cart_D2_f)(const REAL_B lambda,
			      const REAL_BD Lambda,
			      const LOCAL_FUNCTION_D *this);
  /** The Hesse matrix in barycentric coordiantes. */
  const REAL_BB *(*bary_D2_f)(const REAL_B lambda,
			      const LOCAL_FUNCTION_D *this);

  /* A LOCAL_FUNCTION_D must know for itself how to create a "fast"
   * i.e. caching version of itself.
   */
  const QUAD_FUNCTION *quad_function(const QUAD *quad,
				     const LOCAL_FUNCTION_D *this);

  /* If per element information is needed for the function evaluation
   * (i.e. the coefficients of a finite element function) then the
   * INIT_ELEMENT() method must provide it.
   */
  INIT_ELEMENT_DECL;
};

struct quad_function_d
{
  /** The values of the function. */
  const REAL    *(*f)(int iq, const QUAD_FUNCTION_D *this);
  /** The gradient in cartesian coordiantes. */
  const REAL_D  *(*cart_grd_f)(int iq, const REAL_BD Lambda,
			      const QUAD_FUNCTION_D *this);
  /** The gradient in barycentric coordiantes. */
  const REAL_B  *(*bary_grd_f)(int iq, const QUAD_FUNCTION_D *this);
  /** The Hesse matrix in cartesian coordiantes. */
  const REAL_DD *(*cart_D2_f)(int iq, const REAL_BD Lambda,
			     const QUAD_FUNCTION_D *this);
  /** The Hesse matrix in barycentric coordiantes. */
  const REAL_BB *(*bary_D2_f)(intg iq, const QUAD_FUNCTION_D *this);

  const LOCAL_FUNCTION_D *local_function;
  const QUAD *quad;

  INIT_ELEMENT_DECL;
};

struct discrete_function
{
  LOCAL_FUNCTION base;
  DOF_REAL_VEC   *coeffs;
};

struct discrete_function_d
{
  LOCAL_FUNCTION_D base;
  DOF_REAL_VEC     *coeffs; /* Nope, no typo: this is _NOT_ a DOF_REAL_D_VEC */
};

/* Stuff below is incomplete and needs more thinking. The stuff should
 * work efficiently for the case where a considerable part of the
 * finite element space is the Cartesian product of a scalar finite
 * element space;
 */
typedef const REAL    *(*BAS_FCT_D)(const REAL_B lambda DEF_BFCTS_THIS(this));
typedef const REAL_B  *(*GRD_BAS_FCT_D)(const REAL_B lambda DEF_BFCTS_THIS(this));
typedef const REAL_BB *(*D2_BAS_FCT_D)(const REAL_B lambda DEF_BFCTS_THIS(this));

struct bas_fcts_d 
{
  const char   *name;       /*  textual description                         */
  int          dim;         /*  dimension of the corresponding mesh.        */
  int          n_bas_fcts;  /*  number of basisfunctions on one el          */
  int          n_scal_bas_fcts /* dimension of the scalar basis
				* function space
				*/
  int          degree;      /*  maximal degree of the basis functions       */
  int          n_dof[N_NODE_TYPES];   /* dofs from these bas_fcts           */

  /* how many of the DOFs in n_dof are actually derived from scalar
   * basis function instances. Behold: n_dof still contains the total
   * number of DOFs. It is just that get_dof_index() will be called
   * with a stride of DIM_OF_WORLD for the first n_scal_dof[node]
   * basis functions. This is needed to keep track of the Cartesian
   * product structure of the finite-element space.
   */
  int          n_scal_dof[N_NODE_TYPES];

/*************** per-element initializer (maybe NULL) ***********************/
  INIT_ELEMENT_DECL;

/*************** the basis functions themselves       ***********************/

  /* Per convention the basis functions that make up the Cartesian
   * product space come first. Otherwise the (\phi_i, 0), (0, \phi_i)
   * basis functions really must be constructed.
   *
   * So the first DIM_OF_WORLD*n_scal_bas_fcts entries must belong to
   * the Cartesian product space.
   */
  const BAS_FCT_D     *phi_d;
  const GRD_BAS_FCT_D *grd_phi_d;
  const D2_BAS_FCT_D  *D2_phi_d;

/*************** the trace space on the wall (e.g. Mini-element) ************/
  const BAS_FCTS_D *trace_bas_fcts; /* The trace space */

  /* The local DOF mapping for the trace spaces,
   * < 3d:
   * [0][0][wall][walldof] == local dof,
   * 3d:
   * [type][orient][wall][walldof] == local dof.
   */
  const int      *trace_dof_map[2][2][N_WALLS_MAX];

  /* This obscure component can vary from wall to wall in the presence
   * of an INIT_ELEMENT() method. It is _always_ equal to
   * trace_bas_fcts->n_bas_fcts ... BUT ONLY after the respective
   * element initializer has been called (on the trace mesh). If an
   * INIT_ELEMENT() method is present then is _MUST_ initialize
   * trace_dof_map _AND_ n_trace_bas_fcts. Of course, in 3D only the
   * components corresponding to type and orientation of the EL_INFO
   * object have to be taken care of by the INIT_ELEMENT() method.
   */
  int            n_trace_bas_fcts[N_WALLS_MAX];
  int            n_scal_trace_bas_fcts[N_WALLS_MAX];

/*************** interconnection to DOF_ADMIN and mesh   ********************/
  const DOF    *(*get_dof_indices)(const EL *, const DOF_ADMIN *, DOF *
				   DEF_BFCTS_THIS(this));
  const S_CHAR *(*get_bound)(const EL_INFO *, S_CHAR *
			     DEF_BFCTS_THIS(this));

/*************** entries must be set for interpolation   ********************/

  const REAL *(*interpol_d)(const EL_INFO *el_info,
			    int n, const int *indices,
			    LOCAL_FUNCTION *f,
			    REAL *coeff
			    DEF_BFCTS_THIS(this));

/********************   optional entries  ***********************************/

  const int    *(*get_int_vec)(const EL *, const DOF_INT_VEC *, int *);
  const REAL   *(*get_real_vec)(const EL *, const DOF_REAL_VEC *, REAL *);
  const U_CHAR *(*get_uchar_vec)(const EL *, const DOF_UCHAR_VEC *, U_CHAR *);
  const S_CHAR *(*get_schar_vec)(const EL *, const DOF_SCHAR_VEC *, S_CHAR *);

  void  (*real_refine_inter)(DOF_REAL_VEC *, RC_LIST_EL *, int);
  void  (*real_coarse_inter)(DOF_REAL_VEC *, RC_LIST_EL *, int);
  void  (*real_coarse_restr)(DOF_REAL_VEC *, RC_LIST_EL *, int);

  void  *bas_fcts_data;
};

struct dof_admin
{
  MESH         *mesh;
  const char   *name;

  DOF_FREE_UNIT *dof_free;    /* flag bit vector                           */
  unsigned int  dof_free_size;/* flag bit vector size                      */
  unsigned int  first_hole;   /* index of first non-zero dof_free entry    */

  U_CHAR        preserve_coarse_dofs; /* preserve non-leaf DOFs or not     */

  DOF  size;                 /* allocated size of dof_list vector          */
  DOF  used_count;           /* number of used dof indices                 */
  DOF  hole_count;           /* number of FREED dof indices (NOT size-used)*/
  DOF  size_used;            /* > max. index of a used entry               */

  int  n_dof[N_NODE_TYPES];  /* dofs from THIS dof_admin                   */
  int  n_scal_dof[N_NODE_TYPE];
  int  n0_dof[N_NODE_TYPES]; /* start of THIS admin's DOFs in the mesh.    */

  DOF_INT_VEC     *dof_int_vec;           /* linked list of int vectors    */
  DOF_DOF_VEC     *dof_dof_vec;           /* linked list of dof vectors    */
  DOF_DOF_VEC     *int_dof_vec;           /* linked list of dof vectors    */
  DOF_UCHAR_VEC   *dof_uchar_vec;         /* linked list of u_char vectors */
  DOF_SCHAR_VEC   *dof_schar_vec;         /* linked list of s_char vectors */
  DOF_REAL_VEC    *dof_real_vec;          /* linked list of real vectors   */
  DOF_REAL_D_VEC  *dof_real_d_vec;        /* linked list of real_d vectors */
  DOF_PTR_VEC     *dof_ptr_vec;           /* linked list of void * vectors */
  DOF_MATRIX      *dof_matrix;            /* linked list of matrices       */
  DOF_DOWB_MATRIX *dof_dowb_matrix;       /* linked list of block matrices */
  DOF_RDR_MATRIX  *dof_rdr_matrix;        /* linked list of matrices       */

  LIST_NODE       compress_hooks;         /* linked list of custom compress
					   * handlers.
					   */

/*--------------------------------------------------------------------------*/
/*---  pointer for administration; don't touch!                          ---*/
/*--------------------------------------------------------------------------*/

  void            *mem_info;
};


/* A block-element matrix. It is made up of four blocks:
 *
 * / A | c \
 * | ----- |
 * \ r | s /
 *
 * Here A is a block matrix and belongs to the Cartesian product
 * sub-space, s belongs to the truly vector-valued sub-space, and r
 * and c are the respective cross-section regions.
 */
struct el_matrix_d
{
  int n_b_rows;
  int n_b_cols;
  int n_s_rows;
  int n_s_cols;
  DOWBM_TYPE block_type;
  void   **rdrd_mat;
  REAL_D **rdr_mat;
  REAL_D **rrd_mat;
  REAL   **rr_mat;
};
