/*******************************************************************************
 * ALBERTA:  an Adaptive multi Level finite element toolbox using
 *           Bisectioning refinement and Error control by Residual
 *           Techniques for scientific Applications
 *
 * file:     disc-ortho-poly.c
 *
 * description: Orthogonal discontinuous polynomials of degree 1 and 2.
 *
 *******************************************************************************
 *
 *  authors:   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 C.-J. Heine (2008)
 *
 ******************************************************************************/

#include "alberta.h"

#define COMPILED_CONSTANTS 0

#if COMPILED_CONSTANTS
# define WEIGHT_V  (sqrt(6.0))
# define WEIGHT_E  (6.0*sqrt(5.0))
# define C         (0.5 - sqrt(3.0)/6.0)
#else /* computed */
# define WEIGHT_V							\
  2.449489742783178098197284074705891391965947480656670128432692567 
# define WEIGHT_E							\
  13.41640786499873817845504201238765741264371015766915434562538347
# define C								\
  0.2113248654051871177454256097490212721761991243649365619906988368
#endif

static ORTHO_DATA d_ortho_1_1d_data;
static ORTHO_DATA d_ortho_2_1d_data;

/* p.w. linear orthogonal polynomials */
static inline REAL d_ortho_phi_v_1d(const REAL_B lambda, int v)
{
  return WEIGHT_V * (lambda[v] - C);
}

#undef C

static inline const REAL *
d_ortho_grd_phi_v_1d(REAL_B result, const REAL_B lambda, int v, bool doinit)
{
  if (doinit) {
    SET_BAR(2 /* dim */, 0.0, result);
  }

  result[v] = WEIGHT_V;

  return result;
}

static REAL d_ortho_phi_v0_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_v_1d(lambda, 0);
}

static REAL d_ortho_phi_v1_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_v_1d(lambda, 1);
}

static const REAL *
d_ortho_grd_phi_v0_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_v_1d(grd, lambda, 0, false);
}

static const REAL *
d_ortho_grd_phi_v1_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_v_1d(grd, lambda, 1, false);
}

static inline const REAL_B *d_ortho_D2_phi_v_1d(REAL_BB result,
						const REAL_B lambda, int v,
						bool doinit)
{
  if (doinit) {
    MSET_BAR(2 /* dim */, 0.0, result);
  }
  
  return (const REAL_B *)result;
}

static const REAL_B *
d_ortho_D2_phi_v0_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_v_1d(D2, lambda, 0, false);
}

static const REAL_B *
d_ortho_D2_phi_v1_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_v_1d(D2, lambda, 1, false);
}

/*******************************************************************************
 *
 * p.w. quadratic orthogonal add-on
 *
 ******************************************************************************/
#if COMPILED_CONSTANTS
# define B (sqrt(2.0)/12.0)
#else /* computed */
# define B 0.1178511301977579207334740603508081732141393229480790060980566448
#endif

static REAL d_ortho_phi_e_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return
    WEIGHT_E
    *
    (lambda[0]*lambda[1]
     +
     B*(d_ortho_phi_v_1d(lambda, 0) + d_ortho_phi_v_1d(lambda, 1)));
}
    
static const REAL *
d_ortho_grd_phi_e_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;
  REAL_B tmp;

  grd[0] = lambda[1];
  grd[1] = lambda[0];

  AXPY_BAR(1 /* dim */, B, d_ortho_grd_phi_v_1d(tmp, lambda, 0, true), grd);
  AXPY_BAR(1 /* dim */, B, d_ortho_grd_phi_v_1d(tmp, lambda, 1, true), grd);

  SCAL_BAR(1 /* dim */, WEIGHT_E, grd);
  
  return grd;
}

static const REAL_B *
d_ortho_D2_phi_e_1d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  D2[0][1] = D2[1][0] = WEIGHT_E;
  
  return (const REAL_B *)D2;
}

#undef B
#undef WEIGHT_E
#undef WEIGHT_V

static const BAS_FCT d_ortho_phi_1d[N_BAS_LAG_2_1D] = {
  d_ortho_phi_v0_1d, d_ortho_phi_v1_1d, d_ortho_phi_e_1d
};

static const GRD_BAS_FCT d_ortho_grd_phi_1d[N_BAS_LAG_2_1D] = {
  d_ortho_grd_phi_v0_1d, d_ortho_grd_phi_v1_1d, d_ortho_grd_phi_e_1d
};

static const D2_BAS_FCT d_ortho_D2_phi_1d[N_BAS_LAG_2_1D] = {
  d_ortho_D2_phi_v0_1d, d_ortho_D2_phi_v1_1d, d_ortho_D2_phi_e_1d
};

static const REAL child_refine_inter[2][N_BAS_LAG_2_1D][N_BAS_LAG_2_1D] =
{
  {
#if COMPILED_CONSTANTS
    { 0.75+0.25*sqrt(3.0), -0.25*sqrt(3.0)+0.25, -0.125*sqrt(30.0) },
    { 0.25+0.25*sqrt(3.0), 0.75-0.25*sqrt(3.0), 0.125*sqrt(30.0) },
    { 0.0, 0.0, 0.25 }
#else
    { 1.183012701892219323381861585376468091735701313452595157013951745,
      -.1830127018922193233818615853764680917357013134525951570139517448,
      -.6846531968814576418212122285010026674409308687474790677836180618 },
    { .6830127018922193233818615853764680917357013134525951570139517448,
      .3169872981077806766181384146235319082642986865474048429860482552,
      .6846531968814576418212122285010026674409308687474790677836180618 },
    { 0., 0., .25 }
#endif
  },
  {
#if COMPILED_CONSTANTS
    { 0.75-0.25*sqrt(3.0), 0.25*sqrt(3.0)+0.25, 0.125*sqrt(30.0) },
    { 0.25-0.25*sqrt(3.0), 0.75+0.25*sqrt(3.0), -0.125*sqrt(30.0) },
    { 0.0, 0.0, 0.25 }
#else
    { .3169872981077806766181384146235319082642986865474048429860482552,
      .6830127018922193233818615853764680917357013134525951570139517448,
      .6846531968814576418212122285010026674409308687474790677836180618 },
    { -.1830127018922193233818615853764680917357013134525951570139517448,
      1.183012701892219323381861585376468091735701313452595157013951745,
      -.6846531968814576418212122285010026674409308687474790677836180618 },
    { 0., 0., .25 }
#endif    
  }
};

static const REAL child_coarse_inter[2][N_BAS_LAG_2_1D][N_BAS_LAG_2_1D] =
{
  {
#if COMPILED_CONSTANTS
    { 0.375+0.125*sqrt(3.0), 0.125+0.125*sqrt(3.0), 0 },
    { 0.375-0.125*sqrt(3.0), 0.125-0.125*sqrt(3.0), 0 },
    { -sqrt(30.0)/16.0, sqrt(30.0)/16.0, 0.125 }
#else
    { .5915063509461096616909307926882340458678506567262975785069758724,
      .3415063509461096616909307926882340458678506567262975785069758724,
      0. },
    { -.915063509461096616909307926882340458678506567262975785069758724e-1,
      .1584936490538903383090692073117659541321493432737024214930241276,
      0. },
    { -.3423265984407288209106061142505013337204654343737395338918090309,
      .3423265984407288209106061142505013337204654343737395338918090309,
      .125 }
#endif
  },
  {
#if COMPILED_CONSTANTS
    { 0.375-0.125*sqrt(3.0), 0.125-0.125*sqrt(3.0), 0.0 },
    { 0.125+0.125*sqrt(3.0), 0.375+0.125*sqrt(3.0), 0.0 },
    { sqrt(30.0)/16.0, -sqrt(30.0)/16.0, 0.125 },
#else
    { .1584936490538903383090692073117659541321493432737024214930241276,
      -.915063509461096616909307926882340458678506567262975785069758724e-1,
      0. },
    { .3415063509461096616909307926882340458678506567262975785069758724,
      .5915063509461096616909307926882340458678506567262975785069758724,
      0. },
    { .3423265984407288209106061142505013337204654343737395338918090309,
      -.3423265984407288209106061142505013337204654343737395338918090309,
      .125 }
#endif    
  }
};

/******************************************************************************
 *
 * Degree 1
 *
 ******************************************************************************/

#undef DEF_EL_VEC_D_ORTHO_1_1D
#define DEF_EL_VEC_D_ORTHO_1_1D(type, name)			\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_1_1D, N_BAS_LAG_1_1D)

#undef DEFUN_GET_EL_VEC_D_ORTHO_1_1D
#define DEFUN_GET_EL_VEC_D_ORTHO_1_1D(name, type, admin, body, ...)	\
  static const EL_##type##_VEC *					\
  d_ortho_get_##name##_1_1d(type##_VEC_TYPE *vec, const EL *el, __VA_ARGS__) \
  {									\
    FUNCNAME("d_get_"#name"d_1_1d");					\
    static DEF_EL_VEC_D_ORTHO_1_1D(type, rvec_space);			\
    type##_VEC_TYPE *rvec = vec ? vec : rvec_space->vec;		\
    int n0, node, ibas;							\
    DOF **dofptr = el->dof, dof;					\
									\
    DEBUG_TEST_EXIT(true, "");						\
									\
    node = (admin)->mesh->node[CENTER];					\
    n0   = (admin)->n0_dof[CENTER];					\
    for (ibas = 0; ibas < N_BAS_LAG_1_1D; ibas++) {			\
      dof = dofptr[node][n0+ibas];					\
      body;								\
    }									\
									\
    return vec ? NULL : rvec_space;					\
  }									\
  struct _AI_semicolon_dummy

#undef DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D
#define DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(name, type, ASSIGN)		\
  DEFUN_GET_EL_VEC_D_ORTHO_1_1D(_##name##_vec, type, dv->fe_space->admin, \
				ASSIGN(dv->vec[dof], rvec[ibas]),	\
				const DOF_##type##_VEC *dv);		\
  static const EL_##type##_VEC *					\
  d_ortho_get_##name##_vec_1_1d(type##_VEC_TYPE *vec, const EL *el,	\
			       const DOF_##type##_VEC *dv)		\
  {									\
    EL_##type##_VEC *vec_loc = dv->vec_loc;				\
									\
    if (vec != NULL || vec_loc == NULL) {				\
      return d_ortho_get__##name##_vec_1_1d(vec, el, dv);		\
    } else {								\
      d_ortho_get__##name##_vec_1_1d(vec_loc->vec, el, dv);		\
      return vec_loc;							\
    }									\
  }									\
  struct _AI_semicolon_dummy

#undef COPY_EQ
#define COPY_EQ(a, b) (b) = (a)

/*--------------------------------------------------------------------------*/
/*  functions for combining basisfunctions with coefficients                */
/*--------------------------------------------------------------------------*/

DEFUN_GET_EL_VEC_D_ORTHO_1_1D(dof_indices, DOF, admin,
			      rvec[ibas] = dof,
			      const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

static const EL_BNDRY_VEC *d_ortho_get_bound_1_1d(BNDRY_FLAGS *vec,
						  const EL_INFO *el_info,
						  const BAS_FCTS *thisptr)
{
  FUNCNAME("d_get_bound2_1d");
  static DEF_EL_VEC_D_ORTHO_1_1D(BNDRY, rvec_space);
  BNDRY_FLAGS *rvec = vec ? vec : rvec_space->vec;
  int i;

  TEST_FLAG(FILL_BOUND, el_info);

  for (i = 0; i < N_BAS_LAG_1_1D; i++) {
    BNDRY_FLAGS_CPY(rvec[i], el_info->edge_bound[0]);
  }

  return vec ? NULL : rvec_space;
}

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_INT_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(int, INT, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_VEC                  ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(real, REAL, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_D_VEC                ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(real_d, REAL_D, COPY_DOW);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_SCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(schar, SCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_UCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(uchar, UCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_PTR_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(ptr, PTR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_DD_VEC               ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_1D(real_dd, REAL_DD, _AI_MCOPY_DOW);

/****************************************************************************/
/*  functions for interpolation/ restriction during refinement/coarsening   */
/****************************************************************************/

static void
d_ortho_real_refine_inter_1_1d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  EL *el = list->el_info.el;
  EL *child;  
  int i, j, n0, node, ich;

  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];
    
  for (ich = 0; ich < 2; ich++) {
    child = el->child[ich];
    for (i = 0; i < N_BAS_LAG_1_1D; i++) {
      DOF cdof = child->dof[node][n0+i];
      drv->vec[cdof] = 0.0;
      for (j = 0; j < N_BAS_LAG_1_1D; j++) {
	DOF pdof = el->dof[node][n0+j];	
	drv->vec[cdof] += child_refine_inter[ich][i][j] * drv->vec[pdof];
      }
    }
  }
}

static void
d_ortho_real_coarse_inter_1_1d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  EL *el = list->el_info.el;
  EL *child;  
  int i, j, n0, node, ich;

  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];

  for (ich = 0; ich < 2; ich++) {
    child = el->child[ich];
    for (i = 0; i < N_BAS_LAG_1_1D; i++) {
      DOF pdof = el->dof[node][n0+i];
      if (ich == 0) {
	drv->vec[pdof] = 0.0;
      }
      for (j = 0; j < N_BAS_LAG_1_1D; j++) {
	DOF cdof = child->dof[node][n0+j];	
	drv->vec[pdof] += child_coarse_inter[ich][i][j] * drv->vec[cdof];
      }
    }
  }
}

/* Interpolation: we always use L2-interpolation over the entire
 * element, even if WALL >= 0. The quadrature degree is such that we
 * would reproduce the polynomials of the given degree.
 */
static void
d_ortho_interpol_1_1d(EL_REAL_VEC *el_vec,
		      const EL_INFO *el_info, int wall,
		      int no, const int *b_no,
		      LOC_FCT_AT_QP f, void *f_data,
		      const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      el_vec->vec[b_no[i]] = 0.0;      
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_1_1D; ib++) {
      el_vec->vec[ib] = 0.0;
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (ib = 0; ib < N_BAS_LAG_1_1D; ib++) {
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  }
}

static void
d_ortho_interpol_d_1_1d(EL_REAL_D_VEC *el_vec,
			const EL_INFO *el_info, int wall,
			int no, const int *b_no,
			LOC_FCT_D_AT_QP f, void *f_data,
			const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      SET_DOW(0.0, el_vec->vec[b_no[i]]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	AXPY_DOW( qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_1_1D; ib++) {
      SET_DOW(0.0, el_vec->vec[ib]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (ib = 0; ib < N_BAS_LAG_1_1D; ib++) {
	AXPY_DOW(qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  }
}

static void
d_ortho_interpol_dow_1_1d(EL_REAL_VEC_D *el_vec,
			  const EL_INFO *el_info, int wall,
			  int no, const int *b_no,
			  LOC_FCT_D_AT_QP f, void *f_data,
			  const BAS_FCTS *thisptr)
{
  d_ortho_interpol_d_1_1d((EL_REAL_D_VEC *)el_vec,
			  el_info, wall, no, b_no, f, f_data, thisptr);
}

/* The trace-space is degenerated: all basis functions have non-zero
 * trace on all walls. If permute the stuff cyclic s.t. we would
 * eventually have the chance to really define the trace-space as
 * degenerated set of basis functions.
 */
static const int trace_mapping_d_ortho_1_1d[N_WALLS_1D][N_BAS_LAG_1_1D] = {
  { 0, 1 }, { 1, 0 },
};

static const BAS_FCTS disc_ortho1_1d = {
  DISC_ORTHO_NAME(1)"_1d", 1, 1, N_BAS_LAG_1_1D, N_BAS_LAG_1_1D, 1,
  {0, N_BAS_LAG_1_1D, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_ortho1_1d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_ortho_phi_1d, d_ortho_grd_phi_1d, d_ortho_D2_phi_1d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  NULL, /* trace space */
  { { { trace_mapping_d_ortho_1_1d[0],
	trace_mapping_d_ortho_1_1d[1], }, }, }, /* trace mapping */
  { N_BAS_LAG_1_1D,
    N_BAS_LAG_1_1D, }, /* n_trace_bas_fcts */
  d_ortho_get_dof_indices_1_1d,
  d_ortho_get_bound_1_1d,
  d_ortho_interpol_1_1d,
  d_ortho_interpol_d_1_1d,
  d_ortho_interpol_dow_1_1d,
  d_ortho_get_int_vec_1_1d,
  d_ortho_get_real_vec_1_1d,
  d_ortho_get_real_d_vec_1_1d,
  (GET_REAL_VEC_D_TYPE)d_ortho_get_real_d_vec_1_1d,
  d_ortho_get_uchar_vec_1_1d,
  d_ortho_get_schar_vec_1_1d,
  d_ortho_get_ptr_vec_1_1d,
  d_ortho_get_real_dd_vec_1_1d,
  d_ortho_real_refine_inter_1_1d,
  d_ortho_real_coarse_inter_1_1d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&d_ortho_1_1d_data
};

/******************************************************************************
 *
 * Degree 2
 *
 ******************************************************************************/

#undef DEF_EL_VEC_D_ORTHO_2_1D
#define DEF_EL_VEC_D_ORTHO_2_1D(type, name)			\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_2_1D, N_BAS_LAG_2_1D)

#undef DEFUN_GET_EL_VEC_D_ORTHO_2_1D
#define DEFUN_GET_EL_VEC_D_ORTHO_2_1D(name, type, admin, body, ...)	\
  static const EL_##type##_VEC *					\
  d_ortho_get_##name##_2_1d(type##_VEC_TYPE *vec, const EL *el, __VA_ARGS__) \
  {									\
    FUNCNAME("d_get_"#name"d_2_1d");					\
    static DEF_EL_VEC_D_ORTHO_2_1D(type, rvec_space);			\
    type##_VEC_TYPE *rvec = vec ? vec : rvec_space->vec;		\
    int n0, node, ibas;							\
    DOF **dofptr = el->dof, dof;					\
									\
    DEBUG_TEST_EXIT(true, "");						\
									\
    node = (admin)->mesh->node[CENTER];					\
    n0   = (admin)->n0_dof[CENTER];					\
    for (ibas = 0; ibas < N_BAS_LAG_2_1D; ibas++) {			\
      dof = dofptr[node][n0+ibas];					\
      body;								\
    }									\
									\
    return vec ? NULL : rvec_space;					\
  }									\
  struct _AI_semicolon_dummy

#undef DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D
#define DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(name, type, ASSIGN)		\
  DEFUN_GET_EL_VEC_D_ORTHO_2_1D(_##name##_vec, type, dv->fe_space->admin, \
				ASSIGN(dv->vec[dof], rvec[ibas]),	\
				const DOF_##type##_VEC *dv);		\
  static const EL_##type##_VEC *					\
  d_ortho_get_##name##_vec_2_1d(type##_VEC_TYPE *vec, const EL *el,	\
			       const DOF_##type##_VEC *dv)		\
  {									\
    EL_##type##_VEC *vec_loc = dv->vec_loc;				\
									\
    if (vec != NULL || vec_loc == NULL) {				\
      return d_ortho_get__##name##_vec_2_1d(vec, el, dv);		\
    } else {								\
      d_ortho_get__##name##_vec_2_1d(vec_loc->vec, el, dv);		\
      return vec_loc;							\
    }									\
  }									\
  struct _AI_semicolon_dummy

#undef COPY_EQ
#define COPY_EQ(a, b) (b) = (a)

/*--------------------------------------------------------------------------*/
/*  functions for combining basisfunctions with coefficients                */
/*--------------------------------------------------------------------------*/

DEFUN_GET_EL_VEC_D_ORTHO_2_1D(dof_indices, DOF, admin,
			      rvec[ibas] = dof,
			      const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

static const EL_BNDRY_VEC *d_ortho_get_bound_2_1d(BNDRY_FLAGS *vec,
						  const EL_INFO *el_info,
						  const BAS_FCTS *thisptr)
{
  FUNCNAME("d_get_bound2_1d");
  static DEF_EL_VEC_D_ORTHO_2_1D(BNDRY, rvec_space);
  BNDRY_FLAGS *rvec = vec ? vec : rvec_space->vec;
  int i;

  TEST_FLAG(FILL_BOUND, el_info);

  for (i = 0; i < N_BAS_LAG_2_1D; i++) {
    BNDRY_FLAGS_CPY(rvec[i], el_info->edge_bound[0]);
  }

  return vec ? NULL : rvec_space;
}

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_INT_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(int, INT, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_VEC                  ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(real, REAL, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_D_VEC                ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(real_d, REAL_D, COPY_DOW);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_SCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(schar, SCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_UCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(uchar, UCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_PTR_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(ptr, PTR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_DD_VEC               ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_1D(real_dd, REAL_DD, _AI_MCOPY_DOW);

/****************************************************************************/
/*  functions for interpolation/ restriction during refinement/coarsening   */
/****************************************************************************/

static void
d_ortho_real_refine_inter_2_1d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  EL *el = list->el_info.el;
  EL *child;  
  int i, j, n0, node, ich;

  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];
    
  for (ich = 0; ich < 2; ich++) {
    child = el->child[ich];
    for (i = 0; i < N_BAS_LAG_2_1D; i++) {
      DOF cdof = child->dof[node][n0+i];
      drv->vec[cdof] = 0.0;
      for (j = 0; j < N_BAS_LAG_2_1D; j++) {
	DOF pdof = el->dof[node][n0+j];	
	drv->vec[cdof] += child_refine_inter[ich][i][j] * drv->vec[pdof];
      }
    }
  }
}

static void
d_ortho_real_coarse_inter_2_1d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  EL *el = list->el_info.el;
  EL *child;  
  int i, j, n0, node, ich;

  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];

  for (ich = 0; ich < 2; ich++) {
    child = el->child[ich];
    for (i = 0; i < N_BAS_LAG_2_1D; i++) {
      DOF pdof = el->dof[node][n0+i];
      if (ich == 0) {
	drv->vec[pdof] = 0.0;
      }
      for (j = 0; j < N_BAS_LAG_2_1D; j++) {
	DOF cdof = child->dof[node][n0+j];	
	drv->vec[pdof] += child_coarse_inter[ich][i][j] * drv->vec[cdof];
      }
    }
  }
}

/* Interpolation: we always use L2-interpolation over the entire
 * element, even if WALL >= 0. The quadrature degree is such that we
 * would reproduce the polynomials of the given degree.
 */
static void
d_ortho_interpol_2_1d(EL_REAL_VEC *el_vec,
		      const EL_INFO *el_info, int wall,
		      int no, const int *b_no,
		      LOC_FCT_AT_QP f, void *f_data,
		      const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      el_vec->vec[b_no[i]] = 0.0;      
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_2_1D; ib++) {
      el_vec->vec[ib] = 0.0;
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (ib = 0; ib < N_BAS_LAG_2_1D; ib++) {
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  }
}

static void
d_ortho_interpol_d_2_1d(EL_REAL_D_VEC *el_vec,
			const EL_INFO *el_info, int wall,
			int no, const int *b_no,
			LOC_FCT_D_AT_QP f, void *f_data,
			const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      SET_DOW(0.0, el_vec->vec[b_no[i]]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	AXPY_DOW( qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_2_1D; ib++) {
      SET_DOW(0.0, el_vec->vec[ib]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (ib = 0; ib < N_BAS_LAG_2_1D; ib++) {
	AXPY_DOW(qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  }
}

static void
d_ortho_interpol_dow_2_1d(EL_REAL_VEC_D *el_vec,
			  const EL_INFO *el_info, int wall,
			  int no, const int *b_no,
			  LOC_FCT_D_AT_QP f, void *f_data,
			  const BAS_FCTS *thisptr)
{
  d_ortho_interpol_d_1_1d((EL_REAL_D_VEC *)el_vec,
			  el_info, wall, no, b_no, f, f_data, thisptr);
}

/* The trace-space is degenerated: all basis functions have non-zero
 * trace on all walls. If permute the stuff cyclic s.t. we would
 * eventually have the chance to really define the trace-space as
 * degenerated set of basis functions.
 */
static const int trace_mapping_d_ortho_2_1d[N_WALLS_1D][N_BAS_LAG_2_1D] = {
  { 0, 1 }, { 1, 0 }
};


static const BAS_FCTS disc_ortho2_1d = {
  DISC_ORTHO_NAME(2)"_1d", 1, 1, N_BAS_LAG_2_1D, N_BAS_LAG_2_1D, 2,
  {0, N_BAS_LAG_2_1D, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_ortho2_1d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_ortho_phi_1d, d_ortho_grd_phi_1d, d_ortho_D2_phi_1d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  NULL /* &disc_lagrange2_1d */, /* trace space */
  { { { trace_mapping_d_ortho_2_1d[0],
	trace_mapping_d_ortho_2_1d[1],
      }, }, }, /* trace mapping */
  { N_BAS_LAG_2_1D,
    N_BAS_LAG_2_1D, }, /* n_trace_bas_fcts */
  d_ortho_get_dof_indices_2_1d,
  d_ortho_get_bound_2_1d,
  d_ortho_interpol_2_1d,
  d_ortho_interpol_d_2_1d,
  d_ortho_interpol_dow_2_1d,
  d_ortho_get_int_vec_2_1d,
  d_ortho_get_real_vec_2_1d,
  d_ortho_get_real_d_vec_2_1d,
  (GET_REAL_VEC_D_TYPE)d_ortho_get_real_d_vec_2_1d,
  d_ortho_get_uchar_vec_2_1d,
  d_ortho_get_schar_vec_2_1d,
  d_ortho_get_ptr_vec_2_1d,
  d_ortho_get_real_dd_vec_2_1d,
  d_ortho_real_refine_inter_2_1d,
  d_ortho_real_coarse_inter_2_1d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&d_ortho_2_1d_data
};

