/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     element_1d.c                                                   */
/*                                                                          */
/*                                                                          */
/* description:  routines on elements that depend on the dimension in 1d    */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Universitaet Freiburg                                        */
/*             Hermann-Herder-Str. 10                                       */
/*             79104 Freiburg, Germany                                      */
/*                                                                          */
/*  http://www.alberta-fem.de                                               */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003),                         */
/*         C.-J. Heine (2006).                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
# include "config.h" /* probably a good idea to pull this one in here ... */
#endif

#include "alberta.h"

/****************************************************************************/
/*  world_to_coord_1d():  return -1 if inside, otherwise index of lambda<0  */
/****************************************************************************/

int world_to_coord_1d(const EL_INFO *el_info,
		      const REAL *x, REAL_B lambda)
{
  FUNCNAME("world_to_coord_1d");
  REAL lmin;
  int index;

#if (DIM_OF_WORLD == 1)
  {
    REAL a = el_info->coord[0][0];
    REAL length = (el_info->coord[1][0] - a);
    REAL det = ABS(length);
    
    DEBUG_TEST_EXIT((el_info->fill_flag & FILL_COORDS) ||
		    !el_info->mesh->parametric ||
		    el_info->mesh->parametric->use_reference_mesh,
		    "You must enable the use_reference_mesh entry in the "
		    "PARAMETRIC structure to use this function on the "
		    "reference mesh. Use parametric->coord_to_world() "
		    "to access the parametric mesh\n");
    
    if (det < 1.E-20) {
      ERROR_EXIT("length = %le; abort\n", length);
      return(-2);
    }
    
    lambda[1] = (x[0]-a)/length;
    lambda[0] = 1.0 - lambda[1];
    
    index = -1;
    lmin = 0.0;
    for (int i = 0; i <= 1; i++) {
      if ((lambda[i]*det) < -1.E-15) {
	if (lambda[i] < lmin) {
	  index = i;
	  lmin = lambda[i];
	}
      }
    }
  }
#else
  {
    REAL_D p0, p1, tmp;
    REAL_D e;
    REAL   length, scal;

    for(int i=0; i<DIM_OF_WORLD; i++) {
      p0[i]    = el_info->coord[0][i];
      p1[i]    = el_info->coord[1][i];
    }

    length = DIST_DOW(p1, p0);

    if (length < 1.E-20) {
      ERROR_EXIT("length = %le; abort\n", length);
      return(-2);
    }

// project x onto the line through p0 and p1
// e      = p1-p0 / |p1-p0|
// p=p(x) = p0 + [(x-p0) dot e ] e
// =>
// p = lambda0 p0 + lambda1 p1, lambda0 = 1-[(x-p0) dot e]/|p1-p0|
//                              lambda1 =   [(x-p0) dot e]/|p1-p0|

    AXPBY_DOW(1/length, p1, -1/length, p0, e);
    AXPBY_DOW(1, x, -1, p0, tmp);
    scal   = SCP_DOW(tmp, e);
//    AXPBY_DOW(1, p0, scal, e, p);

// calc baryz coord
    lambda[1] = scal/length;
    lambda[0] = 1.0 - lambda[1];
    
    index = -1;
    lmin = 0.0;
    for (int i = 0; i <= 1; i++) {
      if ((lambda[i]*length) < -1.E-15) {
	if (lambda[i] < lmin) {
	  index = i;
	  lmin = lambda[i];
	}
      }
    }
  }
#endif
  return index;
}

/****************************************************************************/
/*  transform local coordintes l to world coordinates; if w is non NULL     */
/*  store them at w otherwise return a pointer to some local static         */
/*  area containing world coordintes                                        */
/****************************************************************************/

const REAL *coord_to_world_1d(const EL_INFO *el_info, const REAL_B l, REAL_D w)
{
  FUNCNAME("coord_to_world_1d");
  static REAL_D world;

  DEBUG_TEST_EXIT((el_info->fill_flag & FILL_COORDS) ||
		  !el_info->mesh->parametric ||
		  el_info->mesh->parametric->use_reference_mesh,
		  "You must enable the use_reference_mesh entry in the "
		  "PARAMETRIC structure to use this function on the "
		  "reference mesh. Use parametric->coord_to_world() "
		  "to access the parametric mesh\n");

  if (!w) {
    w = world;
  }

  AXPBY_DOW(l[0], el_info->coord[0], l[1], el_info->coord[1], w);

  return (const REAL *)w;
}

/*--------------------------------------------------------------------------*/
/*  compute volume of an element                                            */
/*--------------------------------------------------------------------------*/

REAL el_det_1d(const EL_INFO *el_info)
{
  FUNCNAME("el_det_1");

  DEBUG_TEST_EXIT((el_info->fill_flag & FILL_COORDS) ||
		  !el_info->mesh->parametric ||
		  el_info->mesh->parametric->use_reference_mesh,
		  "You must enable the use_reference_mesh entry in the "
		  "PARAMETRIC structure to use this function on the "
		  "reference mesh. Use parametric->coord_to_world() "
		  "to access the parametric mesh\n");

  return DIST_DOW(el_info->coord[0], el_info->coord[1]);
}

REAL el_volume_1d(const EL_INFO *el_info)
{
  return el_det_1d(el_info);
}

/*--------------------------------------------------------------------------*/
/*  compute gradients of barycentric coords on element; return the absolute */
/*  value of the determinant of the Jacobian of the transformation to the   */
/*  reference element                                                       */
/*  Notice: for dim < DIM_OF_WORLD grd_lam will contain the tangential      */
/*  derivative of the barycentric coordinates!                              */
/*--------------------------------------------------------------------------*/

REAL el_grd_lambda_1d(const EL_INFO *el_info, REAL_BD grd_lam)
{
  FUNCNAME("el_grd_lambda_1d");
  REAL   det2;
  int i;

  DEBUG_TEST_EXIT((el_info->fill_flag & FILL_COORDS) ||
		  !el_info->mesh->parametric ||
		  el_info->mesh->parametric->use_reference_mesh,
		  "You must enable the use_reference_mesh entry in the "
		  "PARAMETRIC structure to use this function on the "
		  "reference mesh. Use parametric->coord_to_world() "
		  "to access the parametric mesh\n");

  det2  = DST2_DOW(el_info->coord[0], el_info->coord[1]);

  for(i = 0; i < DIM_OF_WORLD; i++) {
    grd_lam[0][i] = (el_info->coord[0][i] - el_info->coord[1][i]) / det2;
    grd_lam[1][i] = -grd_lam[0][i];
  }

  /* Note: we have to clear any excess entries to 0, otherwise
   * eval_grd_uh_fast() and friends will not work.
   */
  for (i = N_LAMBDA_2D; i < N_LAMBDA_MAX; i++) {
    SET_DOW(0.0, grd_lam[i]);
  }

  return sqrt(det2);
}


/*---8<---------------------------------------------------------------------*/
/*---  wall normal in 1d is really rather easy....                       ---*/
/*--------------------------------------------------------------------->8---*/

REAL get_wall_normal_1d(const EL_INFO *el_info, int wall, REAL *normal)
{
  if (normal) {
#if DIM_OF_WORLD == 1
    normal[0] = wall ? -1.0 : 1.0;
#else
    int    n;
    REAL   det;
    const REAL_D *coord = el_info->coord;

    DEBUG_TEST_EXIT((el_info->fill_flag & FILL_COORDS) ||
		    !el_info->mesh->parametric ||
		    el_info->mesh->parametric->use_reference_mesh,
		    "You must enable the use_reference_mesh entry in the "
		    "PARAMETRIC structure to use this function on the "
		    "reference mesh. Use parametric->coord_to_world() "
		    "to access the parametric mesh\n");
    
    for (n = 0; n < DIM_OF_WORLD; n++)
      normal[n] =
      wall == 1 ? coord[0][n] - coord[1][n] : coord[1][n] - coord[0][n];
    
    det = NORM_DOW(normal);
    for (n = 0; n < DIM_OF_WORLD; n++)
      normal[n] /= det;
#endif
  }
  return 1.0;
}

/*--------------------------------------------------------------------------*/
/* orient the vertices of walls (i.e. codim 1 subsimplexes)                 */
/* used by estimator for the jumps => same quadrature nodes from both sides!*/
/*--------------------------------------------------------------------------*/

const int sorted_wall_vertices_1d[N_WALLS_1D][DIM_FAC_1D][N_VERTICES_0D] = {
  {{1}}, {{0}}
};

int wall_rel_orientation_1d(const EL *el, const EL *neigh, int wall, int ov)
{
  return 0;
}

int wall_orientation_1d(const EL *el, int wall)
{
  return 0;
}
