#ifndef FINDPTFUNCS_H
#define FINDPTFUNCS_H

#include "float.h"
#include "math.h"

/*======================================================
/ from types.h
/======================================================
*/

#define MOAB_POLY_EPS (128*DBL_EPSILON)
#define MOAB_POLY_PI  3.1415926535897932384626433832795028841971693993751058209749445923

#define mbsqrt sqrt
#define mbabs fabs
#define mbcos cos
#define mbsin sin
#define mbfloor floor
#define mbceil ceil

#ifdef INTEGER
#undef INTEGER
#endif

#ifdef real
#undef real
#endif

/* integer type to use for everything */
#if   defined(USE_LONG)
#  define INTEGER long
#elif defined(USE_LONG_LONG)
#  define INTEGER long long
#elif defined(USE_SHORT)
#  define INTEGER short
#else
#  define INTEGER int
#endif

/* when defined, use the given type for global indices instead of INTEGER */
#if   defined(USE_GLOBAL_LONG_LONG)
#  define GLOBAL_INT long long
#elif defined(USE_GLOBAL_LONG)
#  define GLOBAL_INT long
#else
#  define GLOBAL_INT long
#endif

/* floating point type to use for everything */
#if   defined(USE_FLOAT)
   typedef float realType;
#elif defined(USE_LONG_DOUBLE)
   typedef long double realType;
#else
   typedef double realType;
#endif

/* apparently uint and ulong can be defined already in standard headers */
#ifndef uint
typedef   signed INTEGER sint;
#endif

#ifndef uint
typedef unsigned INTEGER uint;
#endif
#undef INTEGER

#ifndef slong
#ifdef GLOBAL_INT
  typedef   signed GLOBAL_INT slong;
#else
  typedef sint slong;
#endif
#endif

#ifndef ulong
#ifdef GLOBAL_INT
  typedef unsigned GLOBAL_INT ulong;
#else
  typedef uint ulong;
#endif
#endif

/*======================================================
/ from poly.h
/======================================================
*/

/* 
  For brevity's sake, some names have been shortened
  Quadrature rules
    Gauss   -> Gauss-Legendre quadrature (open)
    Lobatto -> Gauss-Lobatto-Legendre quadrature (closed at both ends)
  Polynomial bases
    Legendre -> Legendre basis
    Gauss    -> Lagrangian basis using Gauss   quadrature nodes
    Lobatto  -> Lagrangian basis using Lobatto quadrature nodes
*/

/*--------------------------------------------------------------------------
   Legendre Polynomial Matrix Computation
   (compute P_i(x_j) for i = 0, ..., n and a given set of x)
  --------------------------------------------------------------------------*/

/* precondition: n >= 1
   inner index is x index (0 ... m-1);
   outer index is Legendre polynomial number (0 ... n)
 */
void legendre_matrix(const realType *x, int m, realType *P, int n);

/* precondition: n >= 1
   inner index is Legendre polynomial number (0 ... n)
   outer index is x index (0 ... m-1);
 */
void legendre_matrix_t(const realType *x, int m, realType *P, int n);

/* precondition: n >= 1
   compute P_i(x) with i = 0 ... n
 */
void legendre_row(realType x, realType *P, int n);


/*--------------------------------------------------------------------------
   Quadrature Nodes and Weights Calculation
   
   call the _nodes function before calling the _weights function
  --------------------------------------------------------------------------*/

void gauss_nodes(realType *z, int n);   /* n nodes (order = 2n-1) */
void lobatto_nodes(realType *z, int n); /* n nodes (order = 2n-3) */

void gauss_weights(const realType *z, realType *w, int n);
void lobatto_weights(const realType *z, realType *w, int n);

/*--------------------------------------------------------------------------
   Lagrangian to Legendre Change-of-basis Matrix
  --------------------------------------------------------------------------*/

/* precondition: n >= 2
   given the Gauss quadrature rule (z,w,n), compute the square matrix J
   for transforming from the Gauss basis to the Legendre basis:
   
      u_legendre(i) = sum_j J(i,j) u_gauss(j)

   computes J   = .5 (2i+1) w  P (z )
             ij              j  i  j
             
   in column major format (inner index is i, the Legendre index)
 */
void gauss_to_legendre(const realType *z, const realType *w, int n, realType *J);

/* precondition: n >= 2
   same as above, but
   in row major format (inner index is j, the Gauss index)
 */
void gauss_to_legendre_t(const realType *z, const realType *w, int n, realType *J);

/* precondition: n >= 3
   given the Lobatto quadrature rule (z,w,n), compute the square matrix J
   for transforming from the Lobatto basis to the Legendre basis:
   
      u_legendre(i) = sum_j J(i,j) u_lobatto(j)

   in column major format (inner index is i, the Legendre index)
 */
void lobatto_to_legendre(const realType *z, const realType *w, int n, realType *J);

/*--------------------------------------------------------------------------
   Lagrangian basis function evaluation
  --------------------------------------------------------------------------*/

/* given the Lagrangian nodes (z,n) and evaluation points (x,m)
   evaluate all Lagrangian basis functions at all points x
   
   inner index of output J is the basis function index (row-major format)
   provide work array with space for 4*n doubles
 */
void lagrange_weights(const realType *z, unsigned n,
                      const realType *x, unsigned m,
                      realType *J, realType *work);

/* given the Lagrangian nodes (z,n) and evaluation points (x,m)
   evaluate all Lagrangian basis functions and their derivatives
   
   inner index of outputs J,D is the basis function index (row-major format)
   provide work array with space for 6*n doubles
 */
void lagrange_weights_deriv(const realType *z, unsigned n,
                            const realType *x, unsigned m,
                            realType *J, realType *D, realType *work);

/*--------------------------------------------------------------------------
   Speedy Lagrangian Interpolation
   
   Usage:
   
     lagrange_data p;
     lagrange_setup(&p,z,n);    *  setup for nodes z[0 ... n-1] *
     
     the weights
       p->J [0 ... n-1]     interpolation weights
       p->D [0 ... n-1]     1st derivative weights
       p->D2[0 ... n-1]     2nd derivative weights
     are computed for a given x with:
       lagrange_0(p,x);  *  compute p->J *
       lagrange_1(p,x);  *  compute p->J, p->D *
       lagrange_2(p,x);  *  compute p->J, p->D, p->D2 *
       lagrange_2u(p);   *  compute p->D2 after call of lagrange_1(p,x); *
     These functions use the z array supplied to setup
       (that pointer should not be freed between calls)
     Weights for x=z[0] and x=z[n-1] are computed during setup; access as:
       p->J_z0, etc. and p->J_zn, etc.

     lagrange_free(&p);  *  deallocate memory allocated by setup *
  --------------------------------------------------------------------------*/

typedef struct {
  unsigned n;                /* number of Lagrange nodes            */
  const realType *z;             /* Lagrange nodes (user-supplied)      */
  realType *J, *D, *D2;          /* weights for 0th,1st,2nd derivatives */
  realType *J_z0, *D_z0, *D2_z0; /* ditto at z[0]   (computed at setup) */
  realType *J_zn, *D_zn, *D2_zn; /* ditto at z[n-1] (computed at setup) */
  realType *w, *d, *u0, *v0, *u1, *v1, *u2, *v2; /* work data           */
} lagrange_data;

void lagrange_setup(lagrange_data *p, const realType *z, unsigned n);
void lagrange_free(lagrange_data *p);

void lagrange_0(lagrange_data *p, realType x) ;
void lagrange_1(lagrange_data *p, realType x) ;
void lagrange_2(lagrange_data *p, realType x) ;
void lagrange_2u(lagrange_data *p) ;

/*======================================================
/ from tensor.h
/======================================================
*/

/*--------------------------------------------------------------------------
   1-,2-,3-d Tensor Application
   
   the 3d case:
   tensor_f3(R,mr,nr, S,ms,ns, T,mt,nt, u,v, work1,work2)
     gives v = [ R (x) S (x) T ] u
     where R is mr x nr, S is ms x ns, T is mt x nt,
       each in row- or column-major format according to f := r | c
     u is nr x ns x nt in column-major format (inner index is r)
     v is mr x ms x mt in column-major format (inner index is r)
  --------------------------------------------------------------------------*/

void tensor_c1(const realType *R, unsigned mr, unsigned nr, 
               const realType *u, realType *v);
void tensor_r1(const realType *R, unsigned mr, unsigned nr, 
               const realType *u, realType *v);

/* work holds mr*ns realTypes */
void tensor_c2(const realType *R, unsigned mr, unsigned nr,
               const realType *S, unsigned ms, unsigned ns,
               const realType *u, realType *v, realType *work);
void tensor_r2(const realType *R, unsigned mr, unsigned nr,
               const realType *S, unsigned ms, unsigned ns,
               const realType *u, realType *v, realType *work);

/* work1 holds mr*ns*nt realTypes,
   work2 holds mr*ms*nt realTypes */
void tensor_c3(const realType *R, unsigned mr, unsigned nr,
               const realType *S, unsigned ms, unsigned ns,
               const realType *T, unsigned mt, unsigned nt,
               const realType *u, realType *v, realType *work1, realType *work2);
void tensor_r3(const realType *R, unsigned mr, unsigned nr,
               const realType *S, unsigned ms, unsigned ns,
               const realType *T, unsigned mt, unsigned nt,
               const realType *u, realType *v, realType *work1, realType *work2);

/*--------------------------------------------------------------------------
   1-,2-,3-d Tensor Application of Row Vectors (for Interpolation)
   
   the 3d case:
   v = tensor_i3(Jr,nr, Js,ns, Jt,nt, u, work)
   same effect as tensor_r3(Jr,1,nr, Js,1,ns, Jt,1,nt, u,&v, work1,work2):
     gives v = [ Jr (x) Js (x) Jt ] u
     where Jr, Js, Jt are row vectors (interpolation weights)
     u is nr x ns x nt in column-major format (inner index is r)
     v is a scalar
  --------------------------------------------------------------------------*/

realType tensor_i1(const realType *Jr, unsigned nr, const realType *u);

/* work holds ns realTypes */
realType tensor_i2(const realType *Jr, unsigned nr,
               const realType *Js, unsigned ns,
               const realType *u, realType *work);

/* work holds ns*nt + nt realTypes */
realType tensor_i3(const realType *Jr, unsigned nr,
               const realType *Js, unsigned ns,
               const realType *Jt, unsigned nt,
               const realType *u, realType *work);

/*--------------------------------------------------------------------------
   1-,2-,3-d Tensor Application of Row Vectors
             for simultaneous Interpolation and Gradient computation
   
   the 3d case:
   v = tensor_ig3(Jr,Dr,nr, Js,Ds,ns, Jt,Dt,nt, u,g, work)
     gives v   = [ Jr (x) Js (x) Jt ] u
           g_0 = [ Dr (x) Js (x) Jt ] u
           g_1 = [ Jr (x) Ds (x) Jt ] u
           g_2 = [ Jr (x) Js (x) Dt ] u
     where Jr,Dr,Js,Ds,Jt,Dt are row vectors
       (interpolation & derivative weights)
     u is nr x ns x nt in column-major format (inner index is r)
     v is a scalar, g is an array of 3 realTypes
  --------------------------------------------------------------------------*/

realType tensor_ig1(const realType *Jr, const realType *Dr, unsigned nr,
                const realType *u, realType *g);

/* work holds 2*ns realTypes */
realType tensor_ig2(const realType *Jr, const realType *Dr, unsigned nr,
                const realType *Js, const realType *Ds, unsigned ns,
                const realType *u, realType *g, realType *work);

/* work holds 2*ns*nt + 3*ns realTypes */
realType tensor_ig3(const realType *Jr, const realType *Dr, unsigned nr,
                const realType *Js, const realType *Ds, unsigned ns,
                const realType *Jt, const realType *Dt, unsigned nt,
                const realType *u, realType *g, realType *work);

/*======================================================
/ from findpt.h
/======================================================
*/

typedef struct {
  realType x[2], A[4], axis_bnd[4];
} obbox_2;

typedef struct {
  realType x[3], A[9], axis_bnd[6];
} obbox_3;

typedef struct {
  unsigned hash_n;
  realType bnd[4]; /* bounds for all elements */
  realType fac[2]; /* fac[i] = hash_n / (bnd[2*i+1]-bnd[2*i]) */
  obbox_2 *obb; /* obb[nel] -- bounding box info for each element */
  uint *offset; /* hash table -- for cell i,j:
                         uint index = j*hash_n+i,
                                  b = offset[index  ],
                                  e = offset[index+1];
                         elements in cell are
                           offset[b], offset[b+1], ..., offset[e-1] */
  unsigned max; /* maximum # of elements in any cell */
} hash_data_2;

typedef struct {
  unsigned hash_n;
  realType bnd[6]; /* bounds for all elements */
  realType fac[3]; /* fac[i] = hash_n / (bnd[2*i+1]-bnd[2*i]) */
  obbox_3 *obb; /* obb[nel] -- bounding box info for each element */
  uint *offset; /* hash table -- for cell i,j,k:
                         uint index = (k*hash_n+j)*hash_n+i,
                                  b = offset[index  ],
                                  e = offset[index+1];
                         elements in cell are
                           offset[b], offset[b+1], ..., offset[e-1] */
  unsigned max; /* maximum # of elements in any cell */
} hash_data_3;

typedef struct {
  uint el;
  realType r[3];
  realType dist;
} findpt_listel;

typedef struct {
  unsigned constraints;
  unsigned de, d1;
  realType *x[2], *fd1[2];
} opt_edge_data_2;

typedef struct {
  unsigned constraints;
  realType x[2], jac[4];
} opt_point_data_2;

typedef struct {
  lagrange_data *ld;
  unsigned size[3];
  const realType *elx[2];
  opt_edge_data_2 ed;
  opt_point_data_2 pd;
  realType *work;
  realType x[2], jac[4];
} opt_data_2;

typedef struct {
  const realType *xw[2];   /* geometry data */
  realType *z[2];          /* lobatto nodes */
  lagrange_data ld[2]; /* interpolation, derivative weights & data */
  unsigned nptel;      /* nodes per element */
  hash_data_2 *hash;   /* geometric hashing data */
  findpt_listel *list, **sorted, **end;
                                        /* pre-allocated list of elements to
                                           check (found by hashing), and
                                           pre-allocated list of pointers into
                                           the first list for sorting */
  opt_data_2 *od; /* data for the optimization algorithm */
  realType *od_work;
} findpt_data_2;

typedef struct {
  unsigned constraints;
  unsigned dn, d1, d2;
  realType *x[3], *fdn[3];
} opt_face_data_3;

typedef struct {
  unsigned constraints;
  unsigned de, d1, d2;
  realType *x[3], *fd1[3], *fd2[3];
} opt_edge_data_3;

typedef struct {
  unsigned constraints;
  realType x[3], jac[9];
} opt_point_data_3;

typedef struct {
  lagrange_data *ld;
  unsigned size[4];
  const realType *elx[3];
  opt_face_data_3 fd;
  opt_edge_data_3 ed;
  opt_point_data_3 pd;
  realType *work;
  realType x[3], jac[9];
} opt_data_3;

typedef struct {
  const realType *xw[3];   /* geometry data */
  realType *z[3];          /* lobatto nodes */
  lagrange_data ld[3]; /* interpolation, derivative weights & data */
  unsigned nptel;      /* nodes per element */
  hash_data_3 *hash;   /* geometric hashing data */
  findpt_listel *list, **sorted, **end;
                                        /* pre-allocated list of elements to
                                           check (found by hashing), and
                                           pre-allocated list of pointers into
                                           the first list for sorting */
  opt_data_3 *od; /* data for the optimization algorithm */
  realType *od_work;
} findpt_data_3;

findpt_data_2 *findpt_setup_2(
          const realType *const xw[2], const unsigned n[2], uint nel,
          uint max_hash_size, realType bbox_tol);
findpt_data_3 *findpt_setup_3(
          const realType *const xw[3], const unsigned n[3], uint nel,
          uint max_hash_size, realType bbox_tol);

void findpt_free_2(findpt_data_2 *p);
void findpt_free_3(findpt_data_3 *p);

const realType *findpt_allbnd_2(const findpt_data_2 *p);
const realType *findpt_allbnd_3(const findpt_data_3 *p);

typedef int (*findpt_func)(void *, const realType *, int, uint *, realType *, realType *);
int findpt_2(findpt_data_2 *p, const realType x[2], int guess,
             uint *el, realType r[2], realType *dist);
int findpt_3(findpt_data_3 *p, const realType x[3], int guess,
             uint *el, realType r[3], realType *dist);

void findpt_weights_2(findpt_data_2 *p, const realType r[2]);

void findpt_weights_3(findpt_data_3 *p, const realType r[3]);

double findpt_eval_2(findpt_data_2 *p, const realType *u);

double findpt_eval_3(findpt_data_3 *p, const realType *u);

/*======================================================
/ from extrafindpt.h
/======================================================
*/

void opt_alloc_3(opt_data_3 *p, lagrange_data *ld);
void opt_free_3(opt_data_3 *p);
double opt_findpt_3(opt_data_3 *p, const realType *const elx[3],
                           const realType xstar[3], realType r[3], unsigned *constr);
void opt_vol_set_intp_3(opt_data_3 *p, const realType r[3]);

/* for 2d spectralQuad */
/*--------------------------------------------------------------------------

   2 - D

  --------------------------------------------------------------------------*/

void opt_alloc_2(opt_data_2 *p, lagrange_data *ld);
void opt_free_2(opt_data_2 *p);
double opt_findpt_2(opt_data_2 *p, const realType *const elx[2],
                           const realType xstar[2], realType r[2], unsigned *constr);

extern const unsigned opt_no_constraints_2;
extern const unsigned opt_no_constraints_3;

/*======================================================
/ from errmem.h
/======================================================
*/

/* requires:
     <stdlib.h> for malloc, calloc, realloc, free
*/

/*--------------------------------------------------------------------------
   Error Reporting
   Memory Allocation Wrappers to Catch Out-of-memory
  --------------------------------------------------------------------------*/

/* #include "malloc.h" */
#include <stdlib.h>

#ifdef __GNUC__
void fail(const char *fmt, ...) __attribute__ ((noreturn));
#define MAYBE_UNUSED __attribute__ ((unused))
#else
void fail(const char *fmt, ...);
#define MAYBE_UNUSED
#endif

#if 0
{}
#endif

static void *smalloc(size_t size, const char *file) MAYBE_UNUSED;
static void *smalloc(size_t size, const char *file)
{
  void *res = malloc(size);
  if(!res && size) fail("%s: allocation of %d bytes failed\n",file,(int)size);
  return res;
}

static void *scalloc(size_t nmemb, size_t size, const char *file) MAYBE_UNUSED;
static void *scalloc(size_t nmemb, size_t size, const char *file)
{
  void *res = calloc(nmemb, size);
  if(!res && nmemb)
    fail("%s: allocation of %d bytes failed\n",file,(int)size*nmemb);
  return res;
}

static void *srealloc(void *ptr, size_t size, const char *file) MAYBE_UNUSED;
static void *srealloc(void *ptr, size_t size, const char *file)
{
  void *res = realloc(ptr, size);
  if(!res && size) fail("%s: allocation of %d bytes failed\n",file,(int)size);
  return res;
}

#define tmalloc(type, count) \
  ((type*) smalloc((count)*sizeof(type),__FILE__) )
#define tcalloc(type, count) \
  ((type*) scalloc((count),sizeof(type),__FILE__) )
#define trealloc(type, ptr, count) \
  ((type*) srealloc((ptr),(count)*sizeof(type),__FILE__) )
/*
typedef struct { size_t size; void *ptr; } buffer;

static void buffer_init_(buffer *b, size_t size, const char *file) MAYBE_UNUSED;
static void buffer_init_(buffer *b, size_t size, const char *file)
{
  b->size=size, b->ptr=smalloc(size,file);
}
static void buffer_reserve_(buffer *b, size_t min, const char *file)
  MAYBE_UNUSED;
static void buffer_reserve_(buffer *b, size_t min, const char *file)
{
  size_t size = b->size;
  if(size<min) {
    size+=size/2+1;
    if(size<min) size=min;
    b->ptr=srealloc(b->ptr,size,file);
  }
}
static void buffer_free(buffer *b) MAYBE_UNUSED;
static void buffer_free(buffer *b) { free(b->ptr); }

#define buffer_init(b,size) buffer_init_(b,size,__FILE__)
#define buffer_reserve(b,min) buffer_reserve_(b,min,__FILE__)
*/


/*======================================================
/ from minmax.h
/======================================================
*/

/*--------------------------------------------------------------------------
   Min, Max, Norm
  --------------------------------------------------------------------------*/

#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__ ((unused))
#else
#define MAYBE_UNUSED
#endif

#define DECLMINMAX(type, prefix) \
  static type prefix##min_2(type a, type b) MAYBE_UNUSED;               \
  static type prefix##min_2(type a, type b) {                           \
    return b<a?b:a;                                                     \
  }                                                                     \
  static type prefix##max_2(type a, type b) MAYBE_UNUSED;               \
  static type prefix##max_2(type a, type b) {                           \
    return a>b?a:b;                                                     \
  }                                                                     \
  static void prefix##minmax_2(type *min, type *max, type a,            \
                               type b) MAYBE_UNUSED;                    \
  static void prefix##minmax_2(type *min, type *max, type a, type b) {  \
    if(b<a) *min=b, *max=a;                                             \
    else *min=a, *max=b;                                                \
  }                                                                     \
  static type prefix##min_3(type a, type b, type c) MAYBE_UNUSED;       \
  static type prefix##min_3(type a, type b, type c) {                   \
    return b<a?(c<b?c:b):(c<a?c:a);                                     \
  }                                                                     \
  static type prefix##max_3(type a, type b, type c) MAYBE_UNUSED;       \
  static type prefix##max_3(type a, type b, type c) {                   \
    return a>b?(a>c?a:c):(b>c?b:c);                                     \
  }                                                                     \
  static void prefix##minmax_3(type *min, type *max, type a, type b,    \
                               type c) MAYBE_UNUSED;                    \
  static void prefix##minmax_3(type *min, type *max, type a, type b,    \
                               type c) {                                \
    if(b<a) *min=prefix##min_2(b,c), *max=prefix##max_2(a,c);           \
    else    *min=prefix##min_2(a,c), *max=prefix##max_2(b,c);           \
  }

DECLMINMAX(int, i)
DECLMINMAX(unsigned, u)
DECLMINMAX(realType, r)
#undef DECLMINMAX

static realType r1norm_1(realType a) MAYBE_UNUSED;
static realType r1norm_1(realType a) {
  return mbabs(a);
}
static realType r1norm_2(realType a, realType b) MAYBE_UNUSED;
static realType r1norm_2(realType a, realType b) {
  return mbabs(a)+mbabs(b);
}
static realType r1norm_3(realType a, realType b, realType c) MAYBE_UNUSED;
static realType r1norm_3(realType a, realType b, realType c) {
  return mbabs(a)+mbabs(b)+mbabs(c);
}
static realType r2norm_1(realType a) MAYBE_UNUSED;
static realType r2norm_1(realType a) {
  return mbsqrt(a*a);
}
static realType r2norm_2(realType a, realType b) MAYBE_UNUSED;
static realType r2norm_2(realType a, realType b) {
  return mbsqrt(a*a+b*b);
}
static realType r2norm_3(realType a, realType b, realType c) MAYBE_UNUSED;
static realType r2norm_3(realType a, realType b, realType c) {
  return mbsqrt(a*a+b*b+c*c);
}

#endif


