/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* www.alberta-fem.de                                                       */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/* file:     traverse_r_2d.c                                                */
/*                                                                          */
/* description:                                                             */
/*           recursive mesh traversal - 2d routines:                        */
/*           fill_macro_info_2d(), fill_elinfo_2d()                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                         */
/*             Universitaet Freiburg                                        */
/*             Hermann-Herder-Str. 10                                       */
/*             79104 Freiburg, Germany                                      */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*         C.-J. Heine (2006-2008), D. Koester (2004-2006).                 */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/*   fill_macro_info_2d:                              		            */
/*   ----------------                                   		    */
/*   fill EL_INFO structure for a macro element         		    */
/*--------------------------------------------------------------------------*/

#if DIM_MAX >= 3
static void fill_master_macro_info_2d(struct master_info *mst_info,
				      FLAGS fill_flags,
				      MACRO_EL *mst_mel,
				      int mst_ov);
#endif

static void
fill_macro_info_2d(MESH *mesh, const MACRO_EL *mel, EL_INFO *el_info)
{
  FUNCNAME("fill_macro_info_2d");
  EL       *nb;
  MACRO_EL *mnb;
  int      i, ov;
  FLAGS    fill_opp_coords;

  el_info->mesh     = mesh;
  el_info->macro_el = mel;
  el_info->el       = mel->el;
  el_info->parent   = NULL;
  el_info->level    = 0;

  el_info->el_type     = 0;
  el_info->orientation = 1;

  /* Set FILL_NON_PERIODIC in fill-flags s.t. we do not need to check
   * mesh->is_periodic _and_ (fill_flag & FILL_NON_PERIODIC) all the
   * time. Afterwards it suffices to look at el_info->fill_flag.
   *
   * But beware: MACRO_el->neigh also includes the non-periodic
   * neighbourhood relations, MACRO_el->wall_bound also includes the
   * non-periodic boundary types. However, when looking at the EL_INFO
   * structure it suffices to have a look a FILL_NON_PERIODIC. For
   * aesthetical reasons we should convert to FILL_PERIODIC instead of
   * ..._NON_...
   */
  if (!mesh->is_periodic) {
    el_info->fill_flag |= FILL_NON_PERIODIC;
  }

  /* Always fill the macro-wall mapping for now */
  for (i = 0; i < N_WALLS_2D; i++) {
    el_info->macro_wall[i] = i;
  }
  el_info->fill_flag |= FILL_MACRO_WALLS;

  if (el_info->fill_flag & FILL_COORDS) {
    for (i=0; i < N_VERTICES_2D; i++) {
      DEBUG_TEST_EXIT(mel->coord[i], "no mel->coord[%d]\n", i);

      COPY_DOW(*mel->coord[i], el_info->coord[i]);
    }
  }

  if (el_info->fill_flag & (FILL_OPP_COORDS | FILL_NEIGH)) {
    fill_opp_coords = (el_info->fill_flag & FILL_OPP_COORDS);

    for (i = 0; i < N_NEIGH_2D; i++) {
      mnb = mel->neigh[i];
      
      if (mesh->is_periodic
	  && (el_info->fill_flag & FILL_NON_PERIODIC)
	  && mel->neigh_vertices[i][0] >= 0) {
	/* periodic boundary, non-periodic traversal requested, so
	 * fake a real boundary.
	 */
	el_info->neigh[i] = NULL;
      } else if ((mnb = mel->neigh[i])) {

	nb = el_info->neigh[i]      = mel->neigh[i]->el;
	ov = el_info->opp_vertex[i] = mel->opp_vertex[i];

	if (nb->child[0] && (ov != 2)) { /*---  make nb nearest element  ---*/
	  if  (fill_opp_coords) {
	    if (mesh->is_periodic && mel->wall_trafo[i]) {
	      REAL *nbcp;
	      REAL_D tmp;
	      if (nb->new_coord) {
		nbcp = nb->new_coord;
	      } else {
		AXPBY_DOW(0.5, *mnb->coord[0], 0.5, *mnb->coord[1], tmp);
		nbcp = tmp;
	      }
	      AFFINE_DOW(mel->wall_trafo[i], nbcp, el_info->opp_coord[i]);
	    } else {
	      if (nb->new_coord) {
		COPY_DOW(nb->new_coord, el_info->opp_coord[i]);
	      } else {
		AXPBY_DOW(0.5, *mnb->coord[0], 0.5, *mnb->coord[1],
			  el_info->opp_coord[i]);
	      }
	    }
	  }
	  nb = el_info->neigh[i] = nb->child[1-ov];
	  el_info->opp_vertex[i] = 2;
	} else {
	  if  (fill_opp_coords) {
	    if (mesh->is_periodic && mel->wall_trafo[i])
	      AFFINE_DOW(mel->wall_trafo[i], *mnb->coord[ov],
			 el_info->opp_coord[i]);
	    else
	      COPY_DOW(*mnb->coord[ov], el_info->opp_coord[i]);
	  }
	}
      } else {
	el_info->neigh[i] = NULL;
      }
    }
  }

  if ((el_info->fill_flag & FILL_BOUND) != 0) {
    if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
      el_info->face_bound[0] = INTERIOR;
      for (i=0;i<N_WALLS_2D; i++) {
	BNDRY_FLAGS_INIT(el_info->edge_bound[i]);
      }
    }
    if (!mesh->is_periodic) {
      for (i = 0; i < N_WALLS_2D; i++) {
	el_info->wall_bound[i] = mel->wall_bound[i];
      }
      if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	for (i = 0; i < N_VERTICES_2D; i++) {
	  BNDRY_FLAGS_CPY(el_info->vertex_bound[i], mel->vertex_bound[i]);
	  BNDRY_FLAGS_SET(el_info->edge_bound[i], el_info->wall_bound[i]);
	}
      }
    } else {
      /* Support periodic and non-periodic neighbourhood and boundary
       * information on periodic meshes.
       */
      if (el_info->fill_flag & FILL_NON_PERIODIC) {
	  /* use non-periodic boundary information */
	for (i=0; i < N_WALLS_2D; i++) {
	  el_info->wall_bound[i] = mel->wall_bound[i];
	}
	if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	  for (i = 0; i < N_VERTICES_2D; i++) {
	    BNDRY_FLAGS_CPY(el_info->vertex_bound[i], mel->np_vertex_bound[i]);
	    BNDRY_FLAGS_SET(el_info->edge_bound[i], el_info->wall_bound[i]);
	  }
	}
      } else {
	/* use periodic boundary information */
	for (i=0;i<N_WALLS_2D; i++) {
	  if (mel->neigh_vertices[i][0] == -1) {
	    el_info->wall_bound[i] = mel->wall_bound[i];
	  } else {
	    el_info->wall_bound[i] = INTERIOR;
	  }
	}
	if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	  for (i = 0; i < N_VERTICES_2D; i++) {
	    BNDRY_FLAGS_CPY(el_info->vertex_bound[i], mel->vertex_bound[i]);
	    BNDRY_FLAGS_SET(el_info->edge_bound[i], el_info->wall_bound[i]);
	  }
	}
      }
    }
  }

  if (el_info->fill_flag & FILL_PROJECTION) {
    el_info->active_projection = mel->projection[0];

    /* If we have a projection for the refinement edge, it takes precedence! */
    if (mel->projection[3])
      el_info->active_projection = mel->projection[3];
  }

#if DIM_MAX >= 3
  if (el_info->fill_flag & FILL_MASTER_INFO) {
    MACRO_EL *mst_mel   = mel->master.macro_el;
    int      mst_ov     = mel->master.opp_vertex;

    fill_master_macro_info_2d(
      &el_info->master, el_info->fill_flag, mst_mel, mst_ov);

    if ((el_info->fill_flag & FILL_MASTER_NEIGH) != 0) {
      if (mst_mel->neigh[mst_ov] &&
	  (!mesh->is_periodic ||
	   !(el_info->fill_flag & FILL_NON_PERIODIC) ||
	   mst_mel->neigh_vertices[mst_ov][0] < 0)) {
	fill_master_macro_info_2d(&el_info->mst_neigh,
				  el_info->fill_flag,
				  mst_mel->neigh[mst_ov],
				  mst_mel->opp_vertex[mst_ov]);
      } else {
	el_info->mst_neigh.el = NULL;
      }
    }
    if ((el_info->fill_flag & FILL_BOUND) != 0) {
      /* Fetch the boundary classification from the master
       * element. Use el_info->edge_bound to store this information;
       * el_info->vertex bound will then also hold the classification
       * of the vertices relative to the master element.
       */
      if (mst_mel->neigh[mst_ov] == NULL ||
	  (mesh->is_periodic &&
	   (el_info->fill_flag & FILL_NON_PERIODIC) &&
	   mst_mel->neigh_vertices[mst_ov][0] >= 0)) {
	/* face boundary */
	el_info->face_bound[0] = mst_mel->face_bound[mst_ov];
      }
      if (!mesh->is_periodic || (el_info->fill_flag & FILL_NON_PERIODIC) == 0) {
	for (i = 0; i < N_EDGES_2D; i++) {
	  BNDRY_FLAGS_CPY(el_info->edge_bound[i], mel->master.edge_bound[i]);
	}
	for (i = 0; i < N_VERTICES_2D; i++) {
	  BNDRY_FLAGS_CPY(el_info->vertex_bound[i],
			  mel->master.vertex_bound[i]);
	}
      } else {
	for (i = 0; i < N_EDGES_2D; i++) {
	  BNDRY_FLAGS_CPY(el_info->edge_bound[i], mel->master.np_edge_bound[i]);
	}
	for (i = 0; i < N_VERTICES_2D; i++) {
	  BNDRY_FLAGS_CPY(el_info->vertex_bound[i],
			  mel->master.np_vertex_bound[i]);
	}
      }
    }
  }
#endif
}

#if DIM_MAX >= 3
static void fill_master_macro_info_2d(struct master_info *mst_info,
				      FLAGS fill_flags,
				      MACRO_EL *mst_mel,
				      int mst_ov)
{
  EL       *mst_el    = mst_mel->el;
  int      mst_type   = mst_mel->el_type;
  int      mst_orient = mst_mel->orientation;

  if (mst_el->child[0] && mst_ov < 2) {
    /* traverse to nearest neighbour */
    mst_el     = mst_el->child[1-mst_ov];
    mst_orient = mst_orient * child_orientation_3d[mst_type][1-mst_ov];
    mst_ov     = 3;
    mst_type   = (mst_type + 1) % 3;

    if (fill_flags & FILL_COORDS) {
      if (mst_mel->el->new_coord) {
	COPY_DOW(mst_mel->el->new_coord, mst_info->opp_coord);
      } else {
	AXPBY_DOW(0.5, *mst_mel->coord[0], 0.5, *mst_mel->coord[1],
		  mst_info->opp_coord);
      }
    }
  } else if (fill_flags & FILL_COORDS) {
    COPY_DOW(*mst_mel->coord[mst_ov], mst_info->opp_coord);
  }
  mst_info->el          = mst_el;
  mst_info->opp_vertex  = mst_ov;
  mst_info->el_type     = mst_type;
  mst_info->orientation = mst_orient;
}
#endif

/*--------------------------------------------------------------------------*/
/*   fill_elinfo_2d:                                  		            */
/*   ------------                                       		    */
/*   fill EL_INFO structure for one child of an element   		    */
/*--------------------------------------------------------------------------*/

#if DIM_MAX >= 3
static void fill_master_info_2d(struct master_info *mst_info,
				const struct master_info *mst_info_old,
				const EL_INFO *el_info,
				int ichild,
				FLAGS fill_flags);
#endif

static void fill_elinfo_2d(int ichild, FLAGS mask,
			   const EL_INFO *el_info_old, EL_INFO *el_info)
{
  FUNCNAME("fill_el_info_2d");
  EL    *nb;
  EL    *el = el_info_old->el;
  FLAGS fill_flag = el_info_old->fill_flag & mask;
  FLAGS fill_opp_coords;

  DEBUG_TEST_EXIT(el->child[0], "no children?\n");
  el_info->el = el->child[ichild];
  DEBUG_TEST_EXIT(el_info->el, "missing child %d?\n", ichild);

  el_info->macro_el  = el_info_old->macro_el;
  el_info->fill_flag = fill_flag;
  el_info->mesh      = el_info_old->mesh;
  el_info->parent    = el_info_old;
  el_info->level     = el_info_old->level + 1;

  el_info->el_type     = 0;
  el_info->orientation = 1;

  /* Fill the boundary mapping to the macro element in any case, it is
   * cheap enough. Maybe introduce a new fill-flag later
   * on. FILL_MACRO_WALLS or so.
   */
  if (ichild == 0) {
    el_info->macro_wall[0] = el_info_old->macro_wall[2];
    el_info->macro_wall[1] = -1; /* interior wall between children */
    el_info->macro_wall[2] = el_info_old->macro_wall[1];
  } else {
    el_info->macro_wall[0] = -1; /* interior wall between children */
    el_info->macro_wall[1] = el_info_old->macro_wall[2];
    el_info->macro_wall[2] = el_info_old->macro_wall[0];
  }

  if (fill_flag & FILL_COORDS) {
    if (el->new_coord) {
      COPY_DOW(el->new_coord, el_info->coord[2]);
    } else {
      AXPBY_DOW(0.5, el_info_old->coord[0], 0.5, el_info_old->coord[1],
		el_info->coord[2]);
    }

    if (ichild == 0) {
      COPY_DOW(el_info_old->coord[2], el_info->coord[0]);
      COPY_DOW(el_info_old->coord[0], el_info->coord[1]);
    } else {
      COPY_DOW(el_info_old->coord[1], el_info->coord[0]);
      COPY_DOW(el_info_old->coord[2], el_info->coord[1]);
    }
  }

  if (fill_flag & (FILL_NEIGH | FILL_OPP_COORDS)) {
    /* Update neighbour information for the children inside this
     * element. This is unproblematic, even for non-oriented
     * (non-orientable) meshes.
     */
    fill_opp_coords = (fill_flag & FILL_OPP_COORDS);
    if (ichild==0) {
      if ((el_info->neigh[2] = el_info_old->neigh[1])) {
	if (fill_opp_coords)  {
	  COPY_DOW(el_info_old->opp_coord[1], el_info->opp_coord[2]);
	}
      }
      el_info->opp_vertex[2] = el_info_old->opp_vertex[1];

      if (el->child[1]->child[0]) {
	el_info->neigh[1] = el->child[1]->child[1];
	DEBUG_TEST_EXIT(el_info->neigh[1],
	     "el->child[1]->child[0]!=NULL, but el->child[1]->child[1]=NULL\n");

	el_info->opp_vertex[1] = 2;
	if (fill_opp_coords) {
	  if (el->child[1]->new_coord) {
	    COPY_DOW(el->child[1]->new_coord, el_info->opp_coord[1]);
	  } else {
	    AXPBY_DOW(0.5, el_info_old->coord[1], 0.5, el_info_old->coord[2],
		      el_info->opp_coord[1]);
	  }
	}
      } else {
	el_info->neigh[1] = el->child[1];
	DEBUG_TEST_EXIT(el_info->neigh[1],
		    "el->child[0] != NULL, but el->child[1] = NULL\n");

	el_info->opp_vertex[1] = 0;
	if (fill_opp_coords) {
	  COPY_DOW(el_info_old->coord[1], el_info->opp_coord[1]);
	}
      }

      if ((nb = el_info_old->neigh[2])) {
	int ich;

	/* Update neighbour information for children across the
	 * refinement edge. We have to be careful if both elements
	 * have different orientations.
	 */
	if (el_info_old->el->dof[0][0] == nb->dof[0][0]) {
	  ich = 0; /* differing orientation */
	} else {
	  ich = 1; /* compatible orientations */
	}
	
	DEBUG_TEST(el_info_old->opp_vertex[2] == 2, "invalid neighbour\n"); 
	DEBUG_TEST_EXIT(nb->child[0], "missing children?\n");
	nb = nb->child[ich];
	DEBUG_TEST_EXIT(nb, "missing child[%d]?\n", ich);

	if (nb->child[0]) {
	  el_info->opp_vertex[0] = 2;
	  if (fill_opp_coords) {
	    if (nb->new_coord) {
	      const AFF_TRAFO *wt;
	      if ((wt = wall_trafo(el_info_old, 2)) != NULL) {
		AFFINE_DOW(wt, nb->new_coord, el_info->opp_coord[0]);
	      } else {
		COPY_DOW(nb->new_coord, el_info->opp_coord[0]);
	      }
	    } else {
	      AXPBY_DOW(0.5, el_info_old->opp_coord[2],
			0.5, el_info_old->coord[0],
			el_info->opp_coord[0]);
	    }
	  }    
	  nb = nb->child[1-ich];
	} else  {
	  el_info->opp_vertex[0] = ich;
	  if (fill_opp_coords) {
	    COPY_DOW(el_info_old->opp_coord[2], el_info->opp_coord[0]);
	  }
	}
	el_info->neigh[0] = nb;
      } else {
	el_info->neigh[0] = NULL;
      }
    } else { /* ichild==1 */
      if ((el_info->neigh[2] = el_info_old->neigh[0])) {
	if (fill_opp_coords) {
	  COPY_DOW(el_info_old->opp_coord[0], el_info->opp_coord[2]);	  
	}
      }
      el_info->opp_vertex[2] = el_info_old->opp_vertex[0];

      if (el->child[0]->child[0]) {
	el_info->neigh[0] = el->child[0]->child[0];
	el_info->opp_vertex[0] = 2;
	if (fill_opp_coords) {
	  if (el->child[0]->new_coord) {
	    COPY_DOW(el->child[0]->new_coord, el_info->opp_coord[0]);
	  } else {
	    AXPBY_DOW(0.5, el_info_old->coord[0], 0.5, el_info_old->coord[2],
		      el_info->opp_coord[0]);
	  }
	}
      } else {
	el_info->neigh[0] = el->child[0];
	el_info->opp_vertex[0] = 1;
	if (fill_opp_coords) {
	  COPY_DOW(el_info_old->coord[0], el_info->opp_coord[0]);
	}
      }

      if ((nb = el_info_old->neigh[2])) {
	int ich;

	/* Update neighbour information for children across the
	 * refinement edge. We have to be careful if both elements
	 * have different orientations.
	 */
	if (el_info_old->el->dof[0][0] == nb->dof[0][0]) {
	  ich = 1; /* differing orientation */
	} else {
	  ich = 0; /* compatible orientations */
	}

	DEBUG_TEST(el_info_old->opp_vertex[2] == 2,"invalid neighbour\n"); 
	nb = nb->child[ich];
	DEBUG_TEST(nb, "missing child?\n");
	if (nb->child[0]) {
	  el_info->opp_vertex[1] = 2;
	  if (fill_opp_coords)  {
	    if (nb->new_coord) {
	      const AFF_TRAFO *wt;
	      if ((wt = wall_trafo(el_info_old, 2)) != NULL) {
		AFFINE_DOW(wt, nb->new_coord, el_info->opp_coord[1]);
	      } else {
		COPY_DOW(nb->new_coord, el_info->opp_coord[1]);
	      }
	    } else {
	      AXPBY_DOW(0.5, el_info_old->opp_coord[2],
			0.5, el_info_old->coord[1],
			el_info->opp_coord[1]);
	    }
	  }
	  nb = nb->child[1-ich];
	} else {
	  el_info->opp_vertex[1] = ich;
	  if (fill_opp_coords) {
	    COPY_DOW(el_info_old->opp_coord[2], el_info->opp_coord[1]);
	  }
	}
	el_info->neigh[1] = nb;
      } else {
	el_info->neigh[1] = NULL;
      }
    }
  }

  if (fill_flag & FILL_BOUND) {
    /* "face_bound" is the boundary type inherited from the bulk-mesh
     * if this is a trace mesh. And so it is attached to the interior
     * of the element, which might be a boundary face of the bulk-mesh
     * (but more likely face_bound[0] will just be INTERIOR).
     */
    el_info->face_bound[0] = el_info_old->face_bound[0];

    BNDRY_FLAGS_CPY(el_info->vertex_bound[2], el_info_old->edge_bound[2]);

    if (ichild==0) {
      BNDRY_FLAGS_CPY(el_info->vertex_bound[0], el_info_old->vertex_bound[2]);
      BNDRY_FLAGS_CPY(el_info->vertex_bound[1], el_info_old->vertex_bound[0]);

      BNDRY_FLAGS_CPY(el_info->edge_bound[0], el_info_old->edge_bound[2]);
      BNDRY_FLAGS_INIT(el_info->edge_bound[1]);
      BNDRY_FLAGS_CPY(el_info->edge_bound[2], el_info_old->edge_bound[1]);

      el_info->wall_bound[0] = wall_bound(el_info_old, 2);
      el_info->wall_bound[1] = INTERIOR;
      el_info->wall_bound[2] = el_info_old->wall_bound[1];
    } else {
      BNDRY_FLAGS_CPY(el_info->vertex_bound[0], el_info_old->vertex_bound[1]);
      BNDRY_FLAGS_CPY(el_info->vertex_bound[1], el_info_old->vertex_bound[2]);

      BNDRY_FLAGS_INIT(el_info->edge_bound[0]);
      BNDRY_FLAGS_CPY(el_info->edge_bound[1], el_info_old->edge_bound[2]);
      BNDRY_FLAGS_CPY(el_info->edge_bound[2], el_info_old->edge_bound[0]);

      el_info->wall_bound[0] = INTERIOR;
      el_info->wall_bound[1] = el_info_old->wall_bound[2];
      el_info->wall_bound[2] = el_info_old->wall_bound[0];
    }
  }

  if (fill_flag & FILL_PROJECTION) {
    if (wall_proj(el_info, 2) != NULL)
      el_info->active_projection = wall_proj(el_info, 2);
    else
      el_info->active_projection = wall_proj(el_info, -1);
  }

#if DIM_MAX >= 3
  if (fill_flag & FILL_MASTER_INFO) {
    fill_master_info_2d(&el_info->master, &el_info_old->master,
			el_info, ichild, fill_flag);
    if ((fill_flag & FILL_MASTER_NEIGH) != 0) {
      if (el_info_old->mst_neigh.el != NULL) {
	fill_master_info_2d(&el_info->mst_neigh, &el_info_old->mst_neigh,
			    el_info, ichild, fill_flag);
      } else {
	el_info->mst_neigh.el = NULL;
      }
    }
  }
#endif
}

#if DIM_MAX >= 3
static void fill_master_info_2d(struct master_info *mst_info,
				const struct master_info *mst_info_old,
				const EL_INFO *el_info,
				int ichild,
				FLAGS fill_flags)
{
  /* When we reach here we know that we are adjacent to the
   * refinement edge of our master element. The logic is the same as
   * in traverse_r_3d.c, taking the mapping to the master childs
   * into account, as defined in submesh_3d.c.
   */
  EL *mst_el;
  int old_ov     = mst_info_old->opp_vertex;
  int old_orient = mst_info_old->orientation;
  int old_type   = mst_info_old->el_type;
  int mst_ichild, mst_ov, mst_orient, mst_type;
    

  DEBUG_TEST_EXIT(old_ov == 2 || old_ov == 3,
		  "Filling of master info is garbled.\n");
    
  /* First step: copy the connectivity definiton defined in submesh_3d.c */
  if ((old_ov == 2 && (old_orient > 0)) ||
      (old_ov == 3 && (old_orient < 0))) {
    mst_ichild = ichild;
  } else {
    mst_ichild = 1 - ichild;
  }    
  mst_el     = mst_info_old->el->child[mst_ichild];
  mst_ov     = child_face_3d[old_type][old_ov][mst_ichild];
  mst_orient = old_orient*child_orientation_3d[old_type][mst_ichild];
  mst_type   = (old_type + 1) % 3;

  /* mst_ov has to be 1 or 2 at this stage */
  DEBUG_TEST_EXIT(mst_ov == 1 || mst_ov == 2,
		  "Filling of master info is garbled.\n");

  if (mst_el->child[0] && mst_ov == 1) {
    /* This means the master element has children, but its
     * refinement edge is not adjacent with our element, so we need
     * to traverse down one level.
     *
     * This is luckily rather simple (compare with the subsimplex !=
     * 2,3 case in join_elements_recursive_3d() in submesh_3d.c):
     */
    if (fill_flags & FILL_COORDS) {
      if (mst_el->new_coord) {
	COPY_DOW(mst_el->new_coord, mst_info->opp_coord);
      } else {
	/* The master vertex contained in this element must have
	 * number 0, because the opposite vertex has number 1.
	 */
	AXPBY_DOW(0.5, mst_info_old->opp_coord,
		  0.5, el_info->coord[
		    slave_numbering_3d[!!mst_type][mst_orient < 0][mst_ov][0]
		    ],
		  mst_info->opp_coord);
      }
    }

    mst_ichild = 0;
    mst_el     = mst_el->child[mst_ichild];
    mst_ov     = 3;
    mst_orient = mst_orient*child_orientation_3d[mst_type][mst_ichild];
    mst_type   = (mst_type + 1) % 3;
  } else if (fill_flags & FILL_COORDS) {
    COPY_DOW(mst_info_old->opp_coord, mst_info->opp_coord);
  }
    
  mst_info->el          = mst_el;
  mst_info->opp_vertex  = mst_ov;
  mst_info->orientation = mst_orient;
  mst_info->el_type     = mst_type;
}
#endif
