/**@file 
 *
 * An obscure basis-function set, which lives on the master-elements
 * of a given trace-mesh, e.g. to augment a given finite element space
 * with trace-bubbles and the like.
 *
 * Mmmh. To complicated. Abandoned in favour of a bulk-trace-bubble.
 *
 *      author:  Claus-Justus Heine
 *               Abteilung fuer Angewandte Mathematik                     
 *               Albert-Ludwigs-Universitaet Freiburg
 *               Hermann-Herder-Str. 10
 *               79104 Freiburg i.Br.
 *               Germany
 *               claus@mathematik.uni-freiburg.de
 *
 * Copyright (C) by Claus-Justus Heine (2008).
 *
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include <alberta/alberta.h>

#include "albas.h"

typedef struct trace_bulk_data 
{
  const EL        *cur_el;
  const EL_INFO   *cur_el_info;
  int             cur_wall;
  const EL        *cur_tr_el;
  INIT_EL_TAG_CTX tag_ctx;     /**< Tag context for caching purposes. */
  MESH            *trace_mesh; /**< Out dedicated trace mesh. */
  const BAS_FCTS  *bulk_bfcts; /**< The local basis function set in the bulk.*/
  INIT_EL_TAG     bulk_tag;
  const int       *trace_dof_map;
  EL_BNDRY_VEC    *bndry_loc;
  EL_REAL_VEC_D   *ipol_dow_loc;
  EL_REAL_VEC     *ipol_loc;
  EL_REAL_D_VEC   *ipol_d_loc;
  strucbult bfcts {
    BAS_FCT       *phi;
    GRD_BAS_FCT   *grd_phi;
    D2_BAS_FCT    *D2_phi;
    BAS_FCT_D     *phi_d;
    GRD_BAS_FCT_D *grd_phi_d;
    D2_BAS_FCT_D  *D2_phi_d;
  } bfcts[N_WALLS_MAX];
  bool dir_pw_const;
} TB_DATA;

static REAL tb_phi(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return PHI(data->bulk_bfcts, nr, lambda);
}
  
static const REAL *tb_grd_phi(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return GRD_PHI(data->bulk_bfcts, nr, lambda);
}

static
const REAL_B *tb_D2_phi(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return D2_PHI(data->bulk_bfcts, nr, lambda);
}

static const REAL *tb_phi_d(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;
  
  return PHI_D(data->bulk_bfcts, nr, lambda);
}
  
static
const REAL_B *tb_grd_phi_d(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return GRD_PHI_D(data->bulk_bfcts, nr, lambda);
}

static
const REAL_BB *tb_D2_phi_d(const REAL_B lambda, const BAS_FCTS *self, int nr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return D2_PHI_D(data->bulk_bfcts, nr, lambda);
}

#ifdef DEFUN_PHI
# undef DEFUN_PHI
#endif
#define DEFUN_PHI(nr)							\
  static REAL tb_phi_##nr(const REAL_B lambda, const BAS_FCTS *self)	\
  {									\
    return PHI(self, nr, lambda);					\
  }									\
  static								\
  const REAL *tb_grd_phi_##nr(const REAL_B lambda, const BAS_FCTS *self) \
  {									\
    return GRD_PHI(self, nr, lambda);					\
  }									\
  static								\
  const REAL_B *tb_D2_phi_##nr(const REAL_B lambda, const BAS_FCTS *self) \
  {									\
    return D2_PHI(self, nr, lambda);					\
  }									\
  static const REAL *tb_phi_d_##nr(const REAL_B lambda, const BAS_FCTS *self) \
  {									\
    return PHI_D(self, nr, lambda);					\
  }									\
  static								\
  const REAL_B *tb_grd_phi_d_##nr(const REAL_B lambda, const BAS_FCTS *self) \
  {									\
    return GRD_PHI_D(self, nr, lambda);					\
  }									\
  static								\
  const REAL_BB *tb_D2_phi_d_##nr(const REAL_B lambda, const BAS_FCTS *self) \
  {									\
    return D2_PHI_D(self, nr, lambda);					\
  }									\
  struct _AI_semicolon_dummy

DEFUN_PHI(0);
DEFUN_PHI(1);
DEFUN_PHI(2);
DEFUN_PHI(3);
DEFUN_PHI(4);
DEFUN_PHI(5);
DEFUN_PHI(6);
DEFUN_PHI(7);
DEFUN_PHI(8);
DEFUN_PHI(9);

DEFUN_PHI(10);
DEFUN_PHI(11);
DEFUN_PHI(12);
DEFUN_PHI(13);
DEFUN_PHI(14);
DEFUN_PHI(15);
DEFUN_PHI(16);
DEFUN_PHI(17);
DEFUN_PHI(18);
DEFUN_PHI(19);

DEFUN_PHI(20);
DEFUN_PHI(21);
DEFUN_PHI(22);
DEFUN_PHI(23);
DEFUN_PHI(24);
DEFUN_PHI(25);
DEFUN_PHI(26);
DEFUN_PHI(27);
DEFUN_PHI(28);
DEFUN_PHI(29);

DEFUN_PHI(30);
DEFUN_PHI(31);
DEFUN_PHI(32);
DEFUN_PHI(33);
DEFUN_PHI(34);
DEFUN_PHI(35);
DEFUN_PHI(36);
DEFUN_PHI(37);
DEFUN_PHI(38);
DEFUN_PHI(39);

DEFUN_PHI(40);
DEFUN_PHI(41);
DEFUN_PHI(42);
DEFUN_PHI(43);
DEFUN_PHI(44);
DEFUN_PHI(45);
DEFUN_PHI(46);
DEFUN_PHI(47);
DEFUN_PHI(48);
DEFUN_PHI(49);

DEFUN_PHI(50);
DEFUN_PHI(51);
DEFUN_PHI(52);
DEFUN_PHI(53);
DEFUN_PHI(54);
DEFUN_PHI(55);
DEFUN_PHI(56);
DEFUN_PHI(57);
DEFUN_PHI(58);
DEFUN_PHI(59);

static BAS_FCT phi_table[] = {
  tb_phi_0, tb_phi_1, tb_phi_2, tb_phi_3, tb_phi_4,
  tb_phi_5, tb_phi_6, tb_phi_7, tb_phi_8, tb_phi_9,

  tb_phi_10, tb_phi_11, tb_phi_12, tb_phi_13, tb_phi_14,
  tb_phi_15, tb_phi_16, tb_phi_17, tb_phi_18, tb_phi_19,

  tb_phi_20, tb_phi_21, tb_phi_22, tb_phi_23, tb_phi_24,
  tb_phi_25, tb_phi_26, tb_phi_27, tb_phi_28, tb_phi_29,

  tb_phi_30, tb_phi_31, tb_phi_32, tb_phi_33, tb_phi_34,
  tb_phi_35, tb_phi_36, tb_phi_37, tb_phi_38, tb_phi_39,

  tb_phi_40, tb_phi_41, tb_phi_42, tb_phi_43, tb_phi_44,
  tb_phi_45, tb_phi_46, tb_phi_47, tb_phi_48, tb_phi_49,

  tb_phi_50, tb_phi_51, tb_phi_52, tb_phi_53, tb_phi_54,
  tb_phi_55, tb_phi_56, tb_phi_57, tb_phi_58, tb_phi_59,
};

static GRD_BAS_FCT grd_phi_table[] = {
  tb_grd_phi_0, tb_grd_phi_1, tb_grd_phi_2, tb_grd_phi_3, tb_grd_phi_4,
  tb_grd_phi_5, tb_grd_phi_6, tb_grd_phi_7, tb_grd_phi_8, tb_grd_phi_9,

  tb_grd_phi_10, tb_grd_phi_11, tb_grd_phi_12, tb_grd_phi_13, tb_grd_phi_14,
  tb_grd_phi_15, tb_grd_phi_16, tb_grd_phi_17, tb_grd_phi_18, tb_grd_phi_19,

  tb_grd_phi_20, tb_grd_phi_21, tb_grd_phi_22, tb_grd_phi_23, tb_grd_phi_24,
  tb_grd_phi_25, tb_grd_phi_26, tb_grd_phi_27, tb_grd_phi_28, tb_grd_phi_29,

  tb_grd_phi_30, tb_grd_phi_31, tb_grd_phi_32, tb_grd_phi_33, tb_grd_phi_34,
  tb_grd_phi_35, tb_grd_phi_36, tb_grd_phi_37, tb_grd_phi_38, tb_grd_phi_39,

  tb_grd_phi_40, tb_grd_phi_41, tb_grd_phi_42, tb_grd_phi_43, tb_grd_phi_44,
  tb_grd_phi_45, tb_grd_phi_46, tb_grd_phi_47, tb_grd_phi_48, tb_grd_phi_49,

  tb_grd_phi_50, tb_grd_phi_51, tb_grd_phi_52, tb_grd_phi_53, tb_grd_phi_54,
  tb_grd_phi_55, tb_grd_phi_56, tb_grd_phi_57, tb_grd_phi_58, tb_grd_phi_59,
};

static D2_BAS_FCT D2_phi_table[] = {
  tb_D2_phi_0, tb_D2_phi_1, tb_D2_phi_2, tb_D2_phi_3, tb_D2_phi_4,
  tb_D2_phi_5, tb_D2_phi_6, tb_D2_phi_7, tb_D2_phi_8, tb_D2_phi_9,

  tb_D2_phi_10, tb_D2_phi_11, tb_D2_phi_12, tb_D2_phi_13, tb_D2_phi_14,
  tb_D2_phi_15, tb_D2_phi_16, tb_D2_phi_17, tb_D2_phi_18, tb_D2_phi_19,

  tb_D2_phi_20, tb_D2_phi_21, tb_D2_phi_22, tb_D2_phi_23, tb_D2_phi_24,
  tb_D2_phi_25, tb_D2_phi_26, tb_D2_phi_27, tb_D2_phi_28, tb_D2_phi_29,

  tb_D2_phi_30, tb_D2_phi_31, tb_D2_phi_32, tb_D2_phi_33, tb_D2_phi_34,
  tb_D2_phi_35, tb_D2_phi_36, tb_D2_phi_37, tb_D2_phi_38, tb_D2_phi_39,

  tb_D2_phi_40, tb_D2_phi_41, tb_D2_phi_42, tb_D2_phi_43, tb_D2_phi_44,
  tb_D2_phi_45, tb_D2_phi_46, tb_D2_phi_47, tb_D2_phi_48, tb_D2_phi_49,

  tb_D2_phi_50, tb_D2_phi_51, tb_D2_phi_52, tb_D2_phi_53, tb_D2_phi_54,
  tb_D2_phi_55, tb_D2_phi_56, tb_D2_phi_57, tb_D2_phi_58, tb_D2_phi_59,
};

static BAS_FCT_D phi_d_table[] = {
  tb_phi_d_0, tb_phi_d_1, tb_phi_d_2, tb_phi_d_3, tb_phi_d_4,
  tb_phi_d_5, tb_phi_d_6, tb_phi_d_7, tb_phi_d_8, tb_phi_d_9,

  tb_phi_d_10, tb_phi_d_11, tb_phi_d_12, tb_phi_d_13, tb_phi_d_14,
  tb_phi_d_15, tb_phi_d_16, tb_phi_d_17, tb_phi_d_18, tb_phi_d_19,

  tb_phi_d_20, tb_phi_d_21, tb_phi_d_22, tb_phi_d_23, tb_phi_d_24,
  tb_phi_d_25, tb_phi_d_26, tb_phi_d_27, tb_phi_d_28, tb_phi_d_29,

  tb_phi_d_30, tb_phi_d_31, tb_phi_d_32, tb_phi_d_33, tb_phi_d_34,
  tb_phi_d_35, tb_phi_d_36, tb_phi_d_37, tb_phi_d_38, tb_phi_d_39,

  tb_phi_d_40, tb_phi_d_41, tb_phi_d_42, tb_phi_d_43, tb_phi_d_44,
  tb_phi_d_45, tb_phi_d_46, tb_phi_d_47, tb_phi_d_48, tb_phi_d_49,

  tb_phi_d_50, tb_phi_d_51, tb_phi_d_52, tb_phi_d_53, tb_phi_d_54,
  tb_phi_d_55, tb_phi_d_56, tb_phi_d_57, tb_phi_d_58, tb_phi_d_59,
};

static GRD_BAS_FCT_D grd_phi_d_table[] = {
  tb_grd_phi_d_0, tb_grd_phi_d_1, tb_grd_phi_d_2, tb_grd_phi_d_3,
  tb_grd_phi_d_4,
  tb_grd_phi_d_5, tb_grd_phi_d_6, tb_grd_phi_d_7, tb_grd_phi_d_8,
  tb_grd_phi_d_9,

  tb_grd_phi_d_10, tb_grd_phi_d_11, tb_grd_phi_d_12, tb_grd_phi_d_13,
  tb_grd_phi_d_14,
  tb_grd_phi_d_15, tb_grd_phi_d_16, tb_grd_phi_d_17, tb_grd_phi_d_18,
  tb_grd_phi_d_19,

  tb_grd_phi_d_20, tb_grd_phi_d_21, tb_grd_phi_d_22, tb_grd_phi_d_23,
  tb_grd_phi_d_24,
  tb_grd_phi_d_25, tb_grd_phi_d_26, tb_grd_phi_d_27, tb_grd_phi_d_28,
  tb_grd_phi_d_29,

  tb_grd_phi_d_30, tb_grd_phi_d_31, tb_grd_phi_d_32, tb_grd_phi_d_33,
  tb_grd_phi_d_34,
  tb_grd_phi_d_35, tb_grd_phi_d_36, tb_grd_phi_d_37, tb_grd_phi_d_38,
  tb_grd_phi_d_39,

  tb_grd_phi_d_40, tb_grd_phi_d_41, tb_grd_phi_d_42, tb_grd_phi_d_43,
  tb_grd_phi_d_44,
  tb_grd_phi_d_45, tb_grd_phi_d_46, tb_grd_phi_d_47, tb_grd_phi_d_48,
  tb_grd_phi_d_49,

  tb_grd_phi_d_50, tb_grd_phi_d_51, tb_grd_phi_d_52, tb_grd_phi_d_53,
  tb_grd_phi_d_54,
  tb_grd_phi_d_55, tb_grd_phi_d_56, tb_grd_phi_d_57, tb_grd_phi_d_58,
  tb_grd_phi_d_59,
};

static D2_BAS_FCT_D D2_phi_d_table[] = {
  tb_D2_phi_d_0, tb_D2_phi_d_1, tb_D2_phi_d_2, tb_D2_phi_d_3,
  tb_D2_phi_d_4,
  tb_D2_phi_d_5, tb_D2_phi_d_6, tb_D2_phi_d_7, tb_D2_phi_d_8,
  tb_D2_phi_d_9,

  tb_D2_phi_d_10, tb_D2_phi_d_11, tb_D2_phi_d_12, tb_D2_phi_d_13,
  tb_D2_phi_d_14,
  tb_D2_phi_d_15, tb_D2_phi_d_16, tb_D2_phi_d_17, tb_D2_phi_d_18,
  tb_D2_phi_d_19,

  tb_D2_phi_d_20, tb_D2_phi_d_21, tb_D2_phi_d_22, tb_D2_phi_d_23,
  tb_D2_phi_d_24,
  tb_D2_phi_d_25, tb_D2_phi_d_26, tb_D2_phi_d_27, tb_D2_phi_d_28,
  tb_D2_phi_d_29,

  tb_D2_phi_d_30, tb_D2_phi_d_31, tb_D2_phi_d_32, tb_D2_phi_d_33,
  tb_D2_phi_d_34,
  tb_D2_phi_d_35, tb_D2_phi_d_36, tb_D2_phi_d_37, tb_D2_phi_d_38,
  tb_D2_phi_d_39,

  tb_D2_phi_d_40, tb_D2_phi_d_41, tb_D2_phi_d_42, tb_D2_phi_d_43,
  tb_D2_phi_d_44,
  tb_D2_phi_d_45, tb_D2_phi_d_46, tb_D2_phi_d_47, tb_D2_phi_d_48,
  tb_D2_phi_d_49,

  tb_D2_phi_d_50, tb_D2_phi_d_51, tb_D2_phi_d_52, tb_D2_phi_d_53,
  tb_D2_phi_d_54,
  tb_D2_phi_d_55, tb_D2_phi_d_56, tb_D2_phi_d_57, tb_D2_phi_d_58,
  tb_D2_phi_d_59,
};

static const int id_map[] = {
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
  30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
  40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
  50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
};

static
void fill_jump_tables(struct bfcts *jump_table,
		      const int trace_map[],
		      int n_trace_fcts)
{
  int i;

  if (jump_table->phi) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->phi[i] = phi_table[trace_map[i]];
    }
  }
  if (jump_table->grd_phi) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->grd_phi[i] = grd_phi_table[trace_map[i]];
    }
  }
  if (jump_table->D2_phi) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->D2_phi[i] = D2_phi_table[trace_map[i]];
    }
  }
  if (jump_table->phi_d) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->phi_d[i] = phi_d_table[trace_map[i]];
    }
  }
  if (jump_table->grd_phi_d) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->grd_phi_d[i] = grd_phi_d_table[trace_map[i]];
    }
  }
  if (jump_table->D2_phi_d) {
    for (i = 0; i < n_trace_fcts; i++) {
      jump_table->D2_phi_d[i] = D2_phi_d_table[trace_map[i]];
    }
  }
}

static
INIT_EL_TAG trace_bulk_init_element(const EL_INFO *el_info, void *thisptr)
{
  FUNCNAME("trace_bulk_init_element");
  BAS_FCTS *self = (BAS_FCTS *)thisptr;
  TB_DATA *data = (TB_DATA *)self->ext_data;
  MESH *mesh;
  int w, dim;
  INIT_EL_TAG bulk_tag;
  const EL *tr_el;

  if (el_info == NULL) {
    self->n_bas_fcts = 0;
    self->dir_pw_const = true;
    data->cur_el = NULL;
    data->cur_el_info = NULL;
    INIT_OBJECT(data->bulk_bfcts);
    INIT_EL_TAG_CTX_DFLT(&data->tag_ctx);
    return INIT_EL_TAG_CTX_TAG(&data->tag_ctx);
  }

  if (data->cur_el == el_info->el && data->cur_el_info == el_info) {
    return INIT_EL_TAG_CTX_TAG(&data->tag_ctx);    
  }
  data->cur_el      = el_info->el;
  data->cur_el_info = el_info;
  
  mesh = el_info->mesh;
  dim = mesh->dim;

  for (w = 0; w < N_WALLS(dim); w++) {
    int o, t;
    if ((tr_el = get_slave_el(el_info->el, w, data->trace_mesh)) == NULL) {
      continue;
    }
    bulk_tag = INIT_ELEMENT(el_info, data->bulk_bfcts);
    TEST_EXIT(bulk_tag == INIT_EL_TAG_DFLT);
    o = el_info->orientation < 0;
    t = el_info->el_type > 0;
    data->trace_dof_map = data->bulk_bfcts->trace_dof_map[t][o][w];
    if (data->cur_wall != w || bulk_tag != data->bulk_tag) {
      data->bndry_loc->n_components = data->bulk_bfcts->n_trace_bas_fcts[w];
      self->phi          = data->bfcts[w].phi;
      self->grd_phi      = data->bfcts[w].grd_phi;
      self->d2_phi       = data->bfcts[w].d2_phi;
      self->phi_d        = data->bfcts[w].phi_d;
      self->grd_phi_d    = data->bfcts[w].grd_phi_d;
      self->d2_phi_d     = data->bfcts[w].d2_phi_d;
      data->cur_wall     = w;
      self->dir_pw_const = data->dir_pw_const;
      INIT_EL_TAG_CTX_UNIQ(&self->tag_ctx);
    }
    data->bulk_tag = bulk_tag;
    break;
  }

  data->tr_el = tr_el;

  return INIT_EL_TAG_CTX_TAG(&data->tag_ctx);
}

static const EL_DOF_VEC *
tb_get_dof_indices(DOF *dof,
		   const EL *el,
		   const DOF_ADMIN *admin,
		   const BAS_FCTGS *self)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  return GET_DOF_INDICES(
    data->bulk_bfcts->trace_bas_fcts, data->tr_el, admin, dof);
}
    
static const EL_BNDRY_VEC *
tb_get_bound(BNDRY_FLAGS *bound,
	     const EL_INFO *el_info,
	     const BAS_FCTS *self)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;
  BNDRY_FLAGS bndry_loc[data->bulk_bfcts->n_bas_fcts];
  BNDRY_FLAGS *vec = bound == NULL ? data->bndry_loc->vec : bound;
  int i;

  GET_BOUND(data->bulk_bfcts, el_info, bndry_loc);
  for (i = 0; i < self->n_bas_fcts; i++) {
    vec[i] = bndry_loc[data->trace_dof_map[i]];
  }
  return bound == NULL ? data->bndry_loc : bound;
}

static void tb_interpol(EL_REAL_VEC *coeff,
			const EL_INFO *el_info, int wall,
			int n, const int *indices,
			LOC_FCT_AT_QP f, void *f_data,
			const BAS_FCTS *thisptr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;
  REAL tmp_coeff[self->n_bas_fcts];

  if (wall 

  if (indices) {
    int tmp_indices[data->bulk_bfcts->n_bas_fcts];
    memcpy(tmp_coeff, coeff->vec, self->n_bas_fcts * sizeof(REAL));
    coeff->n_components = data->bulk_bfcts->n_bas_fcts;
    memset(coeff->vec, 0, sizeof(REAL) * coeff->n_components);
    for (i = 0; i < coeff->n_components; i++) {
      coeff->vec[data->trace_dof_map[i]] = tmp_coeff[i];
    }
    for (i = 0; i < n; i++) {
      tmp_indices[i] = data->trace_dof_map[indices[i]];
    }
    INTERPOL(coeff,
	     el_info, data->cur_wall, n, tmp_indices,
	     f, f_data, data->bulk_bfcts);
    for (i = 0; i < self->n_bas_fcts; i++) {
      tmp_coeff[i] = coeff->vec[data->trace_dof_map[i]];
    }
    for (i = 0; i < self->n_bas_fcts; i++) {
      coeff->vec[i] = temp_coeff[i];
    }
  } else {
    
  

  if (indices == NULL) {
    for (i = 0; i < self->n_bas_fcts; i++) {
      tmp_coeff =

      coeff->vec[i] = coeff
data->ipol_loc_dow[data->trace_dof_map[i]];
    }
  } else {
    for (i = 0; i < n; i++) {
      int dst = b_no[i];
      int src = data->trace_dof_map[dst];
      vec->vec[dst] = data->ipol_loc_dow[src];
    }
  }
}

static void tb_interpol_d(EL_REAL_D_VEC *coeff,
			  const EL_INFO *el_info, int wall,
			  int n, const int *indices,
			  LOC_FCT_D_AT_QP f, void *f_data,
			  const BAS_FCTS *thisptr)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  INTERPOL_D(data->ipol_loc_dow,
	     el_info, data->cur_wall, -1, NULL, f, f_data, data->bulk_bfcts);

  if (indices == NULL) {
    for (i = 0; i < self->n_bas_fcts; i++) {
      COPY_DOW(data->ipol_loc_dow[data->trace_dof_map[i]], vec->vec[i]);
    }
  } else {
    for (i = 0; i < n; i++) {
      int dst = b_no[i];
      int src = data->trace_dof_map[dst];
      COPY_DOW(data->ipol_loc_dow[src], vec->vec[dst]);
    }
  }
}

static void tb_interpol_dow(EL_REAL_VEC_D *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 *self)
{
  TB_DATA *data = (TB_DATA *)self->ext_data;

  INTERPOL_DOW(data->ipol_loc_dow,
	       el_info, data->cur_wall, -1, NULL, f, f_data, data->bulk_bfcts);

  if (b_no == NULL) {
    for (i = 0; i < self->n_bas_fcts; i++) {
      vec->vec[i] = data->ipol_loc_dow[data->trace_dof_map[i]];
    }
  } else {
    for (i = 0; i < no; i++) {
      int dst = b_no[i];
      int src = data->trace_dof_map[dst];
      vec->vec[dst] = data->ipol_loc_dow[src];
    }
  }
}

const BAS_FCTS *get_trace_bulk_space(const BAS_FCTS *bulk_bfcts,
				     MESH *trace_mesh)
{
  FUNCNAME("get_trace_bulk_space");
  BAS_FCTS *trace_bulk_bfcts;
  TB_DATA  *data;
  int dim = bulk_bfcts->dim;
  char *name;
  int i;

  TEST_EXIT(dim == trace_mesh->dim + 1,
	    "Wrong dimensions: bulk-bas-fcts: %d, trace-mesh: %d\n",
	    dim, trace_mesh->dim);

  trace_bulk_bfcts = MEM_CALLOC(1, BAS_FCTS);
  trace_bulk_bfcts->name = name =
    malloc(sizeof("TraceBulk@@")+strlen(bulk_bfcts)+strlen(trace_mesh->name));
  sprintf(name, "TraceBulk@%s@", trace_mesh->name, bulk_bfcts->name);
  trace_bulk_bfcts->dim    = bulk_bfcts->dim;
  trace_bulk_bfcts->rdim   = bulk_bfcts->rdim;
  trace_bulk_bfcts->degree = bulk_bfcts->degree;
  trace_bulk_bfcts->n_bas_fcts = 0;
  /* Fake to many basis functions in order to make it easier to
   * forward some call-back functions to the underlying bulk basis
   * functions.
   */
  trace_bulk_bfcts->n_bas_fcts_max =
    N_WALLS_MAX * bulk_bfcts->trace_bas_fcs->n_bas_fcts_max;
  
  trace_bulk_bfcts->n_dof[VERTEX] = bulk_bfcts->trace_bas_fcs->n_dof[VERTEX];
  trace_bulk_bfcts->n_dof[CENTER] = bulk_bfcts->trace_bas_fcs->n_dof[CENTER];
  trace_bulk_bfcts->n_dof[EDGE]   = bulk_bfcts->trace_bas_fcs->n_dof[EDGE];
  trace_bulk_bfcts->n_dof[FACE]   = bulk_bfcts->trace_bas_fcs->n_dof[FACE];

  CHAIN_INIT(trace_bulk_bfcts);
  trace_bulk_bfcts->unchained = trace_bulk_bfcts;

  trace_bulk_bfcts->trace_bas_fcts = bulk_bfcts->trace_bas_fcts;
  for (w = 0; w < N_WALLS(dim); w++) {
    trace_bulk_bfcts->n_trace_bas_fcts = 0;
    for (t = 0; t < 1; t++) {
      for (o = 0; o < 1; o++) {
	trace_bulk_bfcts->trace_dof_map[t][o][w] = id_map;
      }
    }
  }
  trace_bulk_bfcts->get_dof_indices = tb_get_dof_indices;
  trace_bulk_bfcts->get_dof_indices = tb_get_bound;
  trace_bulk_bfcts->interpol        = tb_interpol;
  trace_bulk_bfcts->interpol_d      = tb_interpol_d;
  trace_bulk_bfcts->interpol_dow    = tb_interpol_dow;

  trace_bulk_bfcts->dir_pw_const    = bulk_bas_fcts->dir_pw_const;
 
  /* Hook in the default get_FOOBAR_vec() routines */
  trace_bulk_bfcts->get_int_vec     = default_get_int_vec;
  trace_bulk_bfcts->get_real_vec    = default_get_real_vec;
  trace_bulk_bfcts->get_real_d_vec  = default_get_real_d_vec;
  trace_bulk_bfcts->get_real_vec_d  = default_get_real_vec_d;
  trace_bulk_bfcts->get_uchar_vec   = default_get_uchar_vec;
  trace_bulk_bfcts->get_schar_vec   = default_get_schar_vec;
  trace_bulk_bfcts->get_ptr_vec     = default_get_ptr_vec;

  /* */
}

