/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     traverse_nr_2d.c                                               */
/*                                                                          */
/* description:                                                             */
/*           nonrecursive mesh traversal, 2d 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)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

static int coarse_nb_2d[3][3] = {{-2,-2,-2}, {2,-1,1}, {-1,2,0}};
                 /* father.neigh[coarse_nb_2d[i][j]] == child[i-1].neigh[j] */

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


static const EL_INFO *traverse_neighbour_2d(TRAVERSE_STACK *stack,
					    const EL_INFO *elinfo_old,
					    int neighbour)
{
  FUNCNAME("traverse_neighbour_2d");
  EL      *el, *sav_el;
  EL_INFO *old_elinfo, *elinfo;
  int     i, nb, opp_vertex, stack2_used;
  int     sav_index, sav_neighbour = neighbour;
  int     parity = 0;

  DEBUG_TEST_EXIT(stack->stack_used > 0, "no current element");
  DEBUG_TEST_EXIT(stack->traverse_flags & CALL_LEAF_EL,
    "invalid traverse_fill_flag=%d", stack->traverse_flags);
  DEBUG_TEST_EXIT(elinfo_old == stack->elinfo_stack+stack->stack_used,
    "invalid old elinfo");
  DEBUG_TEST_FLAG(FILL_NEIGH, stack->elinfo_stack+stack->stack_used);

  el = stack->elinfo_stack[stack->stack_used].el;
  sav_index = INDEX(el);
  sav_el    = el;

  /* first, goto to leaf level, if necessary... */
  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++;
    neighbour = 2;
  }

  /* 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;
  for (i=0; i<=stack->stack_used; i++)
    stack->save_info_stack[i]   = stack->info_stack[i];
  for (i=0; i<=stack->stack_used; i++)
    stack->save_elinfo_stack[i] = stack->elinfo_stack[i];
  old_elinfo = stack->save_elinfo_stack+stack->stack_used;
  opp_vertex = old_elinfo->opp_vertex[neighbour];

/*--------------------------------------------------------------------------*/
/* First phase: go up in tree until we can go down again.                   */
/*                                                                          */
/* During this first phase, nb is the neighbour index which points from an  */
/* element of the OLD hierarchy branch to the NEW branch                    */
/*--------------------------------------------------------------------------*/

  nb = neighbour;

  while (stack->stack_used > 1)
  {
    stack->stack_used--;
    nb = coarse_nb_2d[stack->info_stack[stack->stack_used]][nb];
    if (nb == -1) break;
    DEBUG_TEST_EXIT(nb >= 0, "invalid coarse_nb_2d %d\n",nb);
  }

/*--------------------------------------------------------------------------*/
/* Now, goto neighbouring element at the local hierarchy entry              */
/* This is either a macro element neighbour or the other child of parent.   */
/* initialize nb for second phase (see below)                               */
/*--------------------------------------------------------------------------*/

  if (nb >= 0) {                        /* go to macro element neighbour */
    const MACRO_EL *mel_old;

    if (stack->traverse_mel->neigh[nb] == 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;
    }

    mel_old = stack->traverse_mel;
    i = mel_old->opp_vertex[nb];
    stack->traverse_mel = stack->traverse_mel->neigh[nb];

    if (mel_old->neigh_vertices[nb][0] != -1) {
      if (mel_old->neigh_vertices[nb][0] == ((i+1) % N_VERTICES_2D)) {
	parity = 1;
      }
    } else if (mel_old->coord[(nb + 1) % N_VERTICES_2D]
	       ==
	       stack->traverse_mel->coord[(i+1) % N_VERTICES_2D]) {
      /* neighbour has different orientation */
      parity = 1;
    }
	
    nb = i;
    
    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;
    
  } 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++;
    nb = 1-i;
  }

/*--------------------------------------------------------------------------*/
/* Second phase: go down in a new hierarchy branch until leaf level.        */
/* Now, nb is the neighbour index which points from an element of the       */
/* NEW hierarchy branch to the OLD branch.                                  */
/*--------------------------------------------------------------------------*/

  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);
      fill_elinfo(1-nb, stack->fill_flag,
		  stack->elinfo_stack+stack->stack_used, 
		  stack->elinfo_stack+stack->stack_used+1);
      stack->info_stack[stack->stack_used] = 2-nb;
      stack->stack_used++;
      nb = 2;
    }

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

                              /* use child i, neighbour of el2->child[nb-1] */
      i = 2 - stack->save_info_stack[stack2_used];
      DEBUG_TEST_EXIT(i < 2, "invalid OLD refinement?");
      if (parity)
	i = 1 - i;
      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++;
      nb = i;

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

      stack2_used++;
      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; */

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


  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, sav_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 !?!?!?");
  }
  if (elinfo->el->child[0])
  {
    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, sav_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]));
    MSG("got no leaf element\n");
    WAIT_REALLY;
  }

  elinfo->el_geom_cache.current_el = NULL;

  return elinfo;
}

