#ifndef ASSOCIATION_H
#define ASSOCIATION_H

#define TRUE 1
#define FALSE 0

#include <iostream>
// #include <omp.h>
#include <mkl.h>

extern "C" {
#include <gsl/gsl_rng.h>
#include <gsl/gsl_const_mksa.h>
}
#include "matrix.h"
#include "connectivity.h"
#include "potential.h"
/* #include "trajectory.h" */
/* #include "read_file_condition.h" */
#include "file_IO.h"
#include "geometry.h"

class ASSOCIATION
: public CONNECTIVITY
{
 public:
  MKL_LONG Nc; // number of chains per micelle
  MKL_LONG Tec; // tolerance of end chains
  MKL_LONG N_min; // 2Nc - Tec
  MKL_LONG N_max; // 2Nc + Tec
  MKL_LONG N_ASSOCIATION;
  bool MULTIPLE_CONNECTIONS;
  bool NUMBER_RESTRICTION;
  bool PROBABILITY_AREA;

  MATRIX N_ASSOCIATION_PARTICLE;
  MATRIX *weight;
  // related with suggestion probability (selecting a degenerated pair)
  MATRIX *CASE_SUGGESTION;
  MATRIX *dPDF_SUGGESTION;
  MATRIX *dCDF_SUGGESTION;
  double *Z_SUGGESTION;

  // related with association probability
  MATRIX *dCDF_ASSOCIATION;
  /* size_t **INDEX_ASSOCIATION; */
  MATRIX *INDEX_ASSOCIATION;
  MKL_LONG *TOKEN_ASSOCIATION;

  // member variables

  /* MKL_LONG flag_other, flag_new, flag_itself, flag_hash_other; */
  static const MKL_LONG flag_other = 0;
  static const MKL_LONG flag_new = 1;
  static const MKL_LONG flag_itself = 2;
  static const MKL_LONG flag_hash_other = 3;
  static const MKL_LONG flag_hash_backtrace = 4;

  bool
    (*ADD_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    (*OPP_DEL_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    (*CANCEL_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    (*CHECK_N_ADD_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    (*CHECK_N_MOV_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    (*CHECK_N_OPP_DEL_ASSOCIATION)
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);

  MKL_LONG
    N_TOTAL_ASSOCIATION
    ();

  MKL_LONG
  N_PARTICLE_ASSOCIATION
  (MKL_LONG index_particle);

  MKL_LONG
    N_CONNECTED_ENDS
    (MKL_LONG given_index);
  MKL_LONG
    N_TOTAL_CONNECTED_ENDS
    ();
  bool
    CHECK_EXIST
    (MKL_LONG index_A, MKL_LONG index_B);
  bool
    CHECK_EXIST_1D
    (MKL_LONG index_A, MKL_LONG index_B);
  MKL_LONG
    add_association
    (const MKL_LONG index_particle, MKL_LONG index_target);
  MKL_LONG
    add_association_INFO
    (POTENTIAL_SET& POTs, const MKL_LONG index_particle, MKL_LONG index_target, double distance);
  MKL_LONG
    opp_del_association
    (const MKL_LONG index_particle, MKL_LONG index_target);
  MKL_LONG
    opp_del_association_hash
    (const MKL_LONG index_particle, MKL_LONG hash_index_target);
  MKL_LONG
    opp_del_association_IK
    (MKL_LONG index_I, MKL_LONG hash_index_K);
  MKL_LONG
    opp_del_association_grab_IK
    (MKL_LONG index_I, MKL_LONG hash_index_K);

  MKL_LONG
    GET_INDEX_HASH_FROM_ROLL
    (const MKL_LONG index_particle, double rolled_p);
  MKL_LONG
    GET_HASH_FROM_ROLL
    (const MKL_LONG index_particle, double rolled_p);
  MKL_LONG
    FIND_HASH_INDEX
    (MKL_LONG index_A, MKL_LONG index_B);
  MKL_LONG
  update_N_association
  ();

  MKL_LONG
  get_N_association_particle
  (MKL_LONG index_particle);

  // At the moment, there is no namespace dependence between KINETICS::NORMALIZED and KINETICS::METROPOLIS.
  // The dependency is set with the w_function and transtion_probability that is developed with lib_potential.
  // For better performance, it would be better to tune the following with specific dependencies (future works).
  double
    update_CASE_SUGGESTION_particle_hash_target
    (POTENTIAL_SET& POTs, const MKL_LONG index_particle, MKL_LONG hash_index_target, double distance);

  double
    update_CASE_SUGGESTION_particle_target
    (POTENTIAL_SET& POTs, const MKL_LONG index_particle, MKL_LONG index_target, double distance);

  double
    update_Z_SUGGESTION_particle
    (const MKL_LONG index_particle);
  double
    update_dPDF_SUGGESTION_particle
    (const MKL_LONG index_particle);
  double
    update_dCDF_SUGGESTION_particle
    (const MKL_LONG index_particle);
  double
    update_CHAIN_SUGGESTION_MAP_particle
    (const MKL_LONG index_particle, POTENTIAL_SET& POTs, RDIST& R_boost);

  double
    update_ASSOCIATION_MAP_particle
    (const MKL_LONG index_particle, POTENTIAL_SET& POTs, RDIST& R_boost);

  MKL_LONG
    return_multiplicity
    (const MKL_LONG index_particle, const MKL_LONG target_particle)
  // this additional function is useful for tracking individual chain
  {
    for(MKL_LONG j=0; j<TOKEN[index_particle]; j++)
      {
        if(HASH[index_particle](j) == target_particle)
          return weight[index_particle](j);
      }
    return 0;
  }

 ASSOCIATION()
   : CONNECTIVITY(){}

  ASSOCIATION
    (COND& given_condition);

  ASSOCIATION
    (MKL_LONG number_of_particles, MKL_LONG number_of_chains_per_particles, MKL_LONG tolerance_connection, bool ALLOWING_MULTIPLE_CONNECTIONS);

  MKL_LONG
    dynamic_alloc
    ();
  MKL_LONG
    set_initial_condition
    ();
  MKL_LONG
    initial
    ();
  MKL_LONG
    initial_inheritance
    ();
  virtual
    ~ASSOCIATION()
    {
      delete[] weight;

      // related with suggestion probability
      delete[] CASE_SUGGESTION;
      delete[] dPDF_SUGGESTION;
      delete[] dCDF_SUGGESTION;
      delete[] Z_SUGGESTION;
      // related with association probability
      delete[] dCDF_ASSOCIATION;
      delete[] INDEX_ASSOCIATION;

      delete[] TOKEN_ASSOCIATION;
    }

  MKL_LONG
    read_exist_weight
  (const char* fn_weight, MKL_LONG N_steps);

};

MKL_LONG
index_set_val
(size_t* index, MKL_LONG given_val, MKL_LONG size_of_arr);

namespace TRUTH_MAP
{
  namespace MULTIPLE
  {
    bool
      CHECK_N_ADD_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
    bool
      CHECK_N_OPP_DEL_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
    bool
      CHECK_N_MOV_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);

    namespace NO_RESTRICTION // this option is used when there are no restriction for number of attached chain ends
    {
      bool
	return_TRUE
	(ASSOCIATION& CONNECT, MKL_LONG index_set[]);

    }
  }

  namespace SINGLE
  {
    bool
      CHECK_N_ADD_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
    bool
      CHECK_N_MOV_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
    bool
      CHECK_N_OPP_DEL_BOOST
      (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  }
  bool
    CANCEL_ASSOCIATION_BOOST
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    ADD_ASSOCIATION_BOOST
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);
  bool
    OPP_DEL_ASSOCIATION_BOOST
    (ASSOCIATION& CONNECT, MKL_LONG index_set[]);

  bool
    OPP_DEL_ASSOCIATION_BASIC
    (ASSOCIATION& CONNECT, MKL_LONG index_itself, MKL_LONG index_target, MKL_LONG index_new);

  bool
    NEW_ASSOCIATION_BASIC
    (ASSOCIATION& CONNECT, MKL_LONG index_itself, MKL_LONG index_target, MKL_LONG index_new);
  bool
    NEW_ASSOCIATION_SINGLE
    (ASSOCIATION& CONNECT, MKL_LONG index_itself, MKL_LONG index_target, MKL_LONG index_new);

  bool
    MOV_ASSOCIATION_BASIC
    (ASSOCIATION& CONNECT, MKL_LONG index_itself, MKL_LONG index_target, MKL_LONG index_new);
  bool
    MOV_ASSOCIATION_SINGLE
    (ASSOCIATION& CONNECT, MKL_LONG index_itself, MKL_LONG index_target, MKL_LONG index_new);
}

namespace MICELLE
{
  double
  update_SOFT_REPULSION_P2(POTENTIAL_SET& given_POT, ASSOCIATION& CONNECT);

  double
  update_SOFT_REPULSION_P2_particle(POTENTIAL_SET& given_POT, ASSOCIATION& CONNECT, MKL_LONG index_particle);

}

namespace KINETICS
{
  double
    CONNECTIVITY_update_dCDF_SUGGESTION_particle
    (ASSOCIATION* CONNECT, const MKL_LONG index_particle);
  double
    CONNECTIVITY_update_CASE_SUGGESTION_particle_hash_target
    (ASSOCIATION* CONNECT, POTENTIAL_SET& POTs, const MKL_LONG index_particle, MKL_LONG hash_index_target, double distance);
  double
    CONNECTIVITY_update_CASE_SUGGESTION_particle_target
    (ASSOCIATION* CONNECT, POTENTIAL_SET& POTs, const MKL_LONG index_particle, MKL_LONG index_target, double distance);

  double
    CONNECTIVITY_update_Z_SUGGESTION_particle
    (ASSOCIATION* CONNECT, const MKL_LONG index_particle);
  double
    CONNECTIVITY_update_dPDF_SUGGESTION_particle
    (ASSOCIATION* CONNECT, const MKL_LONG index_particle);
}

class CHAIN_INFORMATION
{
  /*
    The CHAIN class is using CHAIN_NODE without inheritance.
    It contains dynamically allocated CHAIN_NODE.
  */
 public:

  CHAIN_NODE *CHAIN;
  MKL_LONG N_chains;
  MKL_LONG N_particles;

  bool INITIALIZATION;

  MKL_LONG&
    HEAD
    (MKL_LONG given_chain_index)
    {
      return CHAIN[given_chain_index].HEAD;
    }
  MKL_LONG&
    TAIL
    (MKL_LONG given_chain_index)
    {
      return CHAIN[given_chain_index].TAIL;
    }

  /*
    On the same reason, the ATTACHED will return pointer variable rather than reference variable. This break the existing interface.
  */
  MKL_LONG&
    ATTACHED
    (MKL_LONG given_chain_index, MKL_LONG flag_HEAD_TAIL)
    {
      return CHAIN[given_chain_index].index(flag_HEAD_TAIL);
    }

  MKL_LONG
    mov_attachment
    (MKL_LONG target_particle, MKL_LONG given_chain_index, MKL_LONG flag_HEAD_TAIL)
  {
    /*
      flag_HEAD_TAIL is 0 for HEAD while 1 for TAIL.
    */
    ATTACHED(given_chain_index, flag_HEAD_TAIL) = target_particle;

    return 0;
  }

  MKL_LONG
    add_attachment
    (const MKL_LONG index_particle, MKL_LONG given_chain_index, MKL_LONG flag_HEAD_TAIL)
  {
    // there is no distingushable between add and mov attachment for chain node
    mov_attachment(index_particle, given_chain_index, flag_HEAD_TAIL);
    return 0;
  }

  MKL_LONG
    del_attachment
    (MKL_LONG given_chain_index, MKL_LONG flag_HEAD_TAIL)
  {
    // it make detachment for subjected chain ends
    // then attach to particles which other chain ends are attached
    mov_attachment(ATTACHED(given_chain_index, (flag_HEAD_TAIL + 1)%2), given_chain_index, flag_HEAD_TAIL);
    return 0;
  }

  MKL_LONG
    initial
    (MKL_LONG number_of_chains, MKL_LONG number_of_particles)
  {
    printf("ST:initial:CHAIN_INFORMATION\n");
    N_chains = number_of_chains;
    N_particles = number_of_particles;
    CHAIN = new CHAIN_NODE [N_chains];

    for(MKL_LONG i=0; i<N_chains; i++)
      {
        /*
          it allocate the equivalent number of chain ends per particles
        */
        HEAD(i) = i%N_particles;
        TAIL(i) = i%N_particles;
      }
    INITIALIZATION = TRUE;
    return INITIALIZATION;
  }

  MKL_LONG
    initial
    (ASSOCIATION& CONNECT)
  {
    N_chains = CONNECT.Nc*CONNECT.Np;
    N_particles = CONNECT.Np;
    CHAIN = new CHAIN_NODE [N_chains];
    MKL_LONG cnt = 0;

    for(MKL_LONG i=0; i<N_particles; i++)
      {
        for(MKL_LONG j=i; j<N_particles; j++) // it includes i=j which is loop and prevents j<i means all the pair is once accounted.
          {
            MKL_LONG wij = CONNECT.return_multiplicity(i, j);
            if(i==j)
              {
                // note that weight count chain end rather than chain
                // when i != j, the duplication is removed because we only count the pair with condition j>i
                // when i == j, the duplication happens in the wij.
                wij /=2;
              }
            for(MKL_LONG w=0; w<wij; w++)
              {
                HEAD(cnt) = i;
                TAIL(cnt) = j;
                if(cnt++ > N_chains)
                  printf("Err: counted number of chain overflood\n");
              }
          }
      }
    printf("end_particle_allocation\n");
    INITIALIZATION = TRUE;
    return INITIALIZATION;
  }

  MKL_LONG
    inheritance_chain
    (COND& given_condition, ASSOCIATION& CONNECT)
  {
    N_chains = CONNECT.Nc*CONNECT.Np;
    N_particles = CONNECT.Np;
    /* CHAIN = (CHAIN_NODE*)mkl_malloc(N_chains*sizeof(CHAIN_NODE), BIT); */
    // the problem of the original is that 'malloc' will not call the constructor
    CHAIN = new CHAIN_NODE [N_chains];

    MKL_LONG cnt = 0;
    ifstream GIVEN_CHAIN_FILE;
    GIVEN_CHAIN_FILE.open(given_condition("CONTINUATION_CHAIN_FN").c_str());
    string line;
    while(getline(GIVEN_CHAIN_FILE, line))
      cnt ++;
    GIVEN_CHAIN_FILE.clear();

    GIVEN_CHAIN_FILE.seekg(0);
    MKL_LONG N_steps = atoi(given_condition("CONTINUATION_STEP").c_str());
    if(N_steps == -1)
      {
        for(MKL_LONG i=0; i<cnt-1; i++)
          getline(GIVEN_CHAIN_FILE, line);
      }
    else
      {
        for(MKL_LONG i=0; i<N_steps; i++)
          getline(GIVEN_CHAIN_FILE, line);
      }

    for(MKL_LONG k=0; k<N_chains; k++)
      GIVEN_CHAIN_FILE >> HEAD(k); // index from 0 to N_chains-1 : HEAD

    for(MKL_LONG k=0; k<N_chains; k++)
      GIVEN_CHAIN_FILE >> TAIL(k); // index from N_chains to 2*N_chains-1 : TAIL

    GIVEN_CHAIN_FILE.close();
    INITIALIZATION = TRUE;
    return INITIALIZATION;
  }

  CHAIN_INFORMATION()
    {
      std::cout << "ERR: CHAIN Class must have initialization argument\n";
    }
  CHAIN_INFORMATION(MKL_LONG number_of_chains, MKL_LONG number_of_particles)
    {
      initial(number_of_chains, number_of_particles);
    }
  CHAIN_INFORMATION(COND& given_condition)
    {
      if(given_condition("tracking_individual_chain") == "TRUE")
        {
          if(given_condition("CONTINUATION_CHAIN") == "TRUE")
            {
              printf("ERR: Without CONTINUATION TOPOLOGICAL INFORMATION (.hash, .weight), the tracking individual chain cannot inherit existing .chain file information\n");
            }
          else
            {
              printf("ST:Constructor:CHAIN_INFORMATION\n");
              initial(atoi(given_condition("N_chains_per_particle").c_str())*atoi(given_condition("Np").c_str()), atoi(given_condition("Np").c_str()));
              printf("END:Constructor:CHAIN_INFORMATION\n");
            }
        }
    }
  CHAIN_INFORMATION(COND& given_condition, ASSOCIATION& CONNECT)
    // it initialize based on the CONNECT information
    {
      if(given_condition("tracking_individual_chain") == "TRUE")
        {
          if(given_condition("CONTINUATION_CHAIN") == "TRUE")
            {
              inheritance_chain(given_condition, CONNECT);
            }
          else
            {
              printf("ST:Constructor:CHAIN_INFORMATION\n");
              initial(CONNECT);
              printf("END:Constructor:CHAIN_INFORMATION\n");
            }
        }
    }
  virtual ~CHAIN_INFORMATION()
    {
      if(INITIALIZATION)
        delete[] CHAIN;
    }
};

// double inline MICELLE::update_SOFT_REPULSION_P2_particle(POTENTIAL_SET& given_POT, ASSOCIATION& CONNECT, MKL_LONG index_particle)
// {
//   given_POT.force_variables.C_rep_arr(index_particle) = given_POT.force_variables.repulsion_coefficient_base*CONNECT.N_ASSOCIATION_PARTICLE(index_particle)*CONNECT.N_ASSOCIATION_PARTICLE(index_particle);
//   // C_rep(p) = C_0 * p^2

//   return given_POT.force_variables.C_rep_arr(index_particle);
// }

#endif


/** \file LinkFunc.h
*
* `IRLS' is a C++ implementation of the IRLS algorithm for GLM
* Copyright (C) 2013 Xioaquan Wen, Timothee Flutre
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _LINKFUNC_H_
#define _LINKFUNC_H_

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

class LinkFunc
{
public:
  bool quasi;
  virtual void init_mv(gsl_vector * Y, gsl_vector * mv)=0;
  virtual void compute_z(gsl_vector * y, gsl_vector * mv,
			 gsl_vector * offset, gsl_vector * z)=0;
  virtual void compute_weights(gsl_vector * mv, gsl_vector * w)=0;
  virtual void compute_mv(gsl_vector * bv, gsl_matrix * Xv,
			  gsl_vector * offset, gsl_vector * mv)=0;
  virtual double compute_dispersion(gsl_vector * y, gsl_matrix * Xv,
				    gsl_vector * bv, gsl_vector * offset,
				    gsl_vector * mv, double rank,
				    bool quasi_lik)=0;
  virtual ~LinkFunc() {};

}; // LinkFunc

#endif // _LINKFUNC_H_


#include "spinodal_spect.h"
#include <gsl/gsl_rng.h>

// Generate array of random numbers between 0 and 1
double *rand_ZeroToOne(int Nx, int Ny, int seed, double *random_ZeroToOne_array) {
        const gsl_rng_type *T;
        gsl_rng *r;

        int i,
        NxNy = Nx * Ny;

        gsl_rng_env_setup();
        T = gsl_rng_default;
        r = gsl_rng_alloc(T);
        gsl_rng_set(r, seed);

        // Generate array of random numbers between 0 and 1
        for (i = 0; i < NxNy; i++) {
            random_ZeroToOne_array[i] = gsl_rng_uniform(r);
        }

        gsl_rng_free(r);

        return random_ZeroToOne_array;

    }


/**
* Output routines for stamped images and overlaid traces.
*/
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_vector.h>

#include "spce_output.h"
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spce_pathlength.h"
#include "spc_driz.h"
#include "crossdisp_utils.h"

#define MIN(x,y) (((x)<(y))?(x):(y))
#define MAX(x,y) (((x)>(y))?(x):(y))
#define SQR(x) ((x)*(x))

/* transformation from xi to the index into the trace pixel table */
#define XI_QUANT(xi) ((int)rint(((xi)-minxi)/xi_bin_width))

/**
 * Function: stamp_img
 * Produce a gsl array containing a stamp image.
 * Returns NULL is input object is NULL.
 *
 * Parameters:
 * @param ap_p    - a pointer to a -1 terminated ap_pixel array
 * @param width   - extraction width used
 * @param stp_min - xy-coords of the lower left corner on image
 *
 * Returns:
 * @return res  - a pointer to a newly allocated gsl_matrix containing the
 *                spectrum
 */
gsl_matrix * stamp_img (const ap_pixel * const ap_p, float width, d_point *stp_min)
{
  const ap_pixel *cur_p;
  double min_px = 1e32;
  double max_px = -1e32;
  double min_py = 1e32;
  double max_py = -1e32;
  gsl_matrix *res;
  long xsize,ysize,i,j,n=0;

  if (ap_p==NULL) {
    /* Create a dummy stamp image */
    res = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(res, 0.0);

    return res;
  }

  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      min_px = MIN (min_px, cur_p->p_x);
      max_px = MAX (max_px, cur_p->p_x);
      min_py = MIN (min_py, cur_p->p_y);
      max_py = MAX (max_py, cur_p->p_y);
      n++;
    }

  xsize = max_px-min_px+2;
  ysize = max_py-min_py+2;

  stp_min->x = min_px;
  stp_min->y = min_py;

  if (n>0)
    {
      res = gsl_matrix_alloc(xsize,ysize);
    } else {
      /* Create a dummy stamp image */
      res = gsl_matrix_alloc(10,10);
      /* Fill stamp with 0.0 values */
      gsl_matrix_set_all(res, 0.0);
      return res;
    }

  /* Fill stamp with NaN values */
  gsl_matrix_set_all(res, 0.0);
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      i = (long) floor(cur_p->p_x - min_px+.5);
      j = (long) floor(cur_p->p_y - min_py+.5);
      if ((i<0) || (i>=xsize)) continue;
      if ((j<0) || (j>=ysize)) continue;

      //fprintf(stderr,"i: %d j: %d\n",i,j);
      gsl_matrix_set(res,i,j,cur_p->count);
    }

  return res;
}

/*
 * Function: get_minxy_from_PET
 * The function determines the minimum in
 * x and y of the pixels in a PET
 *
 * Parameters:
 * @param ap_p - the PET vector
 *
 * Returns:
 * @return res - the minima in x, y
 */
d_point
get_minxy_from_PET(const ap_pixel * const ap_p)
{
  const ap_pixel *cur_p;
  double min_px = 1e32;
  //double max_px = -1e32;
  double min_py = 1e32;
  //double max_py = -1e32;
  d_point res;

  // return -1 in case of an empty PET
  if (ap_p==NULL) {
    res.x = -1;
    res.y = -1;
    return res;
  }

  // go over each pixel, determine the maximum
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      min_px = MIN (min_px, cur_p->p_x);
      min_py = MIN (min_py, cur_p->p_y);
    }

  // fill the return
  res.x = min_px;
  res.y = min_py;

  // return the result
  return res;
}

/*
 * Function: get_maxxy_from_PET
 * The function determines the maximum in
 * x and y of the pixels in a PET
 *
 * Parameters:
 * @param ap_p - the PET vector
 *
 * Returns:
 * @return res - the maximum in x, y
 */
d_point
get_maxxy_from_PET(const ap_pixel * const ap_p)
{
  const ap_pixel *cur_p;
  //double min_px = 1e32;
  double max_px = -1e32;
  //double min_py = 1e32;
  double max_py = -1e32;
  d_point res;

  // return -1 in case of an empty PET
  if (ap_p==NULL) {
    res.x = -1;
    res.y = -1;
    return res;
  }

  // go over each pixel, determine the maximum
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      max_px = MAX (max_px, cur_p->p_x);
      max_py = MAX (max_py, cur_p->p_y);
    }

  // fill the return
  res.x = max_px;
  res.y = max_py;

  // return the result
  return res;
}

/**
 * Function: stamp_img_drzprep
 * Creates all drizzle prepare stamp images from a PET
 * vector. In case of an empty PET, dummy stamps are returned.
 *
 * Parameters:
 * @param opt_extr   - flagg to indicate optimal extraction
 * @param ap_p       - a pointer to a -1 terminated ap_pixel array
 * @param width      - extraction width used
 * @param nullval    - the default pixel value
 * @param quant_cont - flag to indicate quantitative cont.
 * @param dimension  - size information
 * @param drzcoeffs  - the drizzle coefficients
 * @param exptime    - the exposure time
 * @param sky_cps    - the sky background
 *
 * Returns:
 * @return drzprep_stamps - a pointer to the new struct with the
 *                          drzprep stamp images
 */
drzprep *
stamp_img_drzprep (const int opt_extr, const ap_pixel *const ap_p, const ap_pixel *const se_p, float width,
		   float nullval, int quant_cont, drzstamp_dim dimension,
		   gsl_matrix *drzcoeffs, double exptime, double sky_cps,
		   double rdnoise, const int bckmode)
{
  drzprep *drzprep_stamps;
  const ap_pixel *cur_p;
  const ap_pixel *xxx_p;

  int i, j;

  double var;
  double corr;
  double sqr_expt;
  double sqr_rdns;

  // allocate space for the output structure
  drzprep_stamps = (drzprep *)malloc(sizeof(drzprep));

  if (ap_p==NULL) {

    // create a dummy count image and set it to default
    drzprep_stamps->counts = gsl_matrix_alloc(10,10);
    gsl_matrix_set_all(drzprep_stamps->counts, nullval);

    // create a dummy error image and set it to default
    drzprep_stamps->error = gsl_matrix_alloc(10,10);
    gsl_matrix_set_all(drzprep_stamps->error, nullval);

    // create a dummy contamination image and set it to default
    drzprep_stamps->cont = gsl_matrix_alloc(10,10);
    gsl_matrix_set_all(drzprep_stamps->cont, nullval);

    // create a dummy model image and set it to default
    drzprep_stamps->model = gsl_matrix_alloc(10,10);
    gsl_matrix_set_all(drzprep_stamps->model, nullval);

    // create a dummy model image and set it to default
    drzprep_stamps->vari = gsl_matrix_alloc(10,10);
    gsl_matrix_set_all(drzprep_stamps->vari, nullval);

    return drzprep_stamps;
  }

  // allocate the count matrix and fill it with default value
  drzprep_stamps->counts  = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
  gsl_matrix_set_all(drzprep_stamps->counts, nullval);

  // allocate the error matrix and fill it with default value
  drzprep_stamps->error  = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
  gsl_matrix_set_all(drzprep_stamps->error, 0.0);

  // allocate the contamination matrix and fill it with default value
  drzprep_stamps->cont  = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
  gsl_matrix_set_all(drzprep_stamps->cont, 0.0);

  // set the two extentions
  // which are only somethimes
  // used to NULL
  drzprep_stamps->model  = NULL;
  drzprep_stamps->vari  = NULL;

  if (opt_extr)
    {
      // allocate the mode; matrix and fill it with default value
      drzprep_stamps->model  = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
      gsl_matrix_set_all(drzprep_stamps->model, 0.0);

      // allocate the mode; matrix and fill it with default value
      drzprep_stamps->vari  = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
      gsl_matrix_set_all(drzprep_stamps->vari, 0.0);
    }

  // square the exposure time for speed
  sqr_expt = exptime*exptime;
  sqr_rdns = rdnoise*rdnoise;

  // go over each PET pixel
  if (opt_extr && bckmode)
    xxx_p = se_p;
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {

      // compute the coordinates in the stamp images
      // if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      i = (int)(cur_p->p_x - dimension.xstart);
      j = (int)(cur_p->p_y - dimension.ystart);

      // check whether the coordinates are inside
      if ((i<0) || (i>=dimension.xsize) || (j<0) || (j>=dimension.ysize))
	{
	  if (opt_extr && bckmode)
	    xxx_p++;
	continue;
	}

      // comute the jacobian of the drizzle coefficients
      corr = fabs(get_det_jacobian(i+1, j+1, drzcoeffs, (int)dimension.xsize,(int)dimension.ysize));

      // compute the count value
      gsl_matrix_set(drzprep_stamps->counts,i,j,(cur_p->count)/corr);

      // MODIFIED in Dec. 2010 in order
      // to get error propagation
      // compute the true error value in electrons
      gsl_matrix_set(drzprep_stamps->error,i,j,(cur_p->error*cur_p->error*sqr_expt)/corr/corr);

      // compute the contamination value
      if (quant_cont)
	gsl_matrix_set(drzprep_stamps->cont,i,j,(cur_p->contam)/corr);
      else
	gsl_matrix_set(drzprep_stamps->cont,i,j,(cur_p->contam));

      if (opt_extr)
	{

	  if (bckmode)
	    {
	      // check that the foreground and background
	      // PET element describe the same pixel
	      if (xxx_p->x != cur_p->x || xxx_p->y != cur_p->y)
		aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			     "aXe_DRZPREP: Background PET and Object PET "
			     "have different pixel orders in PET's.\n");

	      // compute the inverse variance value for the object PET
	      // derived from the noise characteristics
	      var = ((xxx_p->model+xxx_p->contam+sky_cps+cur_p->count)*exptime + sqr_rdns)/sqr_expt;
	      //	      var = cur_p->count*exptime / sqr_expt;

	      // compute the model value
	      gsl_matrix_set(drzprep_stamps->model,i,j,(xxx_p->model/corr));
	    }
	  else
	    {
	      // compute the inverse variance value for the object PET
	      // derived from the noise characteristics
	      var = ((cur_p->model+cur_p->contam+sky_cps)*exptime + sqr_rdns)/sqr_expt;

	      // compute the model value
	      gsl_matrix_set(drzprep_stamps->model,i,j,(cur_p->model/corr));
	    }

	  if (var != 0.0)
	    gsl_matrix_set(drzprep_stamps->vari,i,j,1.0/var);

	}

      if (opt_extr && bckmode)
	xxx_p++;
    }

  // return the result structure
  return drzprep_stamps;
}

/**
 * Function: rectified_stamp_img
 * Produce a gsl array containing a 'rectified aperture'. That is,
 * an image of the spectum showing count on an xi,dist grid instead
 * of the traditional image grid x,y. Returns NULL is input object is NULL.
 *
 * Parameters:
 * @param ap_p  - a pointer to a -1 terminated ap_pixel array
 * @param width - extraction width used
 *
 * Returns:
 * @return res - a pointer to a newly allocated gsl_matrix containing the
 * rectified spectrum
 */
gsl_matrix *
rectified_stamp_img (const ap_pixel * const ap_p, float width, d_point *stp_min)
{
  const ap_pixel *cur_p;
  double min_xi = 1e32;
  double max_xi = -1e32;
  double min_dist = 1e32;
  double max_dist = -1e32;
  gsl_matrix *res;
  long xsize,ysize,i,j,n=0;

  if (ap_p==NULL) {
    /* Create a dummy stamp image */
    res = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(res, 0.0);
    return res;
  }

  i=0;
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      i++;
      min_xi = MIN (min_xi, cur_p->xi);
      max_xi = MAX (max_xi, cur_p->xi);
      min_dist = MIN (min_dist, cur_p->dist);
      max_dist = MAX (max_dist, cur_p->dist);
      n++;
    }

  if (i==0) {
    /* Create a dummy stamp image */
    res = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(res, 0.0);
    return res;
  }

  min_dist = floor(min_dist);
  max_dist = floor(max_dist);
  min_xi = floor(min_xi);
  max_xi = floor(max_xi);

  // compute the array dimensions
  xsize = max_xi-min_xi+2;
  ysize = max_dist-min_dist+2;

  // store the minima in both coords
  stp_min->x = min_xi;
  stp_min->y = min_dist;

  if (n>0)
    {
      res = gsl_matrix_alloc(xsize,ysize);
    } else {
      /* Create a dummy stamp image */
      res = gsl_matrix_alloc(10,10);
      /* Fill stamp with 0.0 values */
      gsl_matrix_set_all(res, 0.0);
      return res;
    }

  res = gsl_matrix_alloc(xsize,ysize);
  /* Fill stamp with NaN values */
  gsl_matrix_set_all(res, GSL_NAN);

  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      i = (long) floor(cur_p->xi - min_xi+.5);
      j = (long) floor(cur_p->dist - min_dist+.5);
      //fprintf(stderr,"i: %d j: %d\n",i,j);
      gsl_matrix_set(res,i,j,cur_p->count);
    }

  return res;
}

/**
 * Function: drizzled_stamp_img
 * Produce a structure containing a drizzled stamp image plux ther
 * associated weight image. The drizzled stamp image has constant
 * dispersion along the x-axis. No drizzling is applied
 * in cross-dispersion direction. The dimensions of the drizzled
 * stampimage were setr previously, and the information
 * is in an input structure. In case that there are no valid pixels,
 * a dummy structure with 10x10 empty pixels is returned.
 *
 * @param ap_p      - pointer to the list of PET pixels
 * @param width     - extraction width used
 * @param dimension - dimensional information on the drizzled stamp
 *
 * @return res      - structure with counts and weights of the drizzled stamp
*/
drzstamp *
drizzled_stamp_img (const  ap_pixel * const ap_p, double width,
		     double orient, const drzstamp_dim dimension)
{
  const ap_pixel *cur_p;

  quadrangle quad;

  drzstamp *res;
  gsl_matrix *counts;
  gsl_matrix *weight;

  int icen, ilow, iupp;
  int jcen, jlow, jupp;

  double xi, jacob=0;
  double value, allweig, weig;

  double maxarr=0.0;
  int iim, jjm;

  //double totweigth;
  //double cos_phi=0.0;

  int ii,jj;
  double arr;
  int stpi,stpj;
  double stpc;

  // allocate space for the result
  res = (drzstamp *) malloc(sizeof(drzstamp));

  // handle an empty PET
  if (ap_p==NULL || !dimension.resolution) {
    /* Create a dummy stamp image */
    counts = gsl_matrix_alloc(10,10);
    weight = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(counts, 0.0);
    gsl_matrix_set_all(weight, 0.0);

    // fill the output
    res->counts = counts;
    res->weight = weight;

    // return the output
    return res;
  }

  /* Allocate the stamp matrix*/
  /* Fill stamp with NaN values */
  counts = gsl_matrix_alloc(dimension.xsize,dimension.ysize);
  gsl_matrix_set_all(counts, GSL_NAN);

  /* Allocate the weight matrix*/
  /* Fill weight with 0.0 values */
  weight = gsl_matrix_alloc(dimension.xsize, dimension.ysize);
  gsl_matrix_set_all(weight, 0.0);

  // go over each pixel
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {

      // Skip this pixel if it was not actually used
      if (fabs(cur_p->dist)>width+1.5)
	continue;

      // create the quadrangle for the current pixel
      //      quad = get_quad_from_pixel2(cur_p, dimension);
      quad = get_quad_from_pixel(cur_p, orient, dimension);

      // get the jacobian (well, easy here)
      // the term "cos(cur_p->dxs)" must be there
      // to correct the enlargement necessary
      // to cover the whole lambda-cross dispersion area!
      // NOT COMPLETELY understood
      jacob = dimension.resolution/cur_p->dlambda*cos(cur_p->dxs);

      // get the central pixel (icen, jcen) of the current PET-pixel
      xi = cur_p->lambda/dimension.resolution;
      icen = (int) floor(xi - dimension.xstart+.5);
      jcen = (int) floor(cur_p->dist - dimension.ystart+.5);

      // get the uper and lower extend of the quadrangle in x
      iupp = (int)floor(quad.xmax - (double)icen + 0.5)+1;
      ilow = (int)floor(quad.xmin - (double)icen + 0.5);

      // get the uper and lower extend of the quadrangle in x
      jupp = (int)floor(quad.ymax - (double)jcen + 0.5)+1;
      jlow = (int)floor(quad.ymin - (double)jcen + 0.5);

      maxarr=0.0;
      //      totweigth = 0.0;
      // go over the extend in x
      for (ii=ilow;ii<iupp;ii++) {
      	// go over the extend in x
      	for (jj=jlow;jj<jupp;jj++) {

      	  // get the coordinates of the current output pixel
      	  stpi = icen+ii;
      	  stpj = jcen+jj;

      	  // check whether the current output pixel is within
      	  // the stamp image; continue if not
      	  if ( (stpi>=dimension.xsize)||(stpi<0)||(stpj>=dimension.ysize)||(stpj<0) )
      	    continue;

      	  // get the area which falls onto the current output pixel
      	  arr = boxer(stpi,stpj,quad.x,quad.y);
      	  if (arr > maxarr)
      	    {
      	      maxarr=arr;
      	      iim = ii;
      	      jjm = jj;
      	    }
      	  // get the already existing counts and weights
      	  stpc = gsl_matrix_get(counts,stpi,stpj);
      	  weig = gsl_matrix_get(weight,stpi,stpj);

      	  // initialize the counts, if necessary
      	  if (isnan(stpc) && (arr!=0.0))
      	    stpc = 0.0;

      	  // compute the new, total weight of the current output pixel
      	  //	  allweig = weig + jacob*arr;
      	  allweig = weig + arr;

      	  // do a weighted sum of the count value at the current output pixel
      	  value = (stpc*weig + arr*cur_p->count*jacob) / (allweig);

      	  // store the new count value and the new weight
      	  gsl_matrix_set(counts,stpi,stpj,value);
      	  gsl_matrix_set(weight,stpi,stpj,allweig);
      	  //	  totweigth = totweigth + arr;
      	}
      }
    }

  // fill the output structure
  res->counts = counts;
  res->weight = weight;

  // return the output
  return res;
}

/**
 * Function: get_drzprep_dim
 * The function computes the dimensional properties
 * of the stamp images created in the drizzle prepare task.
 * Those properties fix the image size
 * and the offsets in both coordinates.
 *
 * Parameter:
 * @param ap_p        - the list of beam pixels to find the diemnsions for
 * @param width       - the extraction width of the beam
 * @param boxwidth    - the boxsize
 * @param boxheight   - the boxwidth
 *
 * Returns:
 * @return dimensions - the filled quadrangle sturcture
 */
drzstamp_dim
get_drzprep_dim(const ap_pixel *const ap_p, float width,
		int boxwidth, int boxheight)
{

  drzstamp_dim dimensions;

  const ap_pixel *cur_p;

  double min_px = 1e32;
  double max_px = -1e32;
  double min_py = 1e32;
  double max_py = -1e32;

  // return a dummy if the PET is NULL
  if (ap_p==NULL)
    {
      dimensions.xstart=0.0;
      dimensions.ystart=0.0;

      dimensions.xsize=0;
      dimensions.ysize=0;

      return dimensions;
    }

  // go over each pixel, find minimum and maximum in x,y
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      // if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      min_px = MIN (min_px, cur_p->p_x);
      max_px = MAX (max_px, cur_p->p_x);
      min_py = MIN (min_py, cur_p->p_y);
      max_py = MAX (max_py, cur_p->p_y);
    }

  // compute the dimension of the stamps
  dimensions.xsize = max_px-min_px+1;
  dimensions.ysize = max_py-min_py+1;

  // correct the dimesions if necessary
  if (boxwidth > dimensions.xsize){
    dimensions.xsize = (long)boxwidth;
  }
  if (boxheight > dimensions.ysize){
    dimensions.ysize = boxheight;
  }

  // fix the start coordinates
  dimensions.xstart= min_px;
  dimensions.ystart= min_py;

  // return the structure
  return dimensions;
}

/**
 * Function: get_stamp_dim
 * The function computes the dimensional properties
 * of the drizzled stamp image associated to a beam.
 * Those properties fix the image size, dispersion
 * and the offsets in both coordinates
 *
 * Parameters:
 * @param ap_p        - the list of beam pixels to find the diemnsions for
 * @param width       - the extraction width of the beam
 * @param conf        - the configuration structure
 * @param beamID      - the the beam ID
 *
 * Returns:
 * @return dimensions - the filled quadrangle sturcture
 */
drzstamp_dim
get_stamp_dim(const ap_pixel *const ap_p, float width,
	      aperture_conf *conf, const int beamID, d_point *stp_min)
{
  drzstamp_dim dimensions;

  const ap_pixel *cur_p;

  double min_lam  = 1e32;
  double max_lam  = -1e32;
  double min_xi   = 1e32;
  double max_xi   = -1e32;
  double min_dist = 1e32;
  double max_dist = -1e32;
  double min_dlam = 1e32;
  double max_dlam = -1e32;

  //double resampwidth = 0.0;

  //long xsize,ysize;
  long npixel=0;
  //long j;

  // check whether there are entries;
  // if not, return a dummy structure
  if (ap_p==NULL)
    {
      dimensions.resolution=0.0;

      dimensions.xstart=0.0;
      dimensions.ystart=0.0;

      dimensions.xsize=0;
      dimensions.ysize=0;

      return dimensions;
    }

  // reset the pixel counter
  npixel=0;

  // go over each pixel
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {

      // Skip the pixel if it was not actually used
      if (fabs(cur_p->dist)>width+.5)
	continue;

      // search for minimum/maximum in wavelength
      min_lam = MIN (min_lam, cur_p->lambda);
      max_lam = MAX (max_lam, cur_p->lambda);

      // search for minimum/maximum in crossdispersion direction
      min_dist = MIN (min_dist, cur_p->dist);
      max_dist = MAX (max_dist, cur_p->dist);

      // search for minimum/maximum in dispersion
      min_dlam = MIN (min_dlam, cur_p->dlambda);
      max_dlam = MAX (max_dlam, cur_p->dlambda);

      // enhance the pixel counter
      npixel++;
    }

  // in case there were no valid pixels,
  // return a dummy structure
  if (!npixel)
    {
      dimensions.resolution=0.0;

      dimensions.xstart=0.0;
      dimensions.ystart=0.0;

      dimensions.xsize=0;
      dimensions.ysize=0;

      return dimensions;
    }

  // fill the resolution value
  // if the drizzle resolution is defined in the
  // configuration file and it is a first order beam
  if (conf->drz_resol && !beamID)
    // take the drizzle value from the config file
    dimensions.resolution = (double)conf->drz_resol;
  // otherwise
  else
    // take the average resoution
    dimensions.resolution = (max_dlam+min_dlam)/2.0;

  // round up and off in crossdispersion direction
  min_dist = floor(min_dist);
  max_dist = ceil(max_dist);

  // round up and off in dispersion direction
  min_xi = floor(min_lam/dimensions.resolution);
  max_xi = ceil(max_lam/dimensions.resolution);

  // derive and store the stamp image dimensions
  dimensions.xsize = max_xi-min_xi+2;
  dimensions.ysize = max_dist-min_dist+2;

  // store the start values
  dimensions.xstart = min_xi;
  dimensions.ystart = min_dist;

  stp_min->x = min_xi * dimensions.resolution;
  stp_min->y = min_dist;

  // return the structure
  return dimensions;
}

/**
 * Function: get_quad_from_pixel
 * The subroutine creates a quadrangle for a PET pixel
 * in the coordinate system of a drizzled stam image.
 * The quadrangle later serves as an input to the boxer
 * routine to resample the PET pixel.
 * Form the math point of view, the generation
 * of the quadrange corresponds to executing an affine
 * transformation of the pixel (x,y) corners into
 * the coordinate system spanned by the wavelength
 * and the crossdispersion direction.
 *
 * Parameters:
 * @param cur_p     - the PET pixel to construct the quadrangle for
 * @param dimension - the dimensions of the stamp image
 *
 * Returns:
 * @return quad - the filled quadrangle sturcture
 */
quadrangle
get_quad_from_pixel(const ap_pixel *cur_p, const double orient,
		    const drzstamp_dim dimension)
{
  quadrangle quad;

  double dxi, ddist;
  double x, y;

  double phi_1, phi_2;
  double cos_phi_1, sin_phi_1, tan_phi_2;
  double term_1, term_2;

  // nomenclature: see contamination.dvi
  phi_1 = orient - 1.5707963267948966;  // crossdispersion direction corresponds to beta; numeric=90deg in rad
  phi_2 = cur_p->dxs - orient + 1.5707963267948966; // traceangle corresponds to alpha

  // nomenclature: see contamination.dvi
  cos_phi_1 = cos(phi_1);
  sin_phi_1 = sin(phi_1);
  tan_phi_2 = tan(phi_2);

  // nomenclature: see contamination.dvi
  term_1 = cos_phi_1*tan_phi_2 + sin_phi_1;
  term_2 = cos_phi_1           - sin_phi_1*tan_phi_2;

  // Bottom left corner (-.5, -.5):
  // get the coos of that corner in the stamp image coo system
  x = -0.5;
  y = -0.5;
  dxi   = +x*cos_phi_1 + y*sin_phi_1;
  ddist = -x*term_1    + y*term_2;
  /*** IMPORTANT:
   * the division by "cos(cur_p->dxs)" in the equations below is
   * is necessary to cover the whole lambda-crossdisp plane.
   * However then the counts are not anymore preserved.
   * This must be taken into account in the calling routine!!
   **/
  quad.x[0] = (cur_p->lambda + cur_p->dlambda*dxi/cos(cur_p->dxs))/dimension.resolution - dimension.xstart;
  quad.y[0] = cur_p->dist + ddist - dimension.ystart;
  //  fprintf(stdout, "%f %f\n", quad.x[0], quad.y[0]);

  // Top left corner (-.5, +.5):
  // get the coos of that corner in the stamp image coo system
  x = -0.5;
  y = +0.5;
  dxi   = +x*cos_phi_1 + y*sin_phi_1;
  ddist = -x*term_1    + y*term_2;
  //  quad.x[1] = cur_p->xi + dxi/cos(cur_p->dxs);
  quad.x[1] = (cur_p->lambda + cur_p->dlambda*dxi/cos(cur_p->dxs))/dimension.resolution - dimension.xstart;
  quad.y[1] = cur_p->dist + ddist - dimension.ystart;

  // Top right corner (+.5, +.5):
  // get the coos of that corner in the stamp image coo system
  x = +0.5;
  y = +0.5;
  dxi   = +x*cos_phi_1 + y*sin_phi_1;
  ddist = -x*term_1    + y*term_2;
  quad.x[2] = (cur_p->lambda + cur_p->dlambda*dxi/cos(cur_p->dxs))/dimension.resolution - dimension.xstart;
  quad.y[2] = cur_p->dist + ddist - dimension.ystart;

  // Bottom right corner (+.5, -.5):
  // get the coos of that corner in the stamp image coo system
  x = +0.5;
  y = -0.5;
  dxi   = +x*cos_phi_1 + y*sin_phi_1;
  ddist = -x*term_1    + y*term_2;
  quad.x[3] = (cur_p->lambda + cur_p->dlambda*dxi/cos(cur_p->dxs))/dimension.resolution - dimension.xstart;
  quad.y[3] = cur_p->dist + ddist - dimension.ystart;

  // get the maximum and minimum of the quadrangle
  // in each dimension
  quad.xmax = MAX(quad.x[3],MAX(quad.x[2],MAX(quad.x[1],quad.x[0])));
  quad.xmin = MIN(quad.x[3],MIN(quad.x[2],MIN(quad.x[1],quad.x[0])));
  quad.ymax = MAX(quad.y[3],MAX(quad.y[2],MAX(quad.y[1],quad.y[0])));
  quad.ymin = MIN(quad.y[3],MIN(quad.y[2],MIN(quad.y[1],quad.y[0])));

  return quad;
}

/**
    Produce a gsl array containing a 'drizzled aperture'. That is,
    an image of the spectum showing count on an xi,dist grid instead
    of the traditional image grid x,y. Returns NULL is input object is NULL.
	[modifies ap_p!!]
    @param ap_p a pointer to a -1 terminated ap_pixel array
    @param width extraction width used
	@param resampwidth A per pixel of output drizzled spectra
    @return a pointer to a newly allocated gsl_matrix containing the
    rectified spectrum
*/
gsl_matrix *
drizzled_stamp_img_orig (const  ap_pixel * const ap_p, float width, aperture_conf *conf)
{
  const ap_pixel *cur_p;

  double min_xi   = 1e32;
  double max_xi   = -1e32;
  double min_dist = 1e32;
  double max_dist = -1e32;
  double min_dlam = 1e32;
  double max_dlam = -1e32;

  double resampwidth = 0.0;

  gsl_matrix *res;

  long xsize,ysize,i,j,n=0;

  double xi;

  if (ap_p==NULL) {
    /* Create a dummy stamp image */
    res = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(res, 0.0);
    return res;
  }

  i=0;
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist)>width+.5) continue; /* Skip this pixel if it was not actually used */
      i++;

      xi = cur_p->lambda;

      min_xi = MIN (min_xi, xi);
      max_xi = MAX (max_xi, xi);

      min_dist = MIN (min_dist, cur_p->dist);
      max_dist = MAX (max_dist, cur_p->dist);

      min_dlam = MIN (min_dlam, cur_p->dlambda);
      max_dlam = MAX (max_dlam, cur_p->dlambda);

      n++;
    }

  if (conf->drz_resol)
    {
      resampwidth = (double)conf->drz_resol;
    }
  else
    {
      resampwidth = (max_dlam+min_dlam)/2.0;
    }

  fprintf(stdout, "resamplewidth: %f\n", resampwidth);

  if (i==0) {
    /* Create a dummy stamp image */
    res = gsl_matrix_alloc(10,10);
    /* Fill stamp with 0.0 values */
    gsl_matrix_set_all(res, 0.0);
    return res;
  }

  min_dist = floor(min_dist);
  max_dist = floor(max_dist);
  min_xi = floor(min_xi/resampwidth);
  max_xi = floor(max_xi/resampwidth);

  xsize = max_xi-min_xi+2;
  ysize = max_dist-min_dist+2;

  if (n>0)
    {
      res = gsl_matrix_alloc(xsize,ysize);
    } else {
      /* Create a dummy stamp image */
      res = gsl_matrix_alloc(10,10);
      /* Fill stamp with 0.0 values */
      gsl_matrix_set_all(res, 0.0);
      return res;
    }

  res = gsl_matrix_alloc(xsize,ysize);
  /* Fill stamp with NaN values */
  gsl_matrix_set_all(res, GSL_NAN);

  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      int ii,jj;
      double x,y,xp,yp,arr;
      double xx[4],yy[4];
      int stpi,stpj;
      double stpc;

      /* Skip this pixel if it was not actually used */
      if (fabs(cur_p->dist)>width+.5)
	continue;

      //cur_p->dxs=0.;
      //cur_p->xi = 100.5 + min_xi;
      //cur_p->dist = 10. + min_dist;
      xi = cur_p->lambda/resampwidth;

      i = (long) floor(xi - min_xi+.5);
      j = (long) floor(cur_p->dist - min_dist+.5);

      /* The center of this PET entry should fall in pixel (i.j) */
      /* Bottom left corner (-.5, -.5) */
      x = -0.5;
      y = -0.5;
      xp =  x*cos(cur_p->dxs) + y*sin(cur_p->dxs);
      yp = -x*sin(cur_p->dxs) + y*cos(cur_p->dxs);
      xx[0] = xp+xi - min_xi;
      yy[0] = yp+cur_p->dist - min_dist;

      /* Top left corner (-.5, +.5) */
      x = -0.5;
      y = +0.5;
      xp =  x*cos(cur_p->dxs) + y*sin(cur_p->dxs);
      yp = -x*sin(cur_p->dxs) + y*cos(cur_p->dxs);
      xx[1] = xp+ xi - min_xi;
      yy[1] = yp+cur_p->dist - min_dist;

      /* Top right corner (+.5, +.5) */
      x = +0.5;
      y = +0.5;
      xp =  x*cos(cur_p->dxs) + y*sin(cur_p->dxs);
      yp = -x*sin(cur_p->dxs) + y*cos(cur_p->dxs);
      xx[2] = xp+ xi - min_xi;
      yy[2] = yp+cur_p->dist - min_dist;

      /* Bottom right corner (+.5, -.5) */
      x = +0.5;
      y = -0.5;
      xp =  x*cos(cur_p->dxs) + y*sin(cur_p->dxs);
      yp = -x*sin(cur_p->dxs) + y*cos(cur_p->dxs);
      // fprintf(stderr,"1 %f %f\n",cur_p->xi - min_xi,cur_p->dist - min_dist);
      xx[3] = xp+ xi - min_xi;
      yy[3] = yp+cur_p->dist - min_dist;

      for (ii=-1;ii<=1;ii++) {
	for (jj=-1;jj<=1;jj++) {
	  //fprintf(stderr,"PET: XI: %f DIST: %f DXS: %f cos:%f\n",cur_p->xi,cur_p->dist,cur_p->dxs,cos(cur_p->dxs));
	  arr = boxer((int) floor(xi - min_xi)+ii,(int) floor(cur_p->dist - min_dist)+jj,xx,yy);

	  //fprintf(stderr,"STP: i:%d j:%d arr: %f\n",(int) floor(cur_p->xi - min_xi)+ii,(int) floor(cur_p->dist - min_dist)+jj,arr);
	  stpi = (int) floor(xi - min_xi)+ii;
	  stpj = (int) floor(cur_p->dist - min_dist)+jj;
	  if ( (stpi>=xsize)||(stpi<0)||(stpj>=ysize)||(stpj<0) ) continue;
	  stpc = gsl_matrix_get(res,stpi,stpj);
	  if (isnan(stpc) && (arr!=0.0)) stpc = 0.0;
	  gsl_matrix_set(res,stpi,stpj,stpc+arr*cur_p->count);
	}
      }

    }

  return res;
}

/**
 * Function: interpolate_over_NaN
 * Function to interpolate over and replace NaN values in a rectified
 * stamp image. This is done by interpolating in the vertical direction.
 * The original gsl_matrix is free'd and is replaced by a new gsl_matrix pointer.
 * Does nothing if input data is NULL.
 *
 * Parameters:
 * @param data - the gsl_matrix where NaN values are to be interpolated over
 */
void interpolate_over_NaN (gsl_matrix *data)
{
  double *x,*y;
  int i,j,n;
  gsl_interp_accel *acc;
  gsl_spline *spline ;

  if (data==NULL) return;

  x = malloc(sizeof(double)*data->size2*data->size1);
  y = malloc(sizeof(double)*data->size2*data->size1);

  for (i=0;i<(int)data->size1;i++) {
    n = 0;
    for (j=0;j<(int)data->size2;j++) {
      if ( !isnan(gsl_matrix_get(data,i,j)) )  {
	       x[n] = j;
	       y[n] = gsl_matrix_get(data,i,j);
	       n++;
      }
    }
    //if (n<data->size2) fprintf(stderr,"%d elements non-NaN found out of %d.\n",n,data->size2);
    if (n<3) continue;

    acc = gsl_interp_accel_alloc ();
    spline = gsl_spline_alloc (gsl_interp_cspline, n);

    /* Interpolating */
    gsl_spline_init (spline, x, y, n);

    for (j=0;j<(int)data->size2;j++) {
      double xi = j;
      //double yi = gsl_spline_eval(spline, xi, acc);
      // revert to older extrapolation allowing functionality
      double yi = spline->interp->type->eval(spline->interp->state, spline->x, spline->y, spline->interp->size, x[j], acc, &y[j]);
      //if (n<data->size2)  fprintf(stderr,"%d %g => %d %g\n",j,gsl_matrix_get(data,i,j),j,yi);
      gsl_matrix_set(data, i, j, yi);
    }
    gsl_spline_free (spline);
    gsl_interp_accel_free(acc);
  }

  free(x);
  free(y);
}

/*
 * Function: free_drzprep
 * The function releases the memory allocated
 * to the various components of a drzprep structure
 *
 * Parameters:
 * @param: drzprep_stamps - the stamp image to free
 */
void
free_drzprep(drzprep *drzprep_stamps)
{
  // release the individual matrixes
  gsl_matrix_free(drzprep_stamps->counts);
  gsl_matrix_free(drzprep_stamps->error);
  gsl_matrix_free(drzprep_stamps->cont);
  if (drzprep_stamps->model)
    gsl_matrix_free(drzprep_stamps->model);
  if (drzprep_stamps->vari)
    gsl_matrix_free(drzprep_stamps->vari);

  // release the struct
  free(drzprep_stamps);
}

/**
 * Function: free_stamp_img
 * Function to free the memeory allocated by a stamp image
 *
 * Parameters:
 * @param rstamp - the stamp image
 */
void free_stamp_img(gsl_matrix *rstamp)
{
  // simply free it and "basta"!
  gsl_matrix_free(rstamp);
}

/**
 * Function: free_drzstamp
 * Function to free the memeory allocated by a
 * drizzled stamp image.
 *
 * Parameters:
 * @param rstamp  - the structure for the drizzled stamp image
 */
void free_drzstamp(drzstamp *rstamp)
{
  // release each component
  gsl_matrix_free(rstamp->counts);
  gsl_matrix_free(rstamp->weight);

  // release the rest
  free(rstamp);
}

/**
  Find the indices of pixels corresponding to the trace (i.e. the
  one with the smallest dist in each column).  If we get
  traces with a slope of significantly more than one, a similar
  function would have to be written operating on rows.

  @param ap_p the aperture pixel table
  @return a gsl_matrix with the indices of the trace pixels in ap_p
*/
gsl_vector_int *
get_trace_inds (const ap_pixel * const ap_p)
{
  const ap_pixel *cur_p;
  int trace_resolution = 10000;	/* Number of xi bins in which to follow the
				   trace */
  gsl_vector_int *trace_inds = gsl_vector_int_alloc (trace_resolution);
  gsl_vector_int *trace_inds2;
  int n, i;
  double distmin;

  gsl_vector_int_set_all (trace_inds, -1);

  /* Find the minimum pixel-to-trace distance in this aperture */
  distmin = 1e32;
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      distmin = MIN (distmin, fabs(cur_p->dist));
    }

  n = 0;
  for (cur_p = ap_p; cur_p->p_x != -1; cur_p++)
    {
      if (fabs(cur_p->dist) < (distmin + .25))
	{
	  gsl_vector_int_set (trace_inds, n, cur_p - ap_p);
	  n++;
	}
    }
  trace_inds2 = gsl_vector_int_alloc (n);
  n = 0;
  for (i = 0; i < (int)trace_inds->size; i++)
    {
      if (gsl_vector_int_get (trace_inds, i) != -1)
	{
	  gsl_vector_int_set (trace_inds2, n,
			      gsl_vector_int_get (trace_inds, i));
	  n++;
	}
    }

  return trace_inds2;
}


#include <gsl/gsl_math.h>
#include "gsl_cblas.h"
#include "cblas.h"

double
cblas_ddot (const int N, const double *X, const int incX, const double *Y,
	    const int incY)
{
#define INIT_VAL  0.0
#define ACC_TYPE  double
#define BASE double
#include "source_dot_r.h"
#undef ACC_TYPE
#undef BASE
#undef INIT_VAL
}


/*! \file getdata.h
    \brief rのメッシュと、そのメッシュにおける電子密度を与えるクラスの宣言

    Copyright © 2015-2019 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#ifndef _GETRMESHANDRHO_H_
#define _GETRMESHANDRHO_H_

#pragma once

#include "../utility/property.h"
#include <cstdint>          // for std::int32_t, std::uint32_t
#include <memory>           // for std::unique_ptr
#include <string>           // for std::string
#include <gsl/gsl_spline.h> // for gsl_interp_accel, gsl_interp_accel_free, gsl_spline, gsl_spline_free

namespace getdata {
    using namespace utility;

    //! A class.
    /*!
        rのメッシュと、そのメッシュにおける動径波動関数を与えるクラス
    */
    class GetData final {
        // #region 列挙型

    public:
        //!  A enumerated type
        /*!
            密度か動径波動関数かを表す列挙型
        */
        enum class Rho_Wf_type {
            // 密度
            RHO,
            // 動径波動関数
            WF
        };

        // #endregion 列挙型

        // #region コンストラクタ・デストラクタ

        //! A constructor.
        /*!
            唯一のコンストラクタ
            \param filename rのメッシュと、そのメッシュにおける電子密度が記録されたデータファイル名
        */
        GetData(std::string const & filename);

        //! A destructor.
        /*!
            デフォルトデストラクタ
        */
        ~GetData() = default;

        // #endregion コンストラクタ・デストラクタ

        // #region メンバ関数

        //!  A public member function (const).
        /*!
            関数の値を返す
            \param r rの値
            \return 関数の値
        */
        double operator()(double r) const;

        //!  A public member function (const).
        /*!
            関数の微分の値を返す
            \param r rの値
            \return 関数の微分の値
        */
        double dphidr(double r) const;

        // #endregion メンバ関数

        // #region プロパティ

        //! A property.
        /*!
            元素名
        */
        Property<std::string const&> Atomname;

        //!  A property.
        /*!
            方位量子数へのプロパティ
        */
        Property<std::uint32_t> const L;

        //!  A property.
        /*!
            主量子数へのプロパティ
        */
        Property<std::int32_t> const N;

        //!  A property.
        /*!
            軌道へのプロパティ
        */
        Property<std::string> const Orbital;

        //! A property.
        /*!
            波動関数の最大値のプロパティ
        */
        Property<double> const Phimax;

        //! A property.
        /*!
            波動関数が最大値を取るときのrへのプロパティ
        */
        Property<double> const R2rhomaxr;

        //!  A private member variable.
        /*!
            解く方程式のタイプへのプロパティ
        */
        Property<GetData::Rho_Wf_type> const Rho_wf_type;

        //! A property.
        /*!
            rのメッシュの最大値のプロパティ
        */
        Property<double> const R_meshmax;

        //! A property.
        /*!
            rのメッシュの最小値のプロパティ
        */
        Property<double> const R_meshmin;

        // #endregion プロパティ

        // #region メンバ変数

    private:
        //! A private member variable.
        /*!
            gsl_interp_accelへのスマートポインタ
        */
        std::unique_ptr<gsl_interp_accel, decltype(&gsl_interp_accel_free)> const acc_;

        //!  A private member variable.
        /*!
            元素名
        */
        std::string atomname_;

        //!  A private member variable.
        /*!
            方位量子数
        */
        std::uint32_t l_;

        //!  A private member variable.
        /*!
            主量子数
        */
        std::int32_t n_;

        //!  A private member variable.
        /*!
            軌道
        */
        std::string orbital_;

        //!  A private member variable.
        /*!
            波動関数φ
        */
        std::vector<double> phi_;

        //!  A private member variable.
        /*!
            波動関数の最大値
        */
        double phimax_;

        //!  A private member variable.
        /*!
            波動関数が最大値を取るときのr
        */
        double r2rhomaxr_;

        //!  A private member variable.
        /*!
            解く方程式のタイプ
        */
        Rho_Wf_type rho_wf_type_;

        //!  A private member variable.
        /*!
            rのメッシュの最大値
        */
        double r_meshmax_;

        //!  A private member variable.
        /*!
            rのメッシュの最小値
        */
        double r_meshmin_;

        //!  A private member variable.
        /*!
           rのメッシュ
        */
        std::vector<double> r_mesh_;

        //! A private member variable.
        /*!
            gsl_interp_typeへのスマートポインタ
        */
        std::unique_ptr<gsl_spline, decltype(&gsl_spline_free)> spline_;

        // #endregion メンバ変数

        // #region 禁止されたコンストラクタ・メンバ関数
    public:

        //! A public constructor (deleted).
        /*!
            デフォルトコンストラクタ（禁止）
        */
        GetData() = delete;

        //! A public copy constructor (deleted).
        /*!
            コピーコンストラクタ（禁止）
            \param dummy コピー元のオブジェクト（未使用）
        */
        GetData(GetData const & dummy) = delete;

        //! A public member function (deleted).
        /*!
            operator=()の宣言（禁止）
            \param dummy コピー元のオブジェクト（未使用）
            \return コピー元のオブジェクト
        */
        GetData& operator=(GetData const & dummy) = delete;

        // #endregion 禁止されたコンストラクタ・メンバ関数
    };

    // #region メンバ関数

    inline double GetData::operator()(double r) const
    {
        return gsl_spline_eval(spline_.get(), r, acc_.get());
    }

    inline double GetData::dphidr(double r) const
    {
        return gsl_spline_eval_deriv(spline_.get(), r, acc_.get());
    }

    // #endregion メンバ関数
}

#endif  // _GETRMESHANDRHO_H_


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef delaunay_3673850c_175d_4077_bef2_fc69e19e5cf8_h
#define delaunay_3673850c_175d_4077_bef2_fc69e19e5cf8_h

#include <gslib/std.h>
#include <gslib/math.h>
#include <ariel/config.h>

/*
 * A implementation of the Delaunay Triangulation and the Constrained Delaunay Triangulation.
 * The original implementation was:
 * https://code.google.com/archive/p/cgprojects09/
 * further more, modifications were done to achieve the constrained version, visit:
 * http://www.geom.uiuc.edu/~samuelp/del_project.html
 */

__ariel_begin__

class ariel_export dt_joint
{
protected:
    vec2            _point;
    void*           _binding;

public:
    dt_joint() { _binding = nullptr; }
    dt_joint(const vec2& p, void* b) { _point = p, _binding = b; }
    void set_point(const vec2& p) { _point = p; }
    const vec2& get_point() const { return _point; }
    void set_binding(void* b) { _binding = b; }
    void* get_binding() const { return _binding; }
};

class ariel_export dt_edge
{
protected:
    dt_joint*       _org;
    dt_edge*        _prev;
    dt_edge*        _next;
    dt_edge*        _symmetric;
    bool            _constraint;
    bool            _checked;

public:
    dt_edge();
    void set_org(dt_joint* j) { _org = j; }
    dt_joint* get_org() const { return _org; }
    void set_dest(dt_joint* j) { _symmetric->set_org(j); }
    dt_joint* get_dest() const { return _symmetric->get_org(); }
    void set_prev_edge(dt_edge* e) { _prev = e; }
    dt_edge* get_prev_edge() const { return _prev; }
    void set_next_edge(dt_edge* e) { _next = e; }
    dt_edge* get_next_edge() const { return _next; }
    void set_symmetric(dt_edge* e) { _symmetric = e; }
    dt_edge* get_symmetric() const { return _symmetric; }
    const vec2& get_org_point() const { return _org->get_point(); }
    const vec2& get_dest_point() const { return get_dest()->get_point(); }
    void* get_org_binding() const { return _org->get_binding(); }
    void* get_dest_binding() const { return get_dest()->get_binding(); }
    dt_edge* get_org_next() const { return _prev->get_symmetric(); }
    dt_edge* get_org_prev() const { return _symmetric->get_next_edge(); }
    dt_edge* get_dest_next() const { return _symmetric->get_prev_edge(); }
    dt_edge* get_dest_prev() const { return _next->get_symmetric(); }
    dt_edge* get_left_next() const { return _next; }
    dt_edge* get_left_prev() const { return _prev; }
    dt_edge* get_right_next() const { return _symmetric->get_next_edge()->get_symmetric(); }
    dt_edge* get_right_prev() const { return _symmetric->get_prev_edge()->get_symmetric(); }
    void set_constraint(bool b) { _constraint = b; }
    bool is_constraint() const { return _constraint; }
    void set_checked(bool c) { _checked = c; }
    bool is_checked() const { return _checked; }
    bool is_outside_boundary() const;
    bool is_boundary() const;
};

struct ariel_export dt_edge_range
{
    dt_edge*        left;
    dt_edge*        right;

    dt_edge_range()  { left = right = nullptr; }
};

typedef list<dt_joint> dt_input_joints;
typedef list<dt_joint*> dt_joint_ptrs;
typedef vector<dt_joint*> dt_joints;
typedef unordered_set<dt_edge*> dt_edges;
typedef vector<dt_edge*> dt_edge_list;

class ariel_export delaunay_triangulation
{
public:
    delaunay_triangulation() {}
    ~delaunay_triangulation() { clear(); }
    void initialize(dt_input_joints& inputs);
    void run();
    void clear();
    dt_edge* add_constraint(const vec2& p1, const vec2& p2);
    void trim(dt_edge_list& edges);
    void set_range_left(dt_edge* e) { _edge_range.left = e; }
    void tracing() const;
    void trace_heuristically() const;
    void trace_mel() const;

protected:
    dt_joints       _sorted_joints;
    dt_edge_range   _edge_range;
    dt_edges        _edge_holdings;

protected:
    bool is_in_range(int begin, int end, dt_joint* joint);
    dt_edge_range delaunay(int begin, int end);
    dt_edge* create_edge_pair();
    dt_edge* connect_edges(dt_edge* e1, dt_edge* e2);
    void destroy_edge_pair(dt_edge* e);
    void shrink_triangulate(dt_edge* cut);
    void shrink_recursively(dt_edge_list& strips, dt_edge_list& temps);
    void collect_trim_edges(dt_edge_list& edges, dt_edge* e);

    template<class _visit>
    static void traverse_triangles_recursively(dt_edge* e, _visit visit)
    {
        assert(e);
        if(e->is_checked())
            return;
        auto do_visit = [&visit](dt_edge* e) {
            assert(e);
            auto* e1 = e->get_prev_edge();
            auto* e2 = e->get_next_edge();
            assert(e1 && e2 && !e1->is_checked() && !e2->is_checked());
            void* b1 = e->get_org_binding();
            void* b2 = e->get_dest_binding();
            void* b3 = e1->get_org_binding();
            void* b4 = e2->get_dest_binding();
            if(b3 != b4)    /* invalid triangle */
                return;
            auto& p1 = e->get_org_point();
            auto& p2 = e->get_dest_point();
            auto& p3 = e1->get_org_point();
            if(!is_concave_angle(p1, p2, p3))     /* ccw! */
                return;
            bool b[3];
            b[0] = e->is_boundary();
            b[1] = e1->is_boundary();
            b[2] = e2->is_boundary();
            visit(b1, b2, b3, b);
            e->set_checked(true);
            e1->set_checked(true);
            e2->set_checked(true);
        };
        do_visit(e);
        traverse_triangles_recursively(e->get_symmetric(), visit);
        traverse_triangles_recursively(e->get_prev_edge()->get_symmetric(), visit);
        traverse_triangles_recursively(e->get_next_edge()->get_symmetric(), visit);
    }
    template<class _visit>
    static void traverse_triangles_flatly(dt_edge* e, _visit visit)
    {
        vector<dt_edge*> edges;
        edges.push_back(e);
        while(!edges.empty()) {
            e = edges.back();
            edges.pop_back();
            if(e->is_checked())
                continue;
            auto do_visit = [&visit](dt_edge* e) {
                assert(e);
                auto* e1 = e->get_prev_edge();
                auto* e2 = e->get_next_edge();
                assert(e1 && e2 && !e1->is_checked() && !e2->is_checked());
                void* b1 = e->get_org_binding();
                void* b2 = e->get_dest_binding();
                void* b3 = e1->get_org_binding();
                void* b4 = e2->get_dest_binding();
                if(b3 != b4)    /* invalid triangle */
                    return;
                auto& p1 = e->get_org_point();
                auto& p2 = e->get_dest_point();
                auto& p3 = e1->get_org_point();
                if(!is_concave_angle(p1, p2, p3))     /* ccw! */
                    return;
                bool b[3];
                b[0] = e->is_boundary();
                b[1] = e1->is_boundary();
                b[2] = e2->is_boundary();
                visit(b1, b2, b3, b);
                e->set_checked(true);
                e1->set_checked(true);
                e2->set_checked(true);
            };
            do_visit(e);
            edges.push_back(e->get_symmetric());
            edges.push_back(e->get_prev_edge()->get_symmetric());
            edges.push_back(e->get_next_edge()->get_symmetric());
        }
    }

public:
    template<class _visit>
    void traverse_triangles(_visit visit)
    {
        traverse_triangles_flatly(_edge_range.left, visit);
    }
    void reset_traverse();
};

__ariel_end__

#endif


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <mpi.h>
#include "compearth.h"
#include "parmt_mtsearch.h"
#ifdef PARMT_USE_INTEL
#include <mkl_lapacke.h>
#include <mkl_cblas.h>
#else
#include <lapacke.h>
#include <cblas.h>
#endif
#include "iscl/array/array.h"
#include "iscl/memory/memory.h"

/*
int main_temp()
{
    double *betas, *gammas, *kappas, *M0s, *mts, *sigmas, *thetas;
    int i, ierr, nmt;
    const int ng = 29;
    const int nb = 29;
    const int nk = 6;
    const int ns = 6;
    const int nt = 6;
    const int nm = 1;
    const int ldm = 8;
    const double oneDeg = M_PI/180.0;
    const double betaMin = 0.0 + oneDeg;
    const double betaMax = M_PI - oneDeg;
    const double gammaMin =-M_PI/6.0 + oneDeg;
    const double gammaMax = M_PI/6.0 - oneDeg;
    const double kappaMin = 0.0 + oneDeg;
    const double kappaMax = 2.0*M_PI - oneDeg;
    const double thetaMin = 0.0 + oneDeg;
    const double thetaMax = 0.5*M_PI - oneDeg;
    const double sigmaMin =-0.5*M_PI + oneDeg;
    const double sigmaMax = 0.5*M_PI - oneDeg;
    nmt = nm*nb*ng*nk*ns*nt;
    mts = memory_calloc64f(nmt*ldm); //(double *)calloc((size_t) (nmt*ldm), sizeof(double));
    M0s = memory_calloc64f(nm); //(double *)calloc((size_t) nm, sizeof(double));
    betas = memory_calloc64f(nb); //(double *)calloc((size_t) nb, sizeof(double));
    gammas = memory_calloc64f(ng); //(double *)calloc((size_t) ng, sizeof(double));
    kappas = memory_calloc64f(nk); //(double *)calloc((size_t) nk, sizeof(double));
    sigmas = memory_calloc64f(ns); //(double *)calloc((size_t) ns, sizeof(double));
    thetas = memory_calloc64f(nt); //(double *)calloc((size_t) nt, sizeof(double));

    ierr = array_linspace64f_work(1.0/sqrt(2.0), 1.0/sqrt(2.0), nm, M0s);
    ierr = array_linspace64f_work(betaMin+0.001,  betaMax-0.001,  nb, betas);
    ierr = array_linspace64f_work(gammaMin, gammaMax, ng, gammas);
    ierr = array_linspace64f_work(kappaMin, kappaMax, nk, kappas);
    ierr = array_linspace64f_work(thetaMin, thetaMax, nt, thetas);
    ierr = array_linspace64f_work(sigmaMin, sigmaMax, ns, sigmas);
    // avoid warnings on theta in tt2cmt
    ierr = gridSearch_discretizeMT64f(ng, gammas, nb, betas,  nm, M0s,
                                      nk, kappas, nt, thetas, ns, sigmas,
                                      ldm, nmt, mts);

double Mdc[6], lam[3], U[6], theta;
    compearth_tt2cmt(0.0, 0.0, 1./sqrt(2.),
                     0.0, 0.1, 0.0, Mdc, lam, U);

    FILE *ofl = fopen("mts.txt", "w");
    for (i=0; i<nmt; i++)
    {
        compearth_angleMT(1, Mdc, &mts[ldm*i], &theta);

        fprintf(ofl, "%f %f %f %f %f %f %f\n", //i+1,
                mts[ldm*i+0], mts[ldm*i+1], mts[ldm*i+2],
                mts[ldm*i+3], mts[ldm*i+4], mts[ldm*i+5], theta);
    }
    fclose(ofl);

    free(mts);
    free(M0s);
    free(betas);
    free(gammas);
    free(kappas);
    free(sigmas);
    free(thetas);
    return 0;
}
*/
//============================================================================//
/*!
 * @brief Provides a cell based discretization of the moment tensor space.
 *
 * @param[in] nb          Number of colatitudes.
 * @param[in] betaLower   Lower colatitude in grid search [0, betaUpper).
 * @param[in] betaUpper   Upper colatitude in grid search (betaLower, pi].
 * @param[in] ng          Number of longitudes.
 * @param[in] gammaLower  Lower longitude in grid search [-pi/6, gammaUpper).
 * @param[in] gammaUpper  Upper longitude in grid search (gammaLower, pi/6].
 * @param[in] nk          Number of strikes in grid search.
 * @param[in] kappaLower  Lower strike in grid search [0, kappaUpper).
 * @param[in] kappaUpper  Upper strike in grid search (kappaLower, 2*pi].
 * @param[in] ns          Number of slips in grid search.
 * @param[in] sigmaLower  Lower slip angle in grid search [-pi, sigmaUpper).
 * @param[in] sigmaUpper  Upper slip angle in grid search (sigmaUpper, pi].
 * @param[in] nt          Number of dips in grid search.
 * @param[in] thetaLower  Lower dip in grid search [0,dipUpper).
 * @param[in] thetaUpper  Upper dip in grid search (dipLower, pi/2].
 * @param[in] nm          Number of magnitudes.
 * @param[in] m0Lower     Lower scalar moment in grid search (Newton-meters).
 * @param[in] m0Upper     Upper scalar moment in grid search (Newton-meters).
 * @param[in] luseLog     If true then the the scalar moments will be
 *                        discretized on a log scale.
 *
 * @param[out] betas      Colatitudes (radians) at cell centers [nb].
 * @param[out] gammas     Longitudes (radians) at cell centers [ng].
 * @param[out] kappas     Strike angles (radians) at cell centers [nk].
 * @param[out] sigmas     Slip angles (radians) at cell centers [ns].
 * @param[out] thetas     Dip angles (radians) at cell centers [nt].
 * @param[out] M0s        Scalar moments (Newton-meters) in grid search [nm].
 *
 * @author Ben Baker
 *
 * @copyright ISTI distributed under Apache 2
 *
 */
int parmt_discretizeCells64f(
    const int nb, const double betaLower, const double betaUpper,
    const int ng, const double gammaLower, const double gammaUpper,
    const int nk, const double kappaLower, const double kappaUpper,
    const int ns, const double sigmaLower, const double sigmaUpper,
    const int nt, const double thetaLower, const double thetaUpper,
    const int nm, const double m0Lower, const double m0Upper,
    const bool luseLog,
    double **betas,  double **gammas, double **kappas,
    double **sigmas, double **thetas, double **M0s)
{
    const char *fcnm = "parmt_discretizeCells64f\0";
    double *mwl, *u, *v, *h;
    double du, du2, dv, dv2, dh, dh2, dk, dk2, ds, ds2,
           hLower, hUpper, mwll, mwul, uLower, uUpper, vLower, vUpper;
    int ierr;
    const double two = 2.0;
    // Initialize and do some basic error checks
    u = NULL;
    v = NULL;
    h = NULL;
    if (nb < 1 || ng < 1 || nk < 1 || ns < 1 || nt < 1 || nm < 1)
    {
        if (nb < 1){printf("%s: no betas\n", fcnm);}
        if (ng < 1){printf("%s: no gammas\n", fcnm);}
        if (nk < 1){printf("%s: no kappas\n", fcnm);}
        if (ns < 1){printf("%s: no sigmas\n", fcnm);}
        if (nt < 1){printf("%s: no thetas\n", fcnm);}
        if (nm < 1){printf("%s: no magnitudes\n", fcnm);}
        return -1;
    }
    // Discretize the moment tensor space in (u, v, h) space
    compearth_beta2u(1, &betaLower, &uLower);
    compearth_beta2u(1, &betaUpper, &uUpper);
    compearth_gamma2v(1, &gammaLower, &vLower);
    compearth_gamma2v(1, &gammaUpper, &vUpper);
    compearth_theta2h(1, &thetaLower, &hLower);
    compearth_theta2h(1, &thetaUpper, &hUpper);
    du = (uUpper - uLower)/(double) nb;
    dv = (vUpper - vLower)/(double) ng;
    dh =-(hUpper - hLower)/(double) nt; // negative sign makes +theta increasing
    dk = (kappaUpper - kappaLower)/(double) nk;
    ds = (sigmaUpper - sigmaLower)/(double) ns;
    du2 = du/two;
    dv2 = dv/two;
    dh2 = dh/two;
    u = array_linspace64f(uLower+du2, uUpper-du2, nb, &ierr);
    v = array_linspace64f(vLower+dv2, vUpper-dv2, ng, &ierr);
    h = array_linspace64f(hLower-dh2, hUpper+dh2, nt, &ierr);
    *betas  = memory_calloc64f(nb);
    *gammas = memory_calloc64f(ng);
    *thetas = memory_calloc64f(nt);
    //compearth_u2beta(nb, 20, 2, u, 1.e-6, *betas);
    compearth_u2beta(nb, u, *betas);
    compearth_v2gamma(ng, v, *gammas);
    compearth_h2theta(nt, h, *thetas);
    // discretize the magnitudes, kappa, and sigma
    dk2 = dk/two;
    ds2 = ds/two;
    if (!luseLog || nm == 1)
    {
        *M0s = array_linspace64f(m0Lower, m0Upper, nm, &ierr);
    }
    else
    {
        // TODO: I doubt this is correct because I don't understand how
        // lune volumes map to `spherical volumes'.  For simplicity I'll
        // use a shell but I think I should be looking at the `active
        // area' of the beta and gamma grid search.  In the interim I'll
        // use the volume of a spherical shell which is given by
        // \frac{4 \pi R^3}{3} where R is the radius (magnitude).
        // To get some form of uniform volume spacing I'll take the log
        // at the upper and lower limit and discretize evenly in logspace
        // then come back to M0.
        /*
        m0ll = 3.0*log(4.0*M_PI*m0Lower/3.0);
        m0ul = 3.0*log(4.0*M_PI*m0Upper/3.0);
        m0l = array_linspace64f(m0ll, m0ul, nm, &ierr);
        // Now solve for m0: 3/(4*pi)*exp(m0l/3)
        cblas_dscal(nm, 1.0/3.0, m0l, 1);         // m0l = m0l/3
        *M0s = array_exp64f(nm, m0l, &ierr);      // *M0 = exp(m0l/3)
        cblas_dscal(nm, 3.0/(4.0*M_PI), *M0s, 1); // *M0 = 3/(4*pi)exp(m0l/3)
        memory_free64f(&m0l);
        */
        // TODO: An alternative to doing math is just discretizing on a
        // the magnitude scale
        compearth_m02mw(1, CE_KANAMORI_1978, &m0Lower, &mwll);
        compearth_m02mw(1, CE_KANAMORI_1978, &m0Upper, &mwul);
        mwl = array_linspace64f(mwll, mwul, nm, &ierr);
        *M0s = memory_calloc64f(nm);
        ierr = compearth_mw2m0(nm, CE_KANAMORI_1978, mwl, *M0s);
        memory_free64f(&mwl);
    }
    *kappas = array_linspace64f(kappaLower+dk2, kappaUpper-dk2, nk, &ierr);
    *sigmas = array_linspace64f(sigmaLower+ds2, sigmaUpper-ds2, ns, &ierr);
    memory_free64f(&u);
    memory_free64f(&v);
    memory_free64f(&h);
    return ierr;
}

//============================================================================//
/*!
 * @brief Computes the moment tensors in the grid search.
 *        The grid search ordering is:
 *         Loop on magnitudes
 *          Loop on colatitudes
 *           Loop on longitudes
 *            Loop on strikes
 *             Loop on slips
 *              Loop on dips
 *
 * @param[in] comm    MPI communicator on which moment tensor will be split
 * @param[in] ng      number of longitudes
 * @param[in] gammas  longitudes (radians) on lune s.t.
 *                    \f$ \gamma \in [-\pi/6, \pi/6] \f$ [ng]
 * @param[in] nb      number of colatitudes
 * @param[in] betas   colatitudes (radians) on lune s.t.
 *                    \f$ \beta \in [0, \pi] \f$ [nb]
 * @param[in] nm      number of scalar moments
 * @param[in] M0s     scalar moments in Newton-meters [nm].
 *                    for a `unit' moment tensor choose M0 = 1/sqrt(2)
 * @param[in] nk      number of strike angles
 * @param[in] kappas  strike angles (radians) s.t.
 *                    \f$ \kappa \in [0, 2\pi] \f$ [nk]
 * @param[in] nt      number of dip angles
 * @param[in] thetas  dips angles (radians) s.t.
 *                    \f$ \theta \in [0, \pi/2] \f$ [nt]
 * @param[in] ns      number of slip angles
 * @param[in] sigmas  slip angles (radians) s.t.
 *                    \f$ \sigma \in [-\pi/2, \pi/2] \f$ [ns]
 * @param[in] ldm     leading dimension of mts.  must be >= 6.
 * @param[in] nmt     number of moment tensors (=nm*nb*ng*nk*ns*nt)
 *
 * @param[out] mts    moment tensors (Newton-meters) in north,east,down s.t.
 *                    \f$ \textbf{m}
 *                      = \f$ \{m_{xx}, m_{yy}, m_{zz},
 *                              m_{xy}, m_{xz}, m_{yz}\} \f$.
 *                    This has dimensions [nm*nb*ng*nk*ns*nt x ldm].  Observe
 *                    the ordering as defind above.
 *
 * @result 0 indicates success
 *
 * @author Ben Baker
 *
 * @copyright ISTI licensed under Apached 2
 *
 */
int parmt_discretizeMT64f_MPI(const MPI_Comm comm,
                              const int ng,
                              const double *__restrict__ gammas,
                              const int nb,
                              const double *__restrict__ betas,
                              const int nm,
                              const double *__restrict__ M0s,
                              const int nk,
                              const double *__restrict__ kappas,
                              const int nt,
                              const double *__restrict__ thetas,
                              const int ns,
                              const double *__restrict__ sigmas,
                              const int ldm,
                              struct localMT_struct *mts)
{
    const char *fcnm = "parmt_discretizeMT64f_MPI\0";
    double *mtWork;
    int64_t nmt64;
    int *displ, *nmtProc, *offset, *sendCounts;
    int dmt, i, ierr, imt1, imt2, myid, nmtall, nmt, nprocs;
    const int master = 0;
    //------------------------------------------------------------------------//
    ierr = 0;
    MPI_Comm_size(comm, &nprocs);
    MPI_Comm_rank(comm, &myid);
    memset(mts, 0, sizeof(struct localMT_struct));
    mts->comm = comm;
    // Check for integer overflow
    nmt64 = (int64_t) ng * (int64_t) nb * (int64_t) nm
           *(int64_t) nk * (int64_t) nt * (int64_t) ns;
    if (nmt64 > INT_MAX)
    {
        printf("%s: Error integer overflow - gridsearch too large\n", fcnm);
        return -1;
    }
    nmt = (int) nmt64;
    // Verify the inputs
    if (ldm < 6 || ng < 1 || nb < 1 || nm < 1 || nk < 1 || nt < 1 || ns < 1 ||
        gammas == NULL || betas == NULL || M0s == NULL || kappas == NULL ||
        thetas == NULL || sigmas == NULL)
    {
        if (ldm < 6){printf("%s: Invalid leading dimension\n", fcnm);}
        if (ng < 1){printf("%s: ng must be positive\n", fcnm);}
        if (nb < 1){printf("%s: nb must be positive\n", fcnm);}
        if (nm < 1){printf("%s: nm must be positive\n", fcnm);}
        if (nk < 1){printf("%s: nk must be positive\n", fcnm);}
        if (nt < 1){printf("%s: nt must be positive\n", fcnm);}
        if (ns < 1){printf("%s: ns must be positive\n", fcnm);}
        if (gammas == NULL){printf("%s: gammas is NULL\n", fcnm);}
        if (betas == NULL){printf("%s: betas is NULL\n", fcnm);}
        if (M0s == NULL){printf("%s: M0s is NULL\n", fcnm);}
        if (kappas == NULL){printf("%s: kappas is NULL\n", fcnm);}
        if (thetas == NULL){printf("%s: thetas is NULL\n", fcnm);}
        if (sigmas == NULL){printf("%s: sigmas is NULL\n", fcnm);}
        return -1;
    }
    // divide the moment tensor grid
    dmt = MAX(nmt/nprocs, 1);
    imt1 = myid*dmt;
    imt2 = (myid + 1)*dmt;
    if (myid == nprocs - 1){imt2 = nmt;}
    mts->nmt = imt2 - imt1;
    mts->ldm = ldm;
    MPI_Allreduce(&mts->nmt, &nmtall, 1, MPI_INTEGER, MPI_SUM, mts->comm);
    if (nmtall != nmt)
    {
        if (myid == master)
        {
            printf("%s: Failed to partition domain %d %d\n", fcnm, nmtall, nmt);
        }
        return -1;
    }
    // create the requisite information for an MPI_GATHER
    mts->nmtProc = memory_calloc32i(nprocs);
    mts->offset = memory_calloc32i(nprocs);
    nmtProc = memory_calloc32i(nprocs);
    offset = memory_calloc32i(nprocs);
    nmtProc[myid] = mts->nmt;
    offset[myid] = imt1;
    MPI_Allreduce(nmtProc, mts->nmtProc, nprocs, MPI_INTEGER,
                  MPI_SUM, mts->comm);
    MPI_Allreduce(offset, mts->offset,  nprocs, MPI_INTEGER,
                  MPI_SUM, mts->comm);
    mts->commSize = nprocs;
    mts->nmtAll = nmtall;
    mts->myid = myid;
    memory_free32i(&nmtProc);
    memory_free32i(&offset);
    // have master process compute all mts and scatter them
    // TODO this should be parallel
    if (mts->myid == master)
    {
        mtWork = memory_calloc64f(mts->ldm*mts->nmtAll);
        ierr = parmt_discretizeMT64f(ng, gammas,
                                     nb, betas,
                                     nm, M0s,
                                     nk, kappas,
                                     nt, thetas,
                                     ns, sigmas,
                                     mts->ldm, mts->nmtAll, mtWork);
    }
    else
    {
        mtWork = memory_calloc64f(1);
    }
    MPI_Bcast(&ierr, 1, MPI_INT, master, mts->comm);
    if (mts->commSize > 1)
    {
        // figure out who gets what part of the moment tensor space
        sendCounts = memory_calloc32i(mts->commSize);
        displ = memory_calloc32i(mts->commSize);
        for (i=0; i<mts->commSize; i++)
        {
            sendCounts[i] = mts->nmtProc[i]*mts->ldm;
            displ[i] = mts->ldm*mts->offset[i];
        }
        // set the memory and distribute it
        mts->mts = memory_calloc64f(mts->ldm*MAX(1,mts->nmt));
        MPI_Scatterv(mtWork, sendCounts, displ,
                     MPI_DOUBLE, mts->mts, mts->ldm*mts->nmt,
                     MPI_DOUBLE, master, mts->comm);
        memory_free64f(&mtWork);
    }
    else
    {
        mts->mts = mtWork;
    }
    return ierr;
}
//============================================================================//
/*!
 * @brief Computes the moment tensors in the grid search.
 *        The grid search ordering is:
 *         Loop on magnitudes
 *          Loop on colatitudes
 *           Loop on longitudes
 *            Loop on strikes
 *             Loop on slips
 *              Loop on dips
 *
 * @param[in] ng      number of longitudes
 * @param[in] gammas  longitudes (radians) on lune s.t.
 *                    \f$ \gamma \in [-\pi/6, \pi/6] \f$ [ng]
 * @param[in] nb      number of colatitudes
 * @param[in] betas   colatitudes (radians) on lune s.t.
 *                    \f$ \beta \in [0, \pi] \f$ [nb]
 * @param[in] nm      number of scalar moments
 * @param[in] M0s     scalar moments in Newton-meters [nm].
 *                    for a `unit' moment tensor choose M0 = 1/sqrt(2)
 * @param[in] nk      number of strike angles
 * @param[in] kappas  strike angles (radians) s.t.
 *                    \f$ \kappa \in [0, 2\pi] \f$ [nk]
 * @param[in] nt      number of dip angles
 * @param[in] thetas  dips angles (radians) s.t.
 *                    \f$ \theta \in [0, \pi/2] \f$ [nt]
 * @param[in] ns      number of slip angles
 * @param[in] sigmas  slip angles (radians) s.t.
 *                    \f$ \sigma \in [-\pi/2, \pi/2] \f$ [ns]
 * @param[in] ldm     leading dimension of mts.  must be >= 6.
 * @param[in] nmt     number of moment tensors (=nm*nb*ng*nk*ns*nt)
 *
 * @param[out] mts    moment tensors (Newton-meters) in north,east,down s.t.
 *                    \f$ \textbf{m}
 *                      = \f$ \{m_{xx}, m_{yy}, m_{zz},
 *                              m_{xy}, m_{xz}, m_{yz}\} \f$.
 *                    This has dimensions [nm*nb*ng*nk*ns*nt x ldm].  Observe
 *                    the ordering as defind above.
 *
 * @result 0 indicates success
 *
 * @author Ben Baker
 *
 * @copyright ISTI licensed under Apached 2
 *
 */
int parmt_discretizeMT64f(const int ng,
                          const double *__restrict__ gammas,
                          const int nb,
                          const double *__restrict__ betas,
                          const int nm,
                          const double *__restrict__ M0s,
                          const int nk,
                          const double *__restrict__ kappas,
                          const int nt,
                          const double *__restrict__ thetas,
                          const int ns,
                          const double *__restrict__ sigmas,
                          const int ldm,
                          const int nmt, double *__restrict__ mts)
{
    const char *fcnm = "parmt_discretizeMT64f\0";
    double lam[3], Muse[6], U[9] __attribute__ ((aligned (64)));
    double *mtWork, deltaDeg, gammaDeg, kappaDeg, sigmaDeg, thetaDeg;
    int i, ierr, ierr1, ib, ig, indx, im, imt, ik, is, it, nmtBase;
    const double M01[1] = {1.0};
    const double pi180i = 180.0/M_PI;
    const double betaMin = 0.0;
    const double betaMax = M_PI;
    const double gammaMin =-M_PI/6.0;
    const double gammaMax = M_PI/6.0;
    const double kappaMin = 0.0;
    const double kappaMax = 2.0*M_PI;
    const double thetaMin = 0.0;
    const double thetaMax = M_PI_2;
    const double sigmaMin =-M_PI_2;
    const double sigmaMax = M_PI_2;
    //const double sqrt2i = 1.0/sqrt(2.0); // unit magnitude MT
    //------------------------------------------------------------------------//
    ierr = 0;
    // Verify the inputs
    if (ldm < 6 || ng < 1 || nb < 1 || nm < 1 || nk < 1 || nt < 1 || ns < 1 ||
        nmt != ng*nb*nm*nk*nt*ns ||
        gammas == NULL || betas == NULL || M0s == NULL || kappas == NULL ||
        thetas == NULL || sigmas == NULL)
    {
        if (ldm < 6){printf("%s: Invalid leading dimension\n", fcnm);}
        if (ng < 1){printf("%s: ng must be positive\n", fcnm);}
        if (nb < 1){printf("%s: nb must be positive\n", fcnm);}
        if (nm < 1){printf("%s: nm must be positive\n", fcnm);}
        if (nk < 1){printf("%s: nk must be positive\n", fcnm);}
        if (nt < 1){printf("%s: nt must be positive\n", fcnm);}
        if (ns < 1){printf("%s: ns must be positive\n", fcnm);}
        if (nmt != ng*nb*nm*nk*nt*ns)
        {
            printf("%s: nmt != ng*nb*nm*nk*nt*ns %d %d\n",
                   fcnm, nmt, ng*nb*nm*nk*nt*ns);
        }
        if (gammas == NULL){printf("%s: gammas is NULL\n", fcnm);}
        if (betas == NULL){printf("%s: betas is NULL\n", fcnm);}
        if (M0s == NULL){printf("%s: M0s is NULL\n", fcnm);}
        if (kappas == NULL){printf("%s: kappas is NULL\n", fcnm);}
        if (thetas == NULL){printf("%s: thetas is NULL\n", fcnm);}
        if (sigmas == NULL){printf("%s: sigmas is NULL\n", fcnm);}
        return -1;
    }
    for (i=0; i<ng; i++)
    {
        if (gammas[i] < gammaMin || gammas[i] > gammaMax)
        {
            printf("%s: gammas %f out or range [%f,%f]\n",
                   fcnm, gammas[i], gammaMin, gammaMax);
            return -1;
        }
    }
    for (i=0; i<nb; i++)
    {
        if (betas[i] < betaMin || betas[i] > betaMax)
        {
            printf("%s: betas %f out or range [%f,%f]\n",
                   fcnm, betas[i], betaMin, betaMax);
            return -1;
        }
    }
    for (i=0; i<nk; i++)
    {
        if (kappas[i] < kappaMin || kappas[i] > kappaMax)
        {
            printf("%s: kappas %f out or range [%f,%f]\n",
                   fcnm, kappas[i], kappaMin, kappaMax);
            return -1;
        }
    }
    for (i=0; i<nt; i++)
    {
        if (thetas[i] < thetaMin || thetas[i] > thetaMax)
        {
            printf("%s: thetas %f out or range [%f,%f]\n",
                   fcnm, thetas[i], thetaMin, thetaMax);
            return -1;
        }
    }
    for (i=0; i<ns; i++)
    {
        if (sigmas[i] < sigmaMin || sigmas[i] > sigmaMax)
        {
            printf("%s: sigmas %f out or range [%f,%f]\n",
                   fcnm, sigmas[i], sigmaMin, sigmaMax);
            return -1;
        }
    }
    // The moment tensors are equivalent up to a scaling factor - so compute
    // the unit moment tensor
    nmtBase = nb*ng*nk*ns*nt;
    if (nm == 1)
    {
        mtWork = mts;
    }
    else
    {
        mtWork = memory_calloc64f(nmtBase*ldm);
    }
#ifdef _OPENMP
    #pragma omp parallel for collapse(5) \
     firstprivate (lam, M01, Muse, U) \
     private (deltaDeg, gammaDeg, kappaDeg, \
              sigmaDeg, thetaDeg, ierr1, imt, ib, ig, ik, is, it) \
     shared (fcnm, M0s, betas, gammas, kappas, sigmas, mtWork, thetas) \
     reduction (max:ierr) \
     default (none)
#endif
    // Loop on colatitudes
    for (ib=0; ib<nb; ib++)
    {
        // Loop on longitudes
        for (ig=0; ig<ng; ig++)
        {
            // Loop on strike
            for (ik=0; ik<nk; ik++)
            {
                // Loop on slip
                for (is=0; is<ns; is++)
                {
                    // Loop on dip
                    for (it=0; it<nt; it++)
                    {
                        // tape**2 space term
                        //M0 = sqrt2i;
                        deltaDeg = (M_PI/2.0 - betas[ib])*pi180i;
                        gammaDeg = gammas[ig]*pi180i;
                        kappaDeg = kappas[ik]*pi180i;
                        sigmaDeg = sigmas[is]*pi180i;
                        thetaDeg = thetas[it]*pi180i;
                        // index
                        imt = ib*ng*nk*ns*nt
                            + ig*nk*ns*nt
                            + ik*ns*nt
                            + is*nt
                            + it;
                        // Compute the corresponding moment tensor.
                        // Silver and Jordan compute M0 = 1/sqrt(2)norm(M)
                        // hence, if it is the scalar moment computation
                        // that handles the 1/sqrt(2) which means I should
                        // use unity here.  This has been verified b/c
                        // apply CMT2m0 on this moment tensor will yield
                        // the input M0.
                        ierr1 = compearth_TT2CMT(1, &gammaDeg, &deltaDeg, M01,
                                                 &kappaDeg, &thetaDeg,
                                                 &sigmaDeg,
                                                 Muse, lam, U);
/*
                        ierr1 = compearth_tt2cmt(gammaDeg, deltaDeg, 1.0, //sqrt2i,
                                                 kappaDeg, thetaDeg,
                                                 sigmaDeg,
                                                 Muse, lam, U);
*/
                        // Convert from USE to our NED estimation basis
                        ierr1 = compearth_convertMT(1, CE_USE, CE_NED, Muse,
                                                    &mtWork[imt*ldm]);
                        if (ierr1 != 0)
                        {
                            printf("%s: Error changing coords\n", fcnm);
                            ierr = ierr + 1;
                            continue;
                        }
                    } // loop on dip
                } // loop on slip
            } // loop on strike
        } // loop on longitudes
    } // loop on latitudes
    if (ierr != 0)
    {
        printf("%s: Errors during mt computation\n", fcnm);
        ierr = 1;
    }
    // Now copy all moment tensors over
    if (nm == 1)
    {
        cblas_dscal(ldm*nmtBase, M0s[0], mtWork, 1);
    }
    else
    {
        for (im=0; im<nm; im++)
        {
            indx = im*nmtBase*ldm; //nb*ng*nk*ns*nt;
            for (imt=0; imt<ldm*nmtBase; imt++)
            {
                mts[indx+imt] = M0s[im]*mtWork[imt];
            }
        }
        memory_free64f(&mtWork);
    }
    mtWork = NULL;
    return ierr;
}


//segSites.h

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
double my_h(double q, void * p);
double my_H(gsl_vector *pVector, void * p);
void siteProbMatrix(gsl_matrix *probs, gsl_vector *pVector,  int maxSampleSize, gsl_vector *sampleSizeVector);
double weightedLikLookSites(gsl_vector *pVector, void * p);
gsl_vector *jointMLEst(double *lik, void * p);
double weightedLikLookSitesNState(gsl_vector *pVector, void * p);
void jointMLEstNState(gsl_vector *results, double *lik, void * p);