#ifndef _ALBERTA_INTERN_H_
#define _ALBERTA_INTERN_H_
/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file: alberta_intern.h                                                   */
/*                                                                          */
/*                                                                          */
/* description: private header file of the ALBERTA package                  */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Albert-Ludwigs-Universitaet Freiburg                         */
/*             Hermann-Herder-Str. 10                                       */
/*             D-79104 Freiburg im Breisgau, Germany                        */
/*                                                                          */
/*  http://www.alberta-fem.de                                               */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2005)                          */
/*  (c) by D. Koester (2002-2005), C.-J. Heine (2002-2009)                  */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
# include "config.h" /* probably a good idea to pull this one in here ... */
#endif

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "alberta.h"

#ifdef __cplusplus
extern "C" {
#elif 0
} /* some editors try to indent because of the brace above */
#endif

#if !ALBERTA_DEBUG && GCC_ATTRIBUTE_FLATTEN
/* "always_inline" causes an internal compiler error, but the
 * "flatten" seems to do the correct thing. We really want to do
 * inlining of large functions here because of some constant boolean
 * arguments which select the respective behaviour. Kind of C++
 * templates for the poor C programmer.
 */
# define FLATTEN_ATTR      __FLATTEN_ATTRIBUTE__
# define FORCE_INLINE_ATTR __FORCE_INLINE_ATTRIBUTE__
#else
# define FLATTEN_ATTR      /* nothing */
# define FORCE_INLINE_ATTR /* nothing */
#endif

#define LAGRANGE_DEG_MAX 4

#define FACULTY(N) \
  (1 * MAX(1, N) * MAX(1, (N)-1) * MAX(1, (N)-2) * MAX(1, (N)-3) * \
   MAX(1, (N)-4) * MAX(1, (N)-5) * MAX(1, (N)-6) * MAX(1, (N)-7) * \
   MAX(1, (N)-8) * MAX(1, (N)-9) * MAX(1, (N)-10))
#define BINOMIAL(N, K) (FACULTY(N)/(FACULTY(K)*FACULTY((N)-(K))))

#define N_BAS_LAGRANGE(DEG, DIM) BINOMIAL(((long)((DEG)+(DIM))), ((long)(DEG)))

/* #define N_BAS_LAG_0D(DEG) N_BAS_LAGRANGE(0, 0) */

#define N_BAS_LAG_1D(DEG) N_BAS_LAGRANGE(DEG, 1)
#define N_BAS_LAG_2D(DEG) N_BAS_LAGRANGE(DEG, 2)
#define N_BAS_LAG_3D(DEG) N_BAS_LAGRANGE(DEG, 3)

typedef struct quad_metadata
{
  /* The per-element cache, this _must_ come first. DO NOT CHANGE. */
  QUAD_EL_CACHE el_cache;
  int n_points;

  struct {
    void *ordinary;   /* ordinary quad-fast objects codim 0 & 1 */
    void *tangential; /* tangential quad-fast objects codim 1 */
  } quad_fast_head;   /* All QUAD_FAST objects for this quadrature */

  /* cached values for parametric meshes, generic part */
  void *param_data[LAGRANGE_DEG_MAX+1];
  void (*delete_param_data)(void *param_data);
} QUAD_METADATA;

#define ___AI_STRINGIZE(ARG) #ARG
#define __AI_STRINGIZE(ARG)  ___AI_STRINGIZE(ARG)
#define _AI_STRINGIZE(ARG)   __AI_STRINGIZE(ARG)

#define ___AI_CONCAT(ARG1, ARG2) ARG1##ARG2
#define __AI_CONCAT(ARG1, ARG2) ___AI_CONCAT(ARG1, ARG2)
#define _AI_CONCAT(ARG1, ARG2) __AI_CONCAT(ARG1, ARG2)

/*--------------------------------------------------------------------------*/
/* information about size of leaf data, which will be hidden at child[1] and*/
/* pointers to functions which will be used for transferring such           */
/* information during refinement/coarsening                                 */
/*--------------------------------------------------------------------------*/
typedef struct leaf_data_info LEAF_DATA_INFO;

struct leaf_data_info
{
  size_t leaf_data_size;
  void (*refine_leaf_data)(EL *parent, EL *child[2]);
  void (*coarsen_leaf_data)(EL *parent, EL *child[2]);
};

/*--------------------------------------------------------------------------*/
/* interpolation of dof vectors during refinement                           */
/* using DOF_VEC_LIST structure                                             */
/*--------------------------------------------------------------------------*/

typedef struct dof_vec_list  DOF_VEC_LIST;

struct dof_vec_list
{
  int             size;            /* current size of total list */
  void            **list;          /* total list, distributed to ones below */

  int             n_dof_int_vec, n_dof_dof_vec;
  int             n_dof_uchar_vec, n_dof_schar_vec;
  int             n_dof_real_vec, n_dof_real_d_vec;
  int             n_dof_ptr_vec;
  int             n_dof_matrix;  

  DOF_INT_VEC     **dof_int_vec;
  DOF_DOF_VEC     **dof_dof_vec;
  DOF_UCHAR_VEC   **dof_uchar_vec;
  DOF_SCHAR_VEC   **dof_schar_vec;
  DOF_REAL_VEC    **dof_real_vec;
  DOF_REAL_D_VEC  **dof_real_d_vec;
  DOF_PTR_VEC     **dof_ptr_vec;
  DOF_MATRIX      **dof_matrix;
};

/*--------------------------------------------------------------------------*/
/* the structure stored in the entry mem_info of mesh.                      */
/* It stores information for memory management and macro elements.          */
/*--------------------------------------------------------------------------*/
typedef struct mesh_mem_info MESH_MEM_INFO;

struct mesh_mem_info
{
  void *dof_ptrs;
  void *dofs[N_NODE_TYPES];
  void *element;

  void *rc_list;
  void *real_d;

  DOF_VEC_LIST   *dvlist;
  DOF_VEC_LIST   *dvlist_np; /* non-periodic dvlist on periodic meshes */

  void           *leaf_data;
  LEAF_DATA_INFO leaf_data_info[1];

/*--------------------------------------------------------------------------*/
/* special entries for the use of master/slave grids.                       */
/*--------------------------------------------------------------------------*/

/* pointers on trace grids */
  MESH           *master;          /* pointer to master mesh */
                                   /* binding of master mesh */
  DOF_PTR_VEC    *master_binding;  /* pointers to master mesh simplices */
  DOF_PTR_VEC    *slave_binding;   /* pointers to slave mesh simplices */
  int            next_trace_id;

/* pointers on master grids */
  int            n_slaves;         /* no. of slaves of this mesh */
  MESH           **slaves;         /* vector of pointers to slaves */

/*---8<---------------------------------------------------------------------*/
/*--- These last two entries are not really for memory management.       ---*/
/*--- They describe the array used to store macro element coordinate     ---*/
/*--- information, which is filled in macro.c and read_mesh.c.           ---*/
/*--------------------------------------------------------------------->8---*/
  int   count;  
  REAL_D *coords;
};

static inline REAL_D *_AI_MCOPY_DOW(REAL_DD src, REAL_DD dst)
{
  return MCOPY_DOW((const REAL_D *)src, dst);
}

/* Determine whether the neighbour in an RC_LIST sits on the other
 * side of a periodic wall. elem and neigh must not be NULL.
 */
static inline int _AI_rc_list_periodic_neigh_p(RC_LIST_EL *rc_el, 
					       RC_LIST_EL *rc_neigh)
{
  return 
    rc_el->el_info.el->dof[0] != rc_neigh->el_info.el->dof[0] &&
    rc_el->el_info.el->dof[1] != rc_neigh->el_info.el->dof[0]; 
}

/* Same as above, but with a 2-element array of edge DOF pointers.
 *
 * BIG FAT NOTE: SIDE-EFFECT:
 * The edge DOF-pointers are updated; this way the test can be used in
 * a "for" loop around the refinement edge without having to update
 * "edge". The orientation of "edge" is maintained.
 */
static inline int _AI_rc_list_periodic_wall_p(RC_LIST_EL *rc_el, DOF *edge[2])
{
  if (rc_el->el_info.el->dof[0] != edge[0] &&
      rc_el->el_info.el->dof[0] != edge[1]) {
    if (rc_el->el_info.el->dof[0][0] == edge[0][0]) {
      edge[0] = rc_el->el_info.el->dof[0];
      edge[1] = rc_el->el_info.el->dof[1];
    } else {
      edge[0] = rc_el->el_info.el->dof[1];
      edge[1] = rc_el->el_info.el->dof[0];
    }
    DEBUG_TEST_EXIT(edge[0][0] < edge[1][0],
		    "Wrong orientation of refinement edge.");
    return true;
  } else {
    return false;
  }
}

/* How to compare two OPERATOR_INFO structures.  the DOWB_ and RDR_
 * versions need additional tests.
 */
#define OP_INFO_EQ_P(oi1, oi2)						\
  (FE_SPACE_EQ_P((oi1)->row_fe_space, (oi2)->row_fe_space) &&		\
   FE_SPACE_EQ_P((oi1)->col_fe_space, (oi2)->col_fe_space) &&		\
   (oi1)->quad[2] == (oi2)->quad[2] &&					\
   (oi1)->quad[1] == (oi2)->quad[1] &&					\
   (oi1)->quad[0] == (oi2)->quad[0] &&					\
   (oi1)->quad_tensor[0] == (oi2)->quad_tensor[0] &&			\
   (oi1)->quad_tensor[1] == (oi2)->quad_tensor[1] &&			\
   (oi1)->quad_tensor[2] == (oi2)->quad_tensor[2] &&			\
   (oi1)->init_element == (oi2)->init_element &&			\
									\
   (oi1)->LALt.real == (oi2)->LALt.real &&				\
   (oi1)->LALt_type == (oi2)->LALt_type &&				\
   (oi1)->LALt_symmetric == (oi2)->LALt_symmetric &&			\
   (oi1)->LALt_pw_const == (oi2)->LALt_pw_const &&			\
   (oi1)->LALt_degree == (oi2)->LALt_degree &&				\
									\
   (oi1)->Lb0.real == (oi2)->Lb0.real &&				\
   (oi1)->Lb0_pw_const == (oi2)->Lb0_pw_const &&			\
   									\
   (oi1)->Lb1.real == (oi2)->Lb1.real &&				\
   (oi1)->Lb1_pw_const == (oi2)->Lb1_pw_const &&			\
									\
   (oi1)->Lb0_Lb1_anti_symmetric == (oi2)->Lb0_Lb1_anti_symmetric &&	\
   (oi1)->Lb_type == (oi2)->Lb_type &&					\
   (oi1)->Lb_degree == (oi2)->Lb_degree &&				\
   (oi1)->advection_field == (oi2)->advection_field &&			\
   (oi1)->adv_fe_space == (oi2)->adv_fe_space &&			\
									\
   (oi1)->c.real == (oi2)->c.real &&					\
   (oi1)->c_type == (oi2)->c_type &&					\
   (oi1)->c_pw_const == (oi2)->c_pw_const &&				\
   (oi1)->c_degree == (oi2)->c_degree &&				\
									\
   (oi1)->fill_flag == (oi2)->fill_flag /*&&				\
   (oi1)->user_data == (oi2)->user_data*/)

/* block-matrix/block-operator types for DIM_OF_WORLD/scalar combinations */

typedef enum op_block_type {
  OP_TYPE_SS = 0,
  OP_TYPE_SV,
  OP_TYPE_VS,
  OP_TYPE_CV,
  OP_TYPE_VC,
  OP_TYPE_VV,
  N_OP_BLOCK_TYPES
} OP_BLOCK_TYPE;

static inline OP_BLOCK_TYPE operator_type(const FE_SPACE *row_fe_space,
					  const FE_SPACE *col_fe_space)
{
  FUNCNAME("operator_type");
  const BAS_FCTS *row_bfcts, *col_bfcts;
  OP_BLOCK_TYPE op_type;

  row_bfcts = row_fe_space->bas_fcts;
  col_bfcts = col_fe_space->bas_fcts;

  if (row_fe_space->rdim == 1) {
    op_type = col_bfcts->rdim == 1 ? OP_TYPE_SS : OP_TYPE_SV;
  } else if (row_bfcts->rdim == 1) {
    op_type = col_bfcts->rdim == 1 ? OP_TYPE_SS : OP_TYPE_CV;
  } else if (col_fe_space->rdim == 1) {
    op_type = OP_TYPE_VS;
  } else {
    op_type = col_bfcts->rdim == 1 ? OP_TYPE_VC : OP_TYPE_VV;
  }
  TEST_EXIT(VECTOR_BASIS_FUNCTIONS || op_type == OP_TYPE_SS,
	    "DIM_OF_WORLD-valued basis functions not supported in this "
	    "version of ALBERTA. re-configure and re-compile the library "
	    "without \"--disable-vector-basis-functions\".\n");

  return op_type;
}

static inline MATENT_TYPE matent_type(const FE_SPACE *row_fe_space,
				      const FE_SPACE *col_fe_space,
				      MATENT_TYPE op_mat_type)
{
  switch (operator_type(row_fe_space, col_fe_space)) {
  case OP_TYPE_VV:
  case OP_TYPE_VS:
  case OP_TYPE_SV:
    return MATENT_REAL;
  case OP_TYPE_SS:
    return op_mat_type;
  case OP_TYPE_VC:
  case OP_TYPE_CV:
    return MATENT_REAL_D;
  default:
    return MATENT_NONE;
  }
}

/* Below are some template like constructs to emit variants of small
 * functions for constant dimensions. The assemble stuff makes
 * extensive use of this.
 */

/* Make sure n_lambda is a constant by emitting several copies of the
 * same function (the compiler cannot know that n_lambda only takes
 * the values 1, 2, 3 or 4, otherwise it could decide by itself to
 * emit several copies of the same function for different values of
 * n_lambda).
 */
#define DIM_NAME(NAMEBASE, DIM) NAMEBASE##_##DIM##D

#define EMIT_DIM_VERSION(CLASS, NAMEBASE, DIM, ARG_DECL, ARG_CALL)	\
  CLASS void FLATTEN_ATTR DIM_NAME(NAMEBASE, DIM) ARG_DECL		\
  {									\
    NAMEBASE ARG_CALL(N_LAMBDA(DIM));					\
  }									\
  struct _AI_semicolon_dummy

#if DIM_MAX == 1
# define EMIT_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL, ARG_CALL)	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 1, ARG_DECL, ARG_CALL)
# define DECLARE_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL) \
  CLASS void DIM_NAME(NAMEBASE, 1) ARG_DECL
# define DEF_DIM_DUMMIES(NAMEBASE, FTYPE)			\
  static const FTYPE DIM_NAME(NAMEBASE, 1) = NULL
#elif DIM_MAX == 2
# define EMIT_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL, ARG_CALL)	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 1, ARG_DECL, ARG_CALL);	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 2, ARG_DECL, ARG_CALL)
# define DECLARE_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL) \
  CLASS void DIM_NAME(NAMEBASE, 1) ARG_DECL;		 \
  CLASS void DIM_NAME(NAMEBASE, 2) ARG_DECL
# define DEF_DIM_DUMMIES(NAMEBASE, FTYPE)		\
  static const FTYPE DIM_NAME(NAMEBASE, 1) = NULL;	\
  static const FTYPE DIM_NAME(NAMEBASE, 2) = NULL
#elif DIM_MAX == 3
# define EMIT_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL, ARG_CALL)	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 1, ARG_DECL, ARG_CALL);	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 2, ARG_DECL, ARG_CALL);	\
  EMIT_DIM_VERSION(CLASS, NAMEBASE, 3, ARG_DECL, ARG_CALL)
# define DECLARE_DIM_VERSIONS(CLASS, NAMEBASE, ARG_DECL) \
  CLASS void DIM_NAME(NAMEBASE, 1) ARG_DECL;		 \
  CLASS void DIM_NAME(NAMEBASE, 2) ARG_DECL;		 \
  CLASS void DIM_NAME(NAMEBASE, 3) ARG_DECL
# define DEF_DIM_DUMMIES(NAMEBASE, FTYPE)		\
  static const FTYPE DIM_NAME(NAMEBASE, 1) = NULL;	\
  static const FTYPE DIM_NAME(NAMEBASE, 2) = NULL;	\
  static const FTYPE DIM_NAME(NAMEBASE, 3) = NULL
#else
# error unsupported DIM_MAX
#endif

/* Generate the proper function name for a given dimension. */
#if DIM_MAX == 1
# define DIM_VARIANT(NAMEBASE, DIM) DIM_NAME(NAMEBASE, 1)
#elif DIM_MAX == 2
# define DIM_VARIANT(NAMEBASE, DIM)				\
  ((DIM) == 1 ? DIM_NAME(NAMEBASE, 1) : DIM_NAME(NAMEBASE, 2))
#elif DIM_MAX == 3
# define DIM_VARIANT(NAMEBASE, DIM)		\
  ((DIM) == 1					\
   ? DIM_NAME(NAMEBASE, 1)			\
   : ((DIM) == 2				\
      ? DIM_NAME(NAMEBASE, 2)			\
      : DIM_NAME(NAMEBASE, 3)))
#else
# error unsupported DIM_MAX
#endif

/*******************************************************************************
 *  stack data structure for non-recursive mesh traversal
 ******************************************************************************/

/* should not this be a private data-structure??? */
struct traverse_stack
{
  MESH           *traverse_mesh;
  int            traverse_level;
  FLAGS          traverse_flags;
  FLAGS          fill_flag;

  const MACRO_EL *traverse_mel;
  int            stack_size;
  int            stack_used;
  EL_INFO        *elinfo_stack;
  U_CHAR         *info_stack;

  const MACRO_EL *save_traverse_mel;
  EL_INFO        *save_elinfo_stack;
  U_CHAR         *save_info_stack;
  int            save_stack_used;

  int            el_count;

  struct {
    int   pos;
    int   traverse_level;
    FLAGS traverse_flags;
    /* fill-flags are in stack->elinfo_stack[marker.pos] */
  } marker;

  TRAVERSE_STACK *next;
};

void __AI_enlarge_traverse_stack(TRAVERSE_STACK *stack);

/*** several lookup-tables for 3d *******************************************/
#if DIM_MAX > 2

/* slave_numbering_3d[type=0,1][orientation][face][vertex]: Define the local
 * slave vertex index given in all cases for master orientation and face.   
 * The rules are: slave numbering is counterclockwise when looking          
 * at the slave triangle from outside the master tetrahedron. The numbering 
 * is defined so that a master refinement edge always corresponds to a slave
 * refinement edge. Unfortunately, the numbering must depend on the parent  
 * orientation to remain consistent.                                        
 */
static const int slave_numbering_3d[2][2][N_WALLS_3D][N_VERTICES_3D] =
  {/* type 0 */ {/* orientation + */ {/* face 0 */ {-1, 1, 2, 0},
				      /* face 1 */ { 1,-1, 0, 2},
				      /* face 2 */ { 0, 1,-1, 2},
				      /* face 3 */ { 1, 0, 2,-1}},
		 /* orientation - */ {/* face 0 */ {-1, 0, 2, 1},
				      /* face 1 */ { 0,-1, 1, 2},
				      /* face 2 */ { 1, 0,-1, 2},
				      /* face 3 */ { 0, 1, 2,-1}}},
   /* type 1 */ {/* orientation + */ {/* face 0 */ {-1, 0, 1, 2},
				      /* face 1 */ { 1,-1, 0, 2},
				      /* face 2 */ { 0, 1,-1, 2},
				      /* face 3 */ { 1, 0, 2,-1}},
		 /* orientation - */ {/* face 0 */ {-1, 1, 0, 2},
				      /* face 1 */ { 0,-1, 1, 2},
				      /* face 2 */ { 1, 0,-1, 2},
				      /* face 3 */ { 0, 1, 2,-1}}}};

/****************************************************************************
 * master_edge_3d[orientation][master face][slave edge]: Gives the master
 * edge corresponding to the slave edge.
 ****************************************************************************/

static const int master_edge_3d[2][2][N_WALLS_3D][N_EDGES_2D] = 
  {/* type 0 */ {/* orientation + */ {/* face 0 */ { 3, 5, 4},
				      /* face 1 */ { 2, 5, 1},
				      /* face 2 */ { 4, 2, 0},
				      /* face 3 */ { 1, 3, 0}},
		 /* orientation - */ {/* face 0 */ { 5, 3, 4},
				      /* face 1 */ { 5, 2, 1},
				      /* face 2 */ { 2, 4, 0},
				      /* face 3 */ { 3, 1, 0}}},
   /* type 1 */ {/* orientation + */ {/* face 0 */ { 4, 3, 5},
				      /* face 1 */ { 2, 5, 1},
				      /* face 2 */ { 4, 2, 0},
				      /* face 3 */ { 1, 3, 0}},
		 /* orientation - */ {/* face 0 */ { 4, 5, 3},
				      /* face 1 */ { 5, 2, 1},
				      /* face 2 */ { 2, 4, 0},
				      /* face 3 */ { 3, 1, 0}}}};

/* child_face_3d[type][parent_face][child_no] give the local face
 * number on the child, when parent_face is the local face number of
 * on the parent.
 */
static const int child_face_3d[3][4][2] = {
  /* type 0: */ { /* face 0: */ {-1, 3},
		  /* face 1: */ { 3,-1},
		  /* face 2: */ { 1, 2},
		  /* face 3: */ { 2, 1}},
  /* type 1: */ { /* face 0: */ {-1, 3},
		  /* face 1: */ { 3,-1},
		  /* face 2: */ { 1, 1},
		  /* face 3: */ { 2, 2}},
  /* type 2: */ { /* face 0: */ {-1, 3},
		  /* face 1: */ { 3,-1},
		  /* face 2: */ { 1, 1},
		  /* face 3: */ { 2, 2}}
};

/* The inverse lookup-array: given the child_face, the parent_type and
 * the child number, "return" the parent's face number of -1.
 *
 * parent_face_3d[parent_type][child_face][ichild]
 */
static const int parent_face_3d[3][N_FACES_3D][2] = {
  /* type 0: */ { /* face 0: */ {-1,-1 },
		  /* face 1: */ { 2, 3 },
		  /* face 2: */ { 3, 2 },
		  /* face 3: */ { 1, 0 }},
  /* type 1: */ { /* face 0: */ {-1,-1 },
		  /* face 1: */ { 2, 2 },
		  /* face 2: */ { 3, 3 },
		  /* face 3: */ { 1, 0 }},
  /* type 2: */ { /* face 0: */ {-1,-1 },
		  /* face 1: */ { 2, 2 },
		  /* face 2: */ { 3, 3 },
		  /* face 3: */ { 1, 0 }},
};

/*--------------------------------------------------------------------------*/
/*   child_vertex_3d[el_type][child][i] =                                   */
/*       father's local vertex index of new vertex i                        */
/*       4 stands for the newly generated vertex                            */
/*--------------------------------------------------------------------------*/
static const int child_vertex_3d[3][2][4] = {
  {{0, 2, 3, 4}, {1, 3, 2, 4}},
  {{0, 2, 3, 4}, {1, 2, 3, 4}},
  {{0, 2, 3, 4}, {1, 2, 3, 4}}
};

/*--------------------------------------------------------------------------*/
/*   child_edge_3d[el_type][child][i] =                                     */
/*       father's local edge index of new edge i                            */
/*       new edge 2 is half of old edge 0,                                  */
/*       new edges 4, 5 are really new edges, and value is different:        */
/*         child_edge_3d[][][4, 5] = index of same edge in other child	    */
/*--------------------------------------------------------------------------*/
static const int child_edge_3d[3][2][6] = {
  {{1, 2, 0, 5, 5, 4}, {4, 3, 0, 5, 5, 4}},
  {{1, 2, 0, 5, 4, 5}, {3, 4, 0, 5, 4, 5}},
  {{1, 2, 0, 5, 4, 5}, {3, 4, 0, 5, 4, 5}}
};

/*--------------------------------------------------------------------------*/
/*   child_orientation[el_type][child] =                                    */
/*      +1 if orientation is not changed during refinement                  */
/*      -1 if orientation is changed during refinement                      */
/*--------------------------------------------------------------------------*/
static const S_CHAR child_orientation_3d[3][2] = {{1, 1}, {1, -1}, {1, -1}};

/*--------------------------------------------------------------------------*/
/*  n_child_edge_3d[el_type][ichild][dir]                                   */
/*  gives local index of new edge on child[ichild] part of face [2+dir] on  */
/*  the parent                                                              */
/*--------------------------------------------------------------------------*/
static const int n_child_edge_3d[3][2][2] = {
  {{5, 4}, {4, 5}},
  {{5, 4}, {5, 4}},
  {{5, 4}, {5, 4}}
};

/*--------------------------------------------------------------------------*/
/*  n_child_face_3d[el_type][ichild][dir]                                   */
/*  gives local index of sub-face on child[ichild] part of face [2+dir] on  */
/*  the parent                                                              */
/*--------------------------------------------------------------------------*/
static const int n_child_face_3d[3][2][2] = {
  {{1, 2}, {2, 1}}, {{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}
};

/*--------------------------------------------------------------------------*/
/*  adjacent_child_3d[position][ichild]                                     */
/*  gives number of the adjacent child on a neighbour element               */
/*     position = 0  same position of element and neigh at refinement edge  */
/*     position = 1  different ...                                          */
/*--------------------------------------------------------------------------*/
static const int adjacent_child_3d[2][2] = {{0, 1}, {1, 0}};

#endif

/***   file memory.c   ******************************************************/
extern void      *AI_get_leaf_data(MESH *mesh);
extern DOF_ADMIN *AI_get_dof_admin(MESH *mesh, const char *name, 
				   const int n_dof[N_NODE_TYPES]);
extern void      AI_free_leaf_data(void *leaf_data, MESH *mesh);
extern void      AI_reactivate_dof(MESH *mesh, const EL *el,
				   DOF **edge_twins, DOF **face_twins);
extern DOF       *AI_get_dof_memory(MESH *mesh, int position);
extern void      AI_free_dof_memory(DOF *dof, MESH *mesh, int position);
extern void      AI_get_dof_list(MESH *mesh, int i);
extern void      AI_get_dof_ptr_list(MESH *mesh);
extern void      AI_fill_missing_dofs(MESH *mesh);
extern void      AI_advance_cookies_rec(MESH *mesh);
extern MESH *
_AI_get_mesh(int dim, const char *name,
	     const MACRO_DATA *macro_data,
	     NODE_PROJECTION *(*init_node_proj)(MESH *, MACRO_EL *, int),
	     AFF_TRAFO *(*init_wall_trafos)(MESH *, MACRO_EL *, int wall),
	     bool strict_periodic);
extern DOF *_AI_get_dof(MESH *mesh, int position, bool alloc_index);

/***   file macro.c   *******************************************************/
extern S_CHAR AI_get_orientation_3d(MACRO_EL *mel);
extern void _AI_compute_element_wall_transformations(MACRO_DATA *data);
extern void
_AI_fill_bound_info(MESH *mesh, int *mel_vertices, int nv, int ne, bool count);
extern void
_AI_macro_data2mesh(MESH *mesh, const MACRO_DATA *data,
		    NODE_PROJECTION *(*init_node_proj)(MESH *, MACRO_EL *, int),
		    AFF_TRAFO *(*init_wall_trafos)(
		      MESH *, MACRO_EL *, int wall),
		    bool strict_periodic);

/***   file dof_admin.c   ***************************************************/
extern bool _AI_check_matrix_types(MATENT_TYPE mat_type, MATENT_TYPE elm_type);
extern void _AI_allocate_n_dofs(DOF_ADMIN *admin, int used_count);

/***   file write_mesh.c   ***************************************************/
extern
size_t AI_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

/***   file read_mesh.c   ***************************************************/
extern
size_t AI_fread(const void *ptr, size_t size, size_t nmemb, FILE *stream);
extern bool_t  AI_xdr_REAL(XDR *xdr, void *rp);
extern bool_t  AI_xdr_U_CHAR(XDR *xdr, void *ucp);
extern bool_t  AI_xdr_S_CHAR(XDR *xdr, void *cp);
extern bool_t  AI_xdr_DOF(XDR *xdr, void *dp);
extern XDR    *AI_xdr_open_file(const char *fn, enum xdr_op mode);
extern bool    AI_xdr_close_file(XDR *xdr);
extern XDR    *AI_xdr_fopen(FILE *file, enum xdr_op mode);
extern bool    AI_xdr_close(XDR *xdr);

extern void _AI_read_REAL(REAL *val);
extern void _AI_read_int(int *val);
extern void _AI_read_string(char *string, int strileng);
extern void _AI_read_var_string(char **string);
extern void _AI_read_vector(void *start, int n, size_t size, xdrproc_t xdrproc);
extern void _AI_read_U_CHAR(U_CHAR *val);
extern void _AI_read_S_CHAR(S_CHAR *val);

/***   file read_mesh_xdr_1.2.c   *******************************************/
extern MESH *_AI_read_mesh_1_2(REAL *timeptr);
extern void _AI_match_node_types(int *node_vec);

/***   file refine.c   ******************************************************/
extern void AI_refine_fct_1d(const EL_INFO *el_info, void *data); 
extern void AI_coarse_fct_1d(const EL_INFO *el_info, void *data);
extern void _AI_refine_update_bbox(MESH *mesh, const REAL_D new_coord);

#if DIM_MAX > 1
extern void  AI_bisect_element_2d(MESH *mesh, EL *el, DOF *dof[3]); 
extern void  AI_bisect_patch_2d(MESH *mesh, RC_LIST_EL ref_list[],
				int n_neighs);
extern void  AI_coarse_patch_2d(MESH *mesh,RC_LIST_EL coarse_list[],
				int n_neigh);
#endif
extern DOF_VEC_LIST *AI_get_dof_vec_list(MESH *mesh);
extern DOF_VEC_LIST *AI_get_dof_vec_list_np(MESH *mesh);
extern void  AI_free_dof_vec_list(MESH *mesh);
extern void  AI_free_dof_vec_list_np(MESH *mesh);
extern void  AI_set_neighs_on_patch_3d(RC_LIST_EL ref_list[], int n_neigh,
				       int bound);
extern int   AI_split_rc_list_3d(RC_LIST_EL *orig,
				 RC_LIST_EL *copy, int n_left);
extern void  AI_reverse_rc_list_3d(RC_LIST_EL ref_list[],
				   int n_neigh, DOF *edge[2]);
extern RC_LIST_EL *AI_rotate_rc_list_3d(RC_LIST_EL *first,
					int n_neigh, DOF *edge[2]);

/***   file submesh.c   *****************************************************/
extern void AI_check_slavery(MESH *master);

/***   file traverse_nr.c ***************************************************/
extern void AI_test_traverse_nr(MESH *mesh, int level, FLAGS fill_flag);
extern void AI_update_elinfo_stack_3d(TRAVERSE_STACK *stack); 

/***   file traverse_r.c  ***************************************************/
extern void AI_test_traverse(MESH *mesh, int level, FLAGS fill_flag);
extern void AI_update_elinfo_3d(EL_INFO *elinfo);

/*** file periodic.c ********************************************************/
extern int
_AI_wall_trafo_vertex_orbit(int dim,
			    int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
			    int nwt,
			    int v, int *orbit, int nv);
extern int
_AI_wall_trafo_vertex_orbits(int dim,
			     int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
			     int nwt,
			     int *orbit_map, int *nvp);
#if DIM_MAX > 2
extern int
_AI_wall_trafo_edge_orbit(int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
			  int nwt,
			  int e, int *orbit,
			  int (*edges)[2], int ne);
extern int
_AI_wall_trafo_edge_orbits(int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
			   int nwt,
			   int *orbit_map, 
			   int (*edges)[2], int ne);
#endif
extern int
_AI_compute_macro_wall_trafos(MESH *mesh,
			      int (**wall_trafos_p)[N_VERTICES(DIM_MAX-1)][2]);

/*** file gauss-quad.c *******************************************************/
void _AI_gauss_quad(int kind, int n, REAL alpha, 
		    REAL beta, int kpts, REAL endpts[2],
		    REAL t[/*n*/], REAL w[/*n*/]);

/*** file eval.c *************************************************************/


extern REAL _AI_inter_fct_loc_param(const EL_INFO *el_info,
				     const QUAD *quad, int iq,
				     void *ud);
extern REAL _AI_inter_fct_loc(const EL_INFO *el_info,
			      const QUAD *quad, int iq,
			      void *ud);
extern const REAL *_AI_inter_fct_loc_d(REAL_D result,
				       const EL_INFO *el_info,
				       const QUAD *quad, int iq,
				       void *ud);
extern const REAL *_AI_inter_fct_loc_d_param(REAL_D result,
					     const EL_INFO *el_info,
					     const QUAD *quad, int iq,
					     void *ud);

/*** file parametric.c *******************************************************/

extern bool _AI_is_lagrange_parametric(MESH *mesh);
extern PARAM_STRATEGY _AI_lagrange_strategy(MESH *mesh);

/*** file block_precon.c ****************************************************/

extern const PRECON *_AI_vget_block_diag_precon(const DOF_MATRIX *A,
						const DOF_SCHAR_VEC *mask,
						int info,
						va_list ap);
extern const PRECON *_AI_get_block_diag_precon(const DOF_MATRIX *A,
					       const DOF_SCHAR_VEC *mask,
					       int info,
					       ...);
extern const PRECON *_AI_vget_block_SSOR_precon(const DOF_MATRIX *A,
						const DOF_SCHAR_VEC *mask,
						int info,
						va_list ap);
extern const PRECON *_AI_get_block_precon(const DOF_MATRIX *A,
					  const DOF_SCHAR_VEC *mask,
					  int info,
					  const PRECON_TYPE *prec_type);

#ifdef __cplusplus
}
#endif

#endif /* !_ALBERTA_INTERN_H_ */
