/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     traverse_nr_3d.c                                               */
/*                                                                          */
/* description:                                                             */
/*           nonrecursive mesh traversal, 3d routines                       */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/* to_do:                                                                   */
/*   use memcopy() for stack copy in traverse_neighbour()		    */
/*                                                      		    */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/*   Refinement/Coarsening vertex numbering             		    */
/*   like Baensch / Kossaczky                                               */
/*--------------------------------------------------------------------------*/

/* father.neigh[coarse_nb[father.typ][i][j]] == child[i-1].neigh[j] */

static int coarse_nb_3d[3][3][4] = {{{-2,-2,-2,-2}, {-1,2,3,1}, {-1,3,2,0}},
				    {{-2,-2,-2,-2}, {-1,2,3,1}, {-1,2,3,0}},
				    {{-2,-2,-2,-2}, {-1,2,3,1}, {-1,2,3,0}}};


/*--------------------------------------------------------------------------*/
/*   traverse_neighbour_3d:                           		            */
/*   -------------------                                		    */
/*   walk through hierarchy tree and look for a neighbour		    */
/*--------------------------------------------------------------------------*/


static const EL_INFO *traverse_neighbour_3d(TRAVERSE_STACK *stack,
					    const EL_INFO *elinfo_old,
					    int neighbour)
{
  FUNCNAME("traverse_neighbour_3d");
  EL      *el, *el2;
  EL_INFO *old_elinfo, *elinfo, *elinfo2;
  DOF    *dof;
  int     i, nb, opp_vertex, stack2_used, typ;
  int     sav_index, sav_neighbour = neighbour;

  DEBUG_TEST_EXIT(stack->stack_used > 0, "no current element\n");

/*
  TEST_EXIT(traverse_fill_flag & CALL_LEAF_EL,
    "invalid traverse_fill_flag=%d\n", traverse_fill_flag);
*/

  DEBUG_TEST_EXIT(elinfo_old == stack->elinfo_stack+stack->stack_used,
	      "invalid old elinfo\n");

  DEBUG_TEST_FLAG(FILL_NEIGH, (stack->elinfo_stack+stack->stack_used));
  el = stack->elinfo_stack[stack->stack_used].el;
  sav_index = INDEX(el);

  /* first, goto to leaf level, if necessary... */
  if (stack->traverse_flags & CALL_LEAF_EL) {
    if ((el->child[0]) && (neighbour < 2)) {
      if (stack->stack_used >= stack->stack_size-1)
	__AI_enlarge_traverse_stack(stack);
      i = 1 - neighbour;
      fill_elinfo(i, stack->fill_flag,
		  stack->elinfo_stack+stack->stack_used,
		  stack->elinfo_stack+stack->stack_used+1);
      stack->info_stack[stack->stack_used] = i+1;
      stack->stack_used++;
      stack->info_stack[stack->stack_used] = 0;
      neighbour = 3;
    }
  }


  /* save information about current element and its position in the tree */
  stack->save_traverse_mel = stack->traverse_mel;
  stack->save_stack_used   = stack->stack_used;

  nb = neighbour;

  while (stack->stack_used > 1)  /* go up in tree until we can go down again */
  {
    stack->stack_used--;
    typ = (stack->elinfo_stack+stack->stack_used)->el_type;
    nb = coarse_nb_3d[typ][stack->info_stack[stack->stack_used]][nb];
    if (nb == -1) break;
    DEBUG_TEST_EXIT(nb >= 0, "invalid coarse_nb_3d %d\n",nb);
  }

  /* save hierarchy information about current element */

  for (i=stack->stack_used; i<=stack->save_stack_used; i++) {
    stack->save_info_stack[i]   = stack->info_stack[i];
    stack->save_elinfo_stack[i] = stack->elinfo_stack[i];
  }

  old_elinfo = stack->save_elinfo_stack+stack->save_stack_used;
  opp_vertex = old_elinfo->opp_vertex[neighbour];

  if (nb >= 0) {                           /* go to macro element neighbour */

    i = stack->traverse_mel->opp_vertex[nb];
    stack->traverse_mel = stack->traverse_mel->neigh[nb];
    if (stack->traverse_mel == NULL)  return NULL;
    
    if ((nb < 2) && (stack->save_stack_used > 1)) {
      stack2_used = 2;                /* go down one level in OLD hierarchy */
    }
    else {
      stack2_used = 1;
    }
    elinfo2 = stack->save_elinfo_stack+stack2_used;
    el2 = elinfo2->el;

    stack->stack_used = 1;
    fill_macro_info(stack->traverse_mesh, stack->traverse_mel,
		    stack->elinfo_stack+stack->stack_used);
    stack->info_stack[stack->stack_used] = 0;
    nb = i;
  }
  else {                                                /* goto other child */

    stack2_used = stack->stack_used + 1;
    if (stack->save_stack_used > stack2_used) {
      stack2_used++;                  /* go down one level in OLD hierarchy */
    }
    elinfo2 = stack->save_elinfo_stack+stack2_used;
    el2 = elinfo2->el;

    if (stack->stack_used >= stack->stack_size-1)
      __AI_enlarge_traverse_stack(stack);
    i = 2 - stack->info_stack[stack->stack_used];
    stack->info_stack[stack->stack_used] = i+1;
    fill_elinfo(i, stack->fill_flag,
		stack->elinfo_stack+stack->stack_used,
		stack->elinfo_stack+stack->stack_used+1);
    stack->stack_used++;
    stack->info_stack[stack->stack_used] = 0;
    nb = 0;

  }


  elinfo = stack->elinfo_stack+stack->stack_used;
  el = elinfo->el;

  while(el->child[0]) {
 
    if (nb < 2) {                         /* go down one level in hierarchy */
      if (stack->stack_used >= stack->stack_size-1)
	__AI_enlarge_traverse_stack(stack);
      stack->info_stack[stack->stack_used] = 2-nb;
      fill_elinfo(1-nb, stack->fill_flag,
		  stack->elinfo_stack+stack->stack_used,
		  stack->elinfo_stack+stack->stack_used+1);
      stack->stack_used++;
      stack->info_stack[stack->stack_used] = 0;
      elinfo = stack->elinfo_stack+stack->stack_used;
      el = elinfo->el;
      nb = 3;
    }

    if (stack->save_stack_used > stack2_used) { /* `refine' both el and el2 */
      DEBUG_TEST_EXIT(el->child[0], "invalid NEW refinement?\n");

      if (el->dof[0][0] == el2->dof[0][0])
	i = stack->save_info_stack[stack2_used] - 1;
      else if (el->dof[1][0] == el2->dof[0][0])
	i = 2 - stack->save_info_stack[stack2_used];
      else
	ERROR_EXIT("no common refinement edge");

      if (el->child[0]  &&  el->child[i]->dof[1][0] == el->dof[nb][0]) 
      {   /*  el->child[0]  Kuni  22.08.96  */
	nb = 1;
      }
      else {
	nb = 2;
      }

      stack->info_stack[stack->stack_used] = i+1;
      if (stack->stack_used >= stack->stack_size-1)
	__AI_enlarge_traverse_stack(stack);
      fill_elinfo(i, stack->fill_flag,
		  stack->elinfo_stack+stack->stack_used,
		  stack->elinfo_stack+stack->stack_used+1);
      stack->stack_used++;
      stack->info_stack[stack->stack_used] = 0;

      elinfo = stack->elinfo_stack+stack->stack_used;
      el = elinfo->el;

      stack2_used++;
      elinfo2 = stack->save_elinfo_stack+stack2_used;
      el2 = elinfo2->el;
      if (stack->save_stack_used > stack2_used) {
	dof = el2->dof[1];
	if (dof[0] != el->dof[1][0] && dof[0] != el->dof[2][0]) {
	  stack2_used++;               /* go down one level in OLD hierarchy */
	  elinfo2 = stack->save_elinfo_stack+stack2_used;
	  el2 = elinfo2->el;
	}
      }

    }
    else {   /* now we're done... */
      elinfo = stack->elinfo_stack+stack->stack_used;
      el = elinfo->el;
      break;
    }
  }


  if (elinfo->neigh[opp_vertex] != old_elinfo->el) {
    MSG(" looking for neighbour %d of element %d at %p\n",
	neighbour, INDEX(old_elinfo->el), old_elinfo->el);
    MSG(" originally: neighbour %d of element %d at %p\n",
	sav_neighbour, sav_index, old_elinfo->el);
    MSG(" got element %d at %p with opp_vertex %d neigh %d\n",
	INDEX(elinfo->el), elinfo->el,
	opp_vertex, INDEX(elinfo->neigh[opp_vertex]));
    DEBUG_TEST_EXIT(elinfo->neigh[opp_vertex] == old_elinfo->el,
		"didn't succeed !?!?!?\n");
  }


  if (stack->traverse_flags & CALL_EVERY_EL_POSTORDER)
    stack->info_stack[stack->stack_used] = 3;
  else if (stack->traverse_flags & CALL_EVERY_EL_INORDER)
    stack->info_stack[stack->stack_used] = 1;  /* ??? */

  elinfo->el_geom_cache.current_el = NULL;
  
  return elinfo;
}


/*--------------------------------------------------------------------------*/
/*   AI_update_elinfo_stack_3d:                       		            */
/*   --------------------                                 		    */
/*   update EL_INFO structures after refinement              		    */
/*--------------------------------------------------------------------------*/

void AI_update_elinfo_stack_3d(TRAVERSE_STACK *stack)
{
  int      i;

  for (i = stack->stack_used; i > 0; i--) {
    AI_update_elinfo_3d(stack->elinfo_stack+i);
  }
  return;
}
