/**
 * @file grid_unstruct.c
 *
 * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
 *
 * @version 1.0
 * @author Moritz Hanke <hanke@dkrz.de>
 */
/*
 * Keywords:
 * Maintainer: Moritz Hanke <hanke@dkrz.de>
 * URL: https://doc.redmine.dkrz.de/YAC/html/index.html
 *
 * This file is part of YAC.
 *
 * YAC 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.
 *
 * YAC 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 YAC.  If not, see <http://www.gnu.org/licenses/gpl.txt>.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "grid.h"
#include "grid_unstruct.h"
#include "utils.h"
#include "dep_list.h"
#include "geometry.h"
#include "utils.h"
#include "ensure_array_size.h"
#include "sphere_part.h"

// routine declarations

static struct grid * copy_grid_unstruct(struct grid * grid);
static void get_2d_grid_extent_unstruct(struct grid * grid, double (* extent)[2]);
static void get_grid_cell_unstruct(struct grid * grid, unsigned cell_index,
                                   struct grid_cell * cell);
static void get_grid_cell2_unstruct(struct grid * grid, unsigned cell_index,
                                    struct grid_cell * cell,
                                    struct bounding_circle * bnd_circle);
static unsigned get_size_x_coords_unstruct(struct grid * grid);
static unsigned get_size_y_coords_unstruct(struct grid * grid);
static double const * get_x_coords_unstruct(struct grid * grid);
static double const * get_y_coords_unstruct(struct grid * grid);
static void set_x_coords_unstruct(struct grid * grid, double * x_coords);
static void set_y_coords_unstruct(struct grid * grid, double * y_coords);
static unsigned get_size_cell_grid_x_coords_unstruct(struct grid * grid);
static unsigned get_size_cell_grid_y_coords_unstruct(struct grid * grid);
static unsigned get_num_grid_cells_unstruct(struct grid * grid);
static unsigned get_num_grid_corners_unstruct(struct grid * grid);
static unsigned get_num_cell_corners_unstruct(struct grid * grid, unsigned cell_index);
static unsigned get_num_corner_cells_unstruct(struct grid * grid, unsigned corner_index);
static unsigned get_num_grid_edges_unstruct(struct grid * grid);
static unsigned get_num_corner_edges_unstruct(struct grid * grid, unsigned corner_index);
static unsigned get_num_cell_edges_unstruct(struct grid * grid, unsigned cell_index);
static unsigned const * get_corner_edges_unstruct(struct grid * grid, unsigned corner_index);
static unsigned const * get_cell_edge_indices_unstruct(struct grid * grid,
                                                       unsigned cell_index);
static enum yac_edge_type get_edge_type_unstruct(struct grid * grid, unsigned edge_index);
static unsigned const * get_cell_corner_indices_unstruct(struct grid * grid,
                                                         unsigned cell_index);
static unsigned const * get_corner_cell_indices_unstruct(struct grid * grid,
                                                         unsigned corner_index);
static unsigned const * get_cell_x_coord_indices_unstruct(struct grid * grid,
                                                          unsigned cell_index);
static unsigned const * get_cell_y_coord_indices_unstruct(struct grid * grid,
                                                          unsigned cell_index);
static unsigned get_corner_x_coord_index_unstruct(struct grid * grid, unsigned corner_index);
static unsigned get_corner_y_coord_index_unstruct(struct grid * grid, unsigned corner_index);
static double get_corner_x_coord_unstruct(struct grid * grid, unsigned corner_index);
static double get_corner_y_coord_unstruct(struct grid * grid, unsigned corner_index);
static int get_aux_grid_cell_unstruct(struct grid * grid, unsigned corner_index,
                                      unsigned * cell_indices, enum yac_edge_type * edge_type);
static struct dep_list * get_cell_neigh_dep_list_unstruct(struct grid * grid);
static void get_boundary_corners_unstruct(struct grid * grid, unsigned * bnd_corners,
                                          unsigned * num_bnd_corners);
static struct grid * generate_cell_grid_unstruct(struct grid * grid,
                                                 double * coordinates_x, 
                                                 double * coordinates_y);
static struct grid * generate_edge_grid_unstruct(struct grid * grid,
                                                 double * coordinates_x, 
                                                 double * coordinates_y);
static struct grid * generate_subgrid_unstruct(struct grid * grid,
                                               unsigned * selected_local_cell_ids,
                                               unsigned num_local_cells,
                                               unsigned ** local_cell_ids,
                                               unsigned ** local_corner_ids,
                                               unsigned ** local_edge_ids);
static void pack_grid_unstruct(struct grid * grid, void * buffer,
                               int buffer_size, int * position,
                               struct communicator * comm);
static int grid_pack_size_unstruct(struct grid * grid, struct communicator * comm);
static struct grid_search * get_grid_search_unstruct(struct grid * grid);
static int cell_contains_non_gc_edge_unstruct(struct grid * grid, unsigned local_cell_id);
static void delete_grid_unstruct(struct grid * grid);

static struct grid_vtable unstruct_grid_vtable = {

   .copy                        = copy_grid_unstruct,
   .get_2d_extent               = get_2d_grid_extent_unstruct,
   .get_grid_cell               = get_grid_cell_unstruct,
   .get_grid_cell2              = get_grid_cell2_unstruct,
   .get_size_x_coords           = get_size_x_coords_unstruct,
   .get_size_y_coords           = get_size_y_coords_unstruct,
   .get_x_coords                = get_x_coords_unstruct,
   .get_y_coords                = get_y_coords_unstruct,
   .set_x_coords                = set_x_coords_unstruct,
   .set_y_coords                = set_y_coords_unstruct,
   .get_size_cell_grid_x_coords = get_size_cell_grid_x_coords_unstruct,
   .get_size_cell_grid_y_coords = get_size_cell_grid_y_coords_unstruct,
   .get_num_grid_cells          = get_num_grid_cells_unstruct,
   .get_num_grid_corners        = get_num_grid_corners_unstruct,
   .get_num_cell_corners        = get_num_cell_corners_unstruct,
   .get_num_corner_cells        = get_num_corner_cells_unstruct,
   .get_num_grid_edges          = get_num_grid_edges_unstruct,
   .get_num_corner_edges        = get_num_corner_edges_unstruct,
   .get_num_cell_edges          = get_num_cell_edges_unstruct,
   .get_corner_edges            = get_corner_edges_unstruct,
   .get_cell_edge_indices       = get_cell_edge_indices_unstruct,
   .get_edge_type               = get_edge_type_unstruct,
   .get_cell_corner_indices     = get_cell_corner_indices_unstruct,
   .get_corner_cell_indices     = get_corner_cell_indices_unstruct,
   .get_cell_x_coord_indices    = get_cell_x_coord_indices_unstruct,
   .get_cell_y_coord_indices    = get_cell_y_coord_indices_unstruct,
   .get_corner_x_coord          = get_corner_x_coord_unstruct,
   .get_corner_y_coord          = get_corner_y_coord_unstruct,
   .get_corner_x_coord_index    = get_corner_x_coord_index_unstruct,
   .get_corner_y_coord_index    = get_corner_y_coord_index_unstruct,
   .get_aux_grid_cell           = get_aux_grid_cell_unstruct,
   .get_cell_neigh_dep_list     = get_cell_neigh_dep_list_unstruct,
   .get_boundary_corners        = get_boundary_corners_unstruct,
   .generate_cell_grid          = generate_cell_grid_unstruct,
   .generate_edge_grid          = generate_edge_grid_unstruct,
   .generate_subgrid            = generate_subgrid_unstruct,
   .pack_grid                   = pack_grid_unstruct,
   .grid_pack_size              = grid_pack_size_unstruct,
   .get_grid_search             = get_grid_search_unstruct,
   .cell_contains_non_gc_edge   = cell_contains_non_gc_edge_unstruct,
   .grid_delete                 = delete_grid_unstruct
};

struct unstruct_grid {

   struct grid_vtable * vtable;
   enum yac_grid_type grid_type;

   struct dep_list * cell_to_neigh; //!< dependency list containing cell neighbourhood
                                    //!< information (automatically generated once it is
                                    //!< needed)

   double * cell_corners_x,       //!< latitude data
          * cell_corners_y,       //!< longitude data
          (*cell_corners_xyz)[3]; //!< 3d coordinates

   unsigned cell_corners_x_by_user; //!< indicates whether cell_corners_x was provided by
                                    //!< the user or automatically generated by a grid
                                    //!< routine
   unsigned cell_corners_y_by_user; //!< indicates whether cell_corners_y was provided by
                                    //!< the user or automatically generated by a grid
                                    //!< routine

   struct dep_list * cell_to_vertex;
   struct dep_list * vertex_to_cell;
   unsigned cell_to_vertex_by_user; //!< indicates whether cell_to_vertex was provided by
                                    //!< the user or automatically generated by a grid
                                    //!< routine (for example generate_cell_grid)
   unsigned num_vertices;

   struct dep_list * corner_to_corner; //!< dependency list that contains for each corner
                                       //!< the other corners that are connected via an edge
                                       //!< it only stores each edge once (for the point with
                                       //!< the lowest id)
   struct dep_list * inv_corner_to_corner; //!< inverted version of corner_to_corner

   struct grid_search * grid_search;
};


// static data used by get_corner_edges_unstruct
static unsigned * corners = NULL;
static size_t size_corners = 0;
// static data used by get_cell_edge_indices_unstruct
static unsigned * edges = NULL;
static size_t size_edges = 0;
// static data used by get_corner_edge_indices_unstruct
static unsigned * cell_corners = NULL;
static size_t size_cell_corners = 0;

static unsigned get_num_cell_corners_unstruct (struct grid * grid, unsigned cell_index) {

  struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

  return
    (unsigned)yac_get_num_dependencies_of_element(
       unstruct_grid->cell_to_vertex, (size_t)cell_index);
}

static unsigned get_num_cell_edges_unstruct (struct grid * grid, unsigned cell_index) {

   return get_num_cell_corners_unstruct(grid, cell_index);
}

static unsigned get_num_corner_cells_unstruct (struct grid * grid, unsigned corner_index) {

  struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;
  
  return
    (unsigned)yac_get_num_dependencies_of_element(
       unstruct_grid->vertex_to_cell, (size_t)corner_index);
}

static unsigned const * get_cell_x_coord_indices_unstruct (struct grid * grid,
                                                           unsigned cell_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return yac_get_dependencies_of_element(
      unstruct_grid->cell_to_vertex, (size_t)cell_index);
}

static unsigned const * get_cell_y_coord_indices_unstruct (struct grid * grid,
                                                           unsigned cell_index) {

   return get_cell_x_coord_indices_unstruct(grid, cell_index);
}

static double get_corner_x_coord_unstruct (struct grid * grid, unsigned corner_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->cell_corners_x[corner_index];
}

static double get_corner_y_coord_unstruct (struct grid * grid, unsigned corner_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->cell_corners_y[corner_index];
}

static unsigned get_corner_x_coord_index_unstruct (struct grid * grid, unsigned corner_index) {

   UNUSED(grid);

   return corner_index;
}

static unsigned get_corner_y_coord_index_unstruct (struct grid * grid, unsigned corner_index) {

   UNUSED(grid);

   return corner_index;
}

static unsigned get_size_x_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->num_vertices;
}

static unsigned get_size_y_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->num_vertices;
}

static double const * get_x_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->cell_corners_x;
}

static double const * get_y_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->cell_corners_y;
}

static void set_x_coords_unstruct(struct grid * grid, double * x_coords) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (!unstruct_grid->cell_corners_x_by_user)
      free(unstruct_grid->cell_corners_x);

   unstruct_grid->cell_corners_x_by_user = 1 == 1;
   unstruct_grid->cell_corners_x = x_coords;
}

static void set_y_coords_unstruct(struct grid * grid, double * y_coords) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (!unstruct_grid->cell_corners_y_by_user)
      free(unstruct_grid->cell_corners_y);

   unstruct_grid->cell_corners_y_by_user = 1 == 1;
   unstruct_grid->cell_corners_y = y_coords;
}

static unsigned get_size_cell_grid_x_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return (unsigned)(yac_get_num_elements(unstruct_grid->cell_to_vertex));
}

static unsigned get_size_cell_grid_y_coords_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return (unsigned)(yac_get_num_elements(unstruct_grid->cell_to_vertex));
}

static unsigned get_num_grid_cells_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return (unsigned)(yac_get_num_elements(unstruct_grid->cell_to_vertex));
}

static unsigned get_num_grid_corners_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return unstruct_grid->num_vertices;
}

static void generate_unstruct_edges(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   size_t num_corners = (size_t)get_num_grid_corners_unstruct(grid);
   size_t * num_edges_per_corner =
      calloc (num_corners, sizeof (*num_edges_per_corner));

   size_t max_num_edges_per_corner;

   size_t * temp_num_edges_per_corner =
      calloc (2 * num_corners, sizeof (*temp_num_edges_per_corner));

   //----------------------------------------------
   // count the maximum amount of edges per corner
   //----------------------------------------------

   size_t num_cells = yac_get_num_elements(unstruct_grid->cell_to_vertex);

   // for all cells
   for (size_t i = 0; i < num_cells; ++i) {

      // get the corners of the current cell
      unsigned const * curr_cell_corners =
         yac_get_dependencies_of_element(unstruct_grid->cell_to_vertex, i);
      size_t curr_num_cell_corners =
         yac_get_num_dependencies_of_element(unstruct_grid->cell_to_vertex, i);

      // for all corners of the current cell
      size_t j;
      for (j = 0; j < curr_num_cell_corners - 1; ++j) {

         if (curr_cell_corners[j] > curr_cell_corners[j+1]) {
            ++temp_num_edges_per_corner[curr_cell_corners[j]];
            ++temp_num_edges_per_corner[curr_cell_corners[j+1] + num_corners];
         } else {
            ++temp_num_edges_per_corner[curr_cell_corners[j+1]];
            ++temp_num_edges_per_corner[curr_cell_corners[j] + num_corners];
         }
      }

      if (curr_cell_corners[0] > curr_cell_corners[j]) {
            ++temp_num_edges_per_corner[curr_cell_corners[0]];
            ++temp_num_edges_per_corner[curr_cell_corners[j] + num_corners];
         } else {
            ++temp_num_edges_per_corner[curr_cell_corners[j]];
            ++temp_num_edges_per_corner[curr_cell_corners[0] + num_corners];
         }
   }

   max_num_edges_per_corner = 0;
   for (size_t i = 0; i < num_corners; ++i) {
   
      if (temp_num_edges_per_corner[i] > max_num_edges_per_corner)
         max_num_edges_per_corner = temp_num_edges_per_corner[i];
      if (temp_num_edges_per_corner[i + num_corners] > max_num_edges_per_corner)
         max_num_edges_per_corner = temp_num_edges_per_corner[i + num_corners];
   }

   //--------------------------------------------------------------
   // get all reverse edges (array is too big, because we do not
   // know yet the actual number of edges per corner)
   //--------------------------------------------------------------

   unsigned * temp_edge_list =
      malloc(
         num_corners * max_num_edges_per_corner * sizeof (temp_edge_list[0]));
   for (size_t i = 0; i < num_corners; ++i) temp_num_edges_per_corner[i] = 0;

   num_cells = yac_get_num_elements(unstruct_grid->cell_to_vertex);

   // for all cells
   for (size_t i = 0; i < num_cells; ++i) {

      // get the corners of the current cell
      unsigned const * curr_cell_corners =
         yac_get_dependencies_of_element(unstruct_grid->cell_to_vertex, i);
      size_t curr_num_cell_corners =
         yac_get_num_dependencies_of_element(unstruct_grid->cell_to_vertex, i);

      // for all corners of the current cell
      size_t j;
      for (j = 0; j < curr_num_cell_corners - 1; ++j) {

         if (curr_cell_corners[j] < curr_cell_corners[j+1])
            temp_edge_list[curr_cell_corners[j+1] * max_num_edges_per_corner +
                           temp_num_edges_per_corner[curr_cell_corners[j+1]]++] =
               curr_cell_corners[j];
         else
            temp_edge_list[curr_cell_corners[j] * max_num_edges_per_corner +
                           temp_num_edges_per_corner[curr_cell_corners[j]]++] =
               curr_cell_corners[j+1];
      }

      if (curr_cell_corners[0] < curr_cell_corners[j])
         temp_edge_list[curr_cell_corners[j] * max_num_edges_per_corner +
                        temp_num_edges_per_corner[curr_cell_corners[j]]++] =
            curr_cell_corners[0];
      else
         temp_edge_list[curr_cell_corners[0] * max_num_edges_per_corner +
                        temp_num_edges_per_corner[curr_cell_corners[0]]++] =
            curr_cell_corners[j];
   }

   //----------------------
   // get the actual edges
   //----------------------

   size_t num_edges = 0;

   // for all corners (we can skip the first corner)
   for (unsigned i = 1; i < (unsigned)num_corners; ++i) {

      // for all reverse edges of the current corner
      for (size_t j = 0; j < temp_num_edges_per_corner[i]; ++j) {

         unsigned curr_corner =
            temp_edge_list[i * max_num_edges_per_corner + j];

         // if the edge is already existing -> get the next one
         if ((curr_corner == i) ||
             (num_edges_per_corner[curr_corner] > 0 &&
              temp_edge_list[
                 curr_corner * max_num_edges_per_corner +
                 num_edges_per_corner[curr_corner] - 1] == i)) continue;

         // store edge
         temp_edge_list[curr_corner * max_num_edges_per_corner +
                        num_edges_per_corner[curr_corner]++] = i;
         ++num_edges;
      }
   }

   free(temp_num_edges_per_corner);

   //-------------------------------------------------------------
   // copy the edges from the temporary edge list to the final one
   //-------------------------------------------------------------

   unsigned * curr_edge = temp_edge_list;
   unsigned * curr_temp_edge = temp_edge_list;

   for (size_t i = 0; i < num_corners;
        ++i, curr_temp_edge += max_num_edges_per_corner) {

      for (size_t j = 0; j < num_edges_per_corner[i]; ++j) {

         *curr_edge = curr_temp_edge[j];
         ++curr_edge;
      }
   }

   unsigned * edge_list =
      realloc(temp_edge_list, num_edges * sizeof (*edge_list));

   unstruct_grid->corner_to_corner =
      yac_dep_list_new((size_t)num_corners, num_edges_per_corner, edge_list);
   unstruct_grid->inv_corner_to_corner =
      yac_invert_dep_list(unstruct_grid->corner_to_corner);
}

// inserts an element into an array and increases the corresponding size
// if the element already exists in the array nothing is done
static void insertion_sort(unsigned element, unsigned * a, unsigned * curr_length) {

   unsigned i, j;

   if (*curr_length > 0) {

      for (i = 0; i < *curr_length; ++i)
         if (a[i] >= element) break;

      // if the element is already in the array
      if ((i != *curr_length) && (a[i] == element)) return;

      // copy new element into array and move bigger elements one position up
      for (j = *curr_length; j > i; --j) {

         a[j] = a[j-1];
      }

      a[i] = element;

      // increase array length indicator
      ++(*curr_length);

   } else {

      a[0] = element;
      *curr_length = 1;
   }
}

static void generate_cell_neigh_dep_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;


   // generate a vertex to cell mapping
   struct dep_list * vertex_to_cell =
      yac_invert_dep_list(unstruct_grid->cell_to_vertex);

   size_t total_num_neighs = 0;
   size_t num_cells = (size_t)get_num_grid_cells_unstruct(grid);

   size_t * num_neigh_per_cell = calloc(num_cells, sizeof(*num_neigh_per_cell));
   unsigned * cell_neigh_dependencies = NULL;
   size_t cell_neigh_dependencies_array_size = 0;

   // for all cells
   for (size_t i = 0; i < num_cells; ++i) {

      size_t curr_num_corners =
        yac_get_num_dependencies_of_element(unstruct_grid->cell_to_vertex, i);
      unsigned const * curr_corners =
        yac_get_dependencies_of_element(unstruct_grid->cell_to_vertex, i);

      // for all corners of the current cell
      for (size_t j = 0; j < curr_num_corners; ++j) {

         size_t curr_num_cells =
            yac_get_num_dependencies_of_element(
              vertex_to_cell, (size_t)curr_corners[j]);
         unsigned const * curr_cells =
            yac_get_dependencies_of_element(
               vertex_to_cell, (size_t)curr_corners[j]);

         // for all cells associated to this corner
         for (size_t k = 0; k < curr_num_cells; ++k) {

            if (curr_cells[k] == (unsigned)i) continue;

            ENSURE_ARRAY_SIZE(cell_neigh_dependencies,
                              cell_neigh_dependencies_array_size,
                              total_num_neighs + num_neigh_per_cell[i] + 1);

            unsigned curr_num_neigh_per_cell = (unsigned)num_neigh_per_cell[i];
            insertion_sort(
              curr_cells[k], cell_neigh_dependencies + total_num_neighs,
              &curr_num_neigh_per_cell);
            num_neigh_per_cell[i] = (size_t)curr_num_neigh_per_cell;
         }
      }
      total_num_neighs += num_neigh_per_cell[i];
   }
   yac_dep_list_delete(vertex_to_cell);

   cell_neigh_dependencies = realloc (cell_neigh_dependencies, total_num_neighs *
                                      sizeof(*cell_neigh_dependencies));

   unstruct_grid->cell_to_neigh =
      yac_dep_list_new(num_cells, num_neigh_per_cell, cell_neigh_dependencies);

}

static unsigned get_num_grid_edges_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
      generate_unstruct_edges (grid);

   return
      (unsigned)yac_get_total_num_dependencies(unstruct_grid->corner_to_corner);
}

static unsigned get_num_corner_edges_unstruct(struct grid * grid, unsigned corner_index) {

  struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

  if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
     generate_unstruct_edges (grid);

  size_t ret = yac_get_num_dependencies_of_element(
                 unstruct_grid->corner_to_corner, (size_t)corner_index);
  ret += yac_get_num_dependencies_of_element(
           unstruct_grid->inv_corner_to_corner, (size_t)corner_index);

  return (unsigned)ret;
}

static struct dep_list * get_cell_neigh_dep_list_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (unstruct_grid->cell_to_neigh == NULL)
      generate_cell_neigh_dep_unstruct(grid);
   return unstruct_grid->cell_to_neigh;
}

static void get_boundary_corners_unstruct(struct grid * grid, unsigned * bnd_corners,
                                          unsigned * num_bnd_corners) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   unsigned * num_cells_per_edge; // contains the number cell associated with each edge

   num_cells_per_edge = calloc (yac_get_num_grid_edges(grid),
                                sizeof (num_cells_per_edge[0]));

   //---------------------------------------------
   // each inner edges belongs to two edges
   // outer edges belong to only on local cell ->
   // corners of outer edges are boundary corners
   //---------------------------------------------
   
   // for all cells
   for (unsigned i = 0; i < yac_get_num_grid_cells(grid); ++i) {

      unsigned const * curr_cell_corners =
         yac_get_dependencies_of_element(
            unstruct_grid->cell_to_vertex, (size_t)i);
      size_t curr_num_cell_corners =
         yac_get_num_dependencies_of_element(
            unstruct_grid->cell_to_vertex, (size_t)i);

      size_t k = curr_num_cell_corners - 1;
      // for all corners of the current cell
      for (size_t j = 0; j < curr_num_cell_corners; k = j++) {

         if (curr_cell_corners[k] < curr_cell_corners[j]) {

            ++num_cells_per_edge[
               yac_get_dependency_index(
                  unstruct_grid->corner_to_corner,
                  (size_t)curr_cell_corners[k], curr_cell_corners[j])];
         } else {

            ++num_cells_per_edge[
               yac_get_dependency_index(
                  unstruct_grid->corner_to_corner,
                  (size_t)curr_cell_corners[j], curr_cell_corners[k])];
         }
      }
   }

   unsigned temp_num_bnd_corners = 0;
   // for all edges
   size_t num_grid_edges = (size_t)yac_get_num_grid_edges(grid);
   for (size_t i = 0; i < num_grid_edges; ++i) {

      if (num_cells_per_edge[i] == 1) {

         size_t corner_a;
         unsigned corner_b;
         yac_get_dependency(unstruct_grid->corner_to_corner, i,
                            &corner_a, &corner_b);

         insertion_sort((unsigned)corner_a, bnd_corners, &temp_num_bnd_corners);
         insertion_sort(corner_b, bnd_corners, &temp_num_bnd_corners);
      }
   }

   *num_bnd_corners = temp_num_bnd_corners;

   free (num_cells_per_edge);
}

static void get_grid_cell_unstruct(struct grid * grid, unsigned cell_index,
                                   struct grid_cell * cell) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   size_t num_corners = (size_t)get_num_cell_corners_unstruct(grid, cell_index);

   // if the memory for the coordinates needs to be reallocated
   if (num_corners > cell->array_size) {
      cell->coordinates_x = realloc (cell->coordinates_x, num_corners * sizeof(cell->coordinates_x[0]));
      cell->coordinates_y = realloc (cell->coordinates_y, num_corners * sizeof(cell->coordinates_y[0]));
      cell->coordinates_xyz = realloc (cell->coordinates_xyz, num_corners * sizeof(cell->coordinates_xyz[0]));
      cell->edge_type = realloc (cell->edge_type, num_corners * sizeof(cell->edge_type[0]));
      cell->array_size = num_corners;
   }

   cell->num_corners = num_corners;

   // set the corners for of the cell
   unsigned const * curr_vertex =
      yac_get_dependencies_of_element(
         unstruct_grid->cell_to_vertex, (size_t)cell_index);

   for (size_t i = 0; i < cell->num_corners; ++i) {

      cell->coordinates_x[i] = unstruct_grid->cell_corners_x[curr_vertex[i]];
      cell->coordinates_y[i] = unstruct_grid->cell_corners_y[curr_vertex[i]];
      cell->coordinates_xyz[i][0] = unstruct_grid->cell_corners_xyz[curr_vertex[i]][0];
      cell->coordinates_xyz[i][1] = unstruct_grid->cell_corners_xyz[curr_vertex[i]][1];
      cell->coordinates_xyz[i][2] = unstruct_grid->cell_corners_xyz[curr_vertex[i]][2];
   }

   // set the edge type for the cell
   for (size_t i = 0; i < cell->num_corners; ++i)
         cell->edge_type[i] = GREAT_CIRCLE;
}

static void get_grid_cell2_unstruct(struct grid * grid, unsigned cell_index,
                                    struct grid_cell * cell,
                                    struct bounding_circle * bnd_circle) {

   get_grid_cell_unstruct(grid, cell_index, cell);
   if (cell->num_corners == 3) {
      yac_get_cell_circumscribe_circle_unstruct_triangle(
         cell->coordinates_xyz[0], cell->coordinates_xyz[1],
         cell->coordinates_xyz[2], bnd_circle);
   } else
      yac_get_cell_bounding_circle(*cell, bnd_circle);
}

static unsigned const * get_corner_edges_unstruct(struct grid * grid, unsigned corner_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
      generate_unstruct_edges (grid);

   size_t c2c_num_edges =
      yac_get_num_dependencies_of_element(
         unstruct_grid->corner_to_corner, (size_t)corner_index);
   size_t inv_c2c_num_edges =
      yac_get_num_dependencies_of_element(
         unstruct_grid->inv_corner_to_corner, (size_t)corner_index);

   size_t max_num_edges = c2c_num_edges + inv_c2c_num_edges;

   if (max_num_edges > size_corners) {

      corners = realloc (corners, max_num_edges * sizeof (corners[0]));
      size_corners = max_num_edges;
   }

   unsigned const * inv_c2c_edges =
      yac_get_dependencies_of_element(
         unstruct_grid->inv_corner_to_corner, (size_t)corner_index);
   memcpy(corners, inv_c2c_edges, inv_c2c_num_edges * sizeof(*inv_c2c_edges));

   unsigned const * c2c_edges =
      yac_get_dependencies_of_element(
         unstruct_grid->corner_to_corner, (size_t)corner_index);
   memcpy(corners + inv_c2c_num_edges, c2c_edges,
          c2c_num_edges * sizeof(*c2c_edges));

   return corners;
}

static unsigned const * get_cell_corner_indices_unstruct(struct grid * grid,
                                                         unsigned cell_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return yac_get_dependencies_of_element(
      unstruct_grid->cell_to_vertex, (size_t)cell_index);
}

static unsigned const * get_corner_cell_indices_unstruct(struct grid * grid, unsigned corner_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   return yac_get_dependencies_of_element(
      unstruct_grid->vertex_to_cell, (size_t)corner_index);
}

static void get_corner_edge_indices_unstruct(
  struct unstruct_grid * unstruct_grid, unsigned corner_index,
  unsigned * edge_indices) {

  size_t num_neigh_cells =
    yac_get_num_dependencies_of_element(
      unstruct_grid->vertex_to_cell, corner_index);

  // get neighbour cells
  unsigned const * cell_indices =
    yac_get_dependencies_of_element(
      unstruct_grid->vertex_to_cell, (size_t)corner_index);

  // cell_corners only contains the corners adjacent to corner_index
  if (2 * num_neigh_cells > size_cell_corners) {

    cell_corners =
      realloc(cell_corners, 2 * num_neigh_cells * sizeof(*cell_corners));
    size_cell_corners = 2 * num_neigh_cells;
  }

  for (size_t i = 0; i < num_neigh_cells; ++i) {

    size_t curr_num_cell_corners =
      yac_get_num_dependencies_of_element(
        unstruct_grid->cell_to_vertex, cell_indices[i]);

    if (curr_num_cell_corners < 3) continue;

    unsigned const * curr_cell_corners =
      yac_get_dependencies_of_element(
        unstruct_grid->cell_to_vertex, (size_t)cell_indices[i]);

    size_t corner_pos;
    for (corner_pos = 0; corner_pos < curr_num_cell_corners; ++corner_pos)
      if (curr_cell_corners[corner_pos] == corner_index) break;

    if (corner_pos == 0) {
      cell_corners[2 * i + 0] = curr_cell_corners[curr_num_cell_corners - 1];
      cell_corners[2 * i + 1] = curr_cell_corners[1];
    } else if (corner_pos == curr_num_cell_corners - 1) {
      cell_corners[2 * i + 0] = curr_cell_corners[curr_num_cell_corners - 2];
      cell_corners[2 * i + 1] = curr_cell_corners[0];
    } else {
      cell_corners[2 * i + 0] = curr_cell_corners[corner_pos - 1];
      cell_corners[2 * i + 1] = curr_cell_corners[corner_pos + 1];
    }
  }

#define GET_EDGE_INDEX(a) \
  ((unsigned)(((a) < corner_index)? \
              (yac_get_dependency_index( \
                 unstruct_grid->corner_to_corner, (size_t)(a), corner_index)): \
              (yac_get_dependency_index( \
                 unstruct_grid->corner_to_corner, (size_t)corner_index, (a)))))

  edge_indices[0] = GET_EDGE_INDEX(cell_corners[0]);
  edge_indices[1] = GET_EDGE_INDEX(cell_corners[1]);
  unsigned curr_neigh_corner = cell_corners[1];
  unsigned curr_cell = 0;
  for (size_t i = 2; i < num_neigh_cells; ++i) {

    for (size_t j = 0; j < num_neigh_cells; ++j) {

      if (curr_cell == j) continue;

      if ((curr_neigh_corner == cell_corners[2 * j + 0]) ||
          (curr_neigh_corner == cell_corners[2 * j + 1])) {

        curr_neigh_corner =
          cell_corners[2 * j + (cell_corners[2 * j + 0] == curr_neigh_corner)];
        curr_cell = j;

        edge_indices[i] =  GET_EDGE_INDEX(curr_neigh_corner);
        break;
      }
    }
  }
#undef GET_EDGE_INDEX
}

static unsigned const * get_cell_edge_indices_unstruct(struct grid * grid, unsigned cell_index) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
      generate_unstruct_edges (grid);

   size_t num_edges = (size_t)get_num_cell_corners_unstruct(grid, cell_index);

   if (size_edges < num_edges) {

      edges = realloc (edges, num_edges * sizeof (edges[0]));
      size_edges = num_edges;
   }

   unsigned const * curr_cell_corners =
      get_cell_corner_indices_unstruct(grid, cell_index);

   for (size_t i = 0; i < num_edges-1; ++i) {

      int flag = curr_cell_corners[i] > curr_cell_corners[i+1];
      edges[i] =
         (unsigned)yac_get_dependency_index(unstruct_grid->corner_to_corner,
                                            (size_t)curr_cell_corners[i+flag],
                                            curr_cell_corners[i+(flag^1)]);
   }

   if (curr_cell_corners[num_edges-1] > curr_cell_corners[0])
      edges[num_edges-1] =
         (unsigned)yac_get_dependency_index(unstruct_grid->corner_to_corner,
                                            (size_t)curr_cell_corners[0],
                                            curr_cell_corners[num_edges-1]);
   else
      edges[num_edges-1] =
         (unsigned)yac_get_dependency_index(unstruct_grid->corner_to_corner,
                                            (size_t)curr_cell_corners[num_edges-1],
                                            curr_cell_corners[0]);

   return edges;
}

static enum yac_edge_type get_edge_type_unstruct(struct grid * grid, unsigned edge_index) {

   UNUSED(grid);
   UNUSED(edge_index);

   return GREAT_CIRCLE;
}

static void get_2d_grid_extent_unstruct(struct grid * grid, double (* extent)[2]) {

   double const tol = 1.0e-12;
   struct bounding_circle circle;
   double circle_inc_angle;

   yac_get_grid_bounding_circle(grid, &circle);
   circle_inc_angle = compute_angle(circle.inc_angle);

   // check if the grid covers the whole sphere
   if (circle_inc_angle - tol >= M_PI) {

      extent[0][0] =  -M_PI_2;
      extent[0][1] =   M_PI_2;
      extent[1][0] = -M_PI;
      extent[1][1] =  M_PI;

      return;
   }

   double rad_inc_angle;
   double base_point[2];

   rad_inc_angle = circle_inc_angle + tol;
   XYZtoLL(circle.base_vector, &(base_point[0]), &(base_point[1]));
   base_point[0] += tol;
   base_point[1] += tol;

   extent[1][0] = MAX(-M_PI_2, base_point[1] - rad_inc_angle);
   extent[1][1] = MIN( M_PI_2, base_point[1] + rad_inc_angle);

   // check if the circle covers a pole
   if (fabs(extent[1][0] + M_PI_2) <= tol ||
       fabs(extent[1][1] - M_PI_2) <= tol) {

      extent[0][0] = -M_PI;
      extent[0][1] =  M_PI;
   } else {
      extent[0][0] = base_point[0] - rad_inc_angle;
      extent[0][1] = base_point[0] + rad_inc_angle;
   }
}

static struct grid * generate_cell_grid_unstruct(struct grid * grid,
                                                 double * coordinates_x, 
                                                 double * coordinates_y) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   // construct corner to cell dependency list
   struct dep_list * corner_to_cell =
      yac_invert_dep_list(unstruct_grid->cell_to_vertex);

   // generate neighbour dependency list if it does no yet exist
   // struct dep_list * cell_to_neigh;
   if (yac_get_num_elements(unstruct_grid->cell_to_neigh) == 0)
      generate_cell_neigh_dep_unstruct(grid);
   // cell_to_neigh = &(unstruct_grid->cell_to_neigh);

   size_t num_total_cells = 0;
   size_t * num_corners_per_cell =
      calloc(yac_get_num_grid_corners(grid), sizeof(*num_corners_per_cell));
   unsigned * cell_corner_dependencies = NULL;
   size_t size_cell_corner_dependencies = 0;
   size_t num_total_cell_corner_dependencies = 0;
   unsigned * edge_to_cell = NULL;
   unsigned * num_cell_per_edge = NULL;
   size_t size_num_cell_per_edge = 0;
   unsigned num_grid_corners = get_num_grid_corners_unstruct(grid);

   // for all corners of the original grid (possible centres of cells of the cell grid)
   for (unsigned i = 0; i < num_grid_corners; ++i) {

      size_t num_curr_adjacent_cells =
         yac_get_num_dependencies_of_element(corner_to_cell, (size_t)i);

      // check whether the current corner is the centre of a cell of the cell grid

      // if the current corner has not enough adjacent cell to form the centre
      // of a new cell of the cell grid
      if (num_curr_adjacent_cells <= 2) continue;

      // gets the corners that are directly connected to the current corner (i) by
      // an edge
      size_t num_adjacent_corners =
        (size_t)get_num_corner_edges_unstruct(grid, i);
      unsigned const * adjacent_corners = get_corner_edges_unstruct(grid, i);

      // check whether num_cell_per_edge is big enough
      if (size_num_cell_per_edge < num_adjacent_corners) {

         size_num_cell_per_edge = num_adjacent_corners;
         num_cell_per_edge =
            realloc(num_cell_per_edge, size_num_cell_per_edge *
                    sizeof (*num_cell_per_edge));
         edge_to_cell =
            realloc(edge_to_cell, 2 * size_num_cell_per_edge *
                    sizeof(*edge_to_cell));
      }

      // initialise num_cell_per_edge
      for (size_t j = 0; j < num_adjacent_corners; ++j)
         num_cell_per_edge[j] = 0;

      // gets all cells that have the current corner (i) on their boundary definition
      unsigned const * curr_adjacent_cells =
         yac_get_dependencies_of_element(corner_to_cell, (size_t)i);
         
      // for all cell adjacent to the current corner
      for (size_t j = 0; j < num_curr_adjacent_cells; ++j) {

         // gets the corners of the current cell (curr_adjacent_cells[j])
         size_t num_curr_corners_of_cell =
            yac_get_num_dependencies_of_element(
               unstruct_grid->cell_to_vertex, (size_t)curr_adjacent_cells[j]);
         unsigned const * curr_corners_of_cell =
            yac_get_dependencies_of_element(
               unstruct_grid->cell_to_vertex, (size_t)curr_adjacent_cells[j]);

         // for all corners of the current cell
         // find the current corner (i) in the boundary definition of the
         // current cell (curr_adjacent_cells[j])
         size_t k;
         for (k = 0; k < num_curr_corners_of_cell; ++k)
            if (curr_corners_of_cell[k] == i) break;

         unsigned curr_adjacent_corners[2];

         // get the two associated edges
         if (k == 0) {
            curr_adjacent_corners[0] = curr_corners_of_cell[num_curr_corners_of_cell-1];
            curr_adjacent_corners[1] = curr_corners_of_cell[k+1];
         } else if (k == num_curr_corners_of_cell - 1){
            curr_adjacent_corners[0] = curr_corners_of_cell[k-1];
            curr_adjacent_corners[1] = curr_corners_of_cell[0];
         } else {
            curr_adjacent_corners[0] = curr_corners_of_cell[k-1];
            curr_adjacent_corners[1] = curr_corners_of_cell[k+1];
         }

         // for all adjacent corners of the current corner (i)
         for (size_t l = 0; l < num_adjacent_corners; ++l) {

            if (adjacent_corners[l] == curr_adjacent_corners[0]) {

               edge_to_cell[l + num_cell_per_edge[l] * num_adjacent_corners] =
                  curr_adjacent_cells[j];
               num_cell_per_edge[l]++;

            } else if (adjacent_corners[l] == curr_adjacent_corners[1]) {

               edge_to_cell[l + num_cell_per_edge[l] * num_adjacent_corners] =
                  curr_adjacent_cells[j];
               num_cell_per_edge[l]++;
            }
         }
      } // (j = 0; j < num_curr_adjacent_cells; ++j)

      // check whether we found two cells for each edge
      {
        size_t j;
        for (j = 0; j < num_adjacent_corners; ++j)
           if (num_cell_per_edge[j] != 2) break;
        if (j != num_adjacent_corners) continue;
      }

      // check whether we have enough space to store the new cell
      ENSURE_ARRAY_SIZE(
        cell_corner_dependencies, size_cell_corner_dependencies,
        num_total_cell_corner_dependencies + num_adjacent_corners);

      unsigned curr_corner = adjacent_corners[0];
      unsigned curr_cell = edge_to_cell[0];

      cell_corner_dependencies[num_total_cell_corner_dependencies] = curr_cell;

      unsigned * edge_cell_a = edge_to_cell;
      unsigned * edge_cell_b = edge_to_cell + num_adjacent_corners;

      // bring the corners into the correct order to form a cell
      for (size_t j = 1; j < num_adjacent_corners; ++ j) {

         for (size_t k = 0; k < num_adjacent_corners; ++k) {

            unsigned curr_adjacent_corner = adjacent_corners[k];
            if (curr_adjacent_corner == curr_corner) continue;

            if (edge_cell_a[k] == curr_cell) {
               curr_corner = curr_adjacent_corner;
               curr_cell = edge_cell_b[k];
               break;
            } else if (edge_cell_b[k] == curr_cell) {
               curr_corner = curr_adjacent_corner;
               curr_cell = edge_cell_a[k];
               break;
            }
         }

         cell_corner_dependencies[num_total_cell_corner_dependencies + j] =
           curr_cell;
      }

      num_corners_per_cell[num_total_cells++] = num_adjacent_corners;
      num_total_cell_corner_dependencies += num_adjacent_corners;
   } // (i = 0; i < get_num_grid_corners(*grid); ++i)

   free(num_cell_per_edge);
   free(edge_to_cell);
   yac_dep_list_delete(corner_to_cell);


   num_corners_per_cell =
      realloc(num_corners_per_cell,
              num_total_cells * sizeof(*num_corners_per_cell));
   cell_corner_dependencies =
      realloc(cell_corner_dependencies, num_total_cell_corner_dependencies *
              sizeof (cell_corner_dependencies[0]));

   struct dep_list * cell_to_vertex =
      yac_dep_list_new(
         num_total_cells, num_corners_per_cell, cell_corner_dependencies);

   struct unstruct_grid * cell_grid =
      (struct unstruct_grid *)yac_unstruct_grid_new(
         coordinates_x, coordinates_y, get_num_grid_cells_unstruct(grid),
         cell_to_vertex);

   cell_grid->cell_to_vertex_by_user = 1 == 0;

   return (struct grid *)cell_grid;
}

static struct grid * generate_edge_grid_unstruct(struct grid * grid,
                                                 double * coordinates_x, 
                                                 double * coordinates_y) {


  struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;
  size_t num_grid_cells = yac_get_num_elements(unstruct_grid->cell_to_vertex);
  size_t num_grid_corners = (size_t)(unstruct_grid->num_vertices);
  size_t num_grid_edges =
    yac_get_total_num_dependencies(unstruct_grid->corner_to_corner);

  if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
    generate_unstruct_edges(grid);

  size_t max_num_edge_grid_cells = num_grid_cells + num_grid_corners;
  // each edge can be a corner of four edge grid cells
  // (a potential cell for each adjacent corner and cell)
  size_t max_total_num_corners_per_edge_grid_cell = 4 * num_grid_edges;

  size_t * num_corners_per_edge_grid_cell =
    malloc(max_num_edge_grid_cells * sizeof(*num_corners_per_edge_grid_cell));
  unsigned * corners_of_edge_grid_cells =
    malloc(max_total_num_corners_per_edge_grid_cell *
           sizeof(*corners_of_edge_grid_cells));

  size_t num_edge_grid_cells = num_grid_cells;
  size_t total_num_corners_per_edge_grid_cell = 0;

  for (size_t i = 0; i < num_grid_cells; ++i)
    num_corners_per_edge_grid_cell[i] =
      yac_get_num_dependencies_of_element(unstruct_grid->cell_to_vertex, i);

  // for each grid cell
  for (size_t i = 0; i < num_grid_cells; ++i) {

    memcpy(
      corners_of_edge_grid_cells + total_num_corners_per_edge_grid_cell,
      get_cell_edge_indices_unstruct(grid, (unsigned)i),
      num_corners_per_edge_grid_cell[i] * sizeof(*corners_of_edge_grid_cells));

    total_num_corners_per_edge_grid_cell += num_corners_per_edge_grid_cell[i];
  }

  // for each grid corner
  for (size_t i = 0; i < num_grid_corners; ++i) {

    size_t curr_num_corner_edges =
      yac_get_num_dependencies_of_element(unstruct_grid->corner_to_corner, i);
    curr_num_corner_edges +=
      yac_get_num_dependencies_of_element(
        unstruct_grid->inv_corner_to_corner, i);

    // if the number of adjacent cells and edges is identical => inner corner
    if (yac_get_num_dependencies_of_element(
          unstruct_grid->vertex_to_cell, i) == curr_num_corner_edges) {

      num_corners_per_edge_grid_cell[num_edge_grid_cells] =
        curr_num_corner_edges;

      get_corner_edge_indices_unstruct(
        unstruct_grid, (unsigned)i,
        corners_of_edge_grid_cells + total_num_corners_per_edge_grid_cell);

      total_num_corners_per_edge_grid_cell += curr_num_corner_edges;
      num_edge_grid_cells++;
    }
  }

  num_corners_per_edge_grid_cell =
    realloc(num_corners_per_edge_grid_cell,
            num_edge_grid_cells * sizeof(*num_corners_per_edge_grid_cell));
  corners_of_edge_grid_cells =
    realloc(corners_of_edge_grid_cells,
            total_num_corners_per_edge_grid_cell *
            sizeof(*corners_of_edge_grid_cells));

  struct dep_list * cell_to_vertex =
    yac_dep_list_new(num_edge_grid_cells, num_corners_per_edge_grid_cell,
                     corners_of_edge_grid_cells);

  {
    struct grid * edge_grid =
      yac_unstruct_grid_new(
        coordinates_x, coordinates_y, (unsigned)num_grid_edges, cell_to_vertex);
    ((struct unstruct_grid*)edge_grid)->cell_to_vertex_by_user = 0;
    return edge_grid;
  }
}

static int get_aux_grid_cell_unstruct(struct grid * grid, unsigned corner_index,
                                      unsigned * cell_indices, enum yac_edge_type * edge_type) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   size_t j, k;
   unsigned * edge_to_cell;
   unsigned * num_cell_per_edge;

   size_t num_adjacent_cells =
      yac_get_num_dependencies_of_element(
         unstruct_grid->vertex_to_cell, (size_t)corner_index);

   // if the current corner has not enough adjacent cell to form the centre
   // of a new cell of the cell grid
   if (num_adjacent_cells <= 2) return 1 == 0;

   // gets the corners that are directly connected to the given corner by
   // an edge
   size_t num_adjacent_corners =
      (size_t)(get_num_corner_edges_unstruct(grid, corner_index));
   unsigned const * adjacent_corners =
      get_corner_edges_unstruct(grid, corner_index);

   num_cell_per_edge = calloc (num_adjacent_corners, sizeof (num_cell_per_edge[0]));
   edge_to_cell = malloc (2 * num_adjacent_corners * sizeof (edge_to_cell[0]));

   // gets all cells that have the given corner on their boundary definition
   unsigned const * adjacent_cells =
      yac_get_dependencies_of_element(
         unstruct_grid->vertex_to_cell, (size_t)corner_index);

   // for all cell adjacent to the given corner
   for (j = 0; j < num_adjacent_cells; ++j) {

      // gets the corners of the current cell (adjacent_cells[j])
      size_t num_curr_corners_of_cell =
         yac_get_num_dependencies_of_element(
            unstruct_grid->cell_to_vertex, (size_t)adjacent_cells[j]);
      unsigned const * curr_corners_of_cell =
         yac_get_dependencies_of_element(
            unstruct_grid->cell_to_vertex, (size_t)adjacent_cells[j]);

      // for all corners of the current cell
      // find the given corner in the boundary definition of the
      // current cell (adjacent_cells[j])
      for (k = 0; k < num_curr_corners_of_cell; ++k)
         if (curr_corners_of_cell[k] == corner_index) break;

      unsigned curr_adjacent_corners[2];

      // get the two associated edges
      if (k == 0) {
         curr_adjacent_corners[0] = curr_corners_of_cell[num_curr_corners_of_cell-1];
         curr_adjacent_corners[1] = curr_corners_of_cell[k+1];
      } else if (k == num_curr_corners_of_cell - 1){
         curr_adjacent_corners[0] = curr_corners_of_cell[k-1];
         curr_adjacent_corners[1] = curr_corners_of_cell[0];
      } else {
         curr_adjacent_corners[0] = curr_corners_of_cell[k-1];
         curr_adjacent_corners[1] = curr_corners_of_cell[k+1];
      }

      // for all adjacent corners of the given corner
      for (k = 0; k < num_adjacent_corners; ++k) {

         if (adjacent_corners[k] == curr_adjacent_corners[0]) {

            edge_to_cell[k + num_cell_per_edge[k] * num_adjacent_corners] =
               adjacent_cells[j];
            num_cell_per_edge[k]++;

         } else if (adjacent_corners[k] == curr_adjacent_corners[1]) {

            edge_to_cell[k + num_cell_per_edge[k] * num_adjacent_corners] =
               adjacent_cells[j];
            num_cell_per_edge[k]++;
         }
      }
   } // (j = 0; j < num_curr_adjacent_cells; ++j)

   // check whether we found two cells for each edge
   for (j = 0; j < num_adjacent_corners; ++j)
      if (num_cell_per_edge[j] != 2) break;

   free(num_cell_per_edge);

   // if the current corner is the centre of a cell grid cell
   if (j == num_adjacent_corners) {

      unsigned curr_corner;
      unsigned curr_cell;

      curr_corner = adjacent_corners[0];
      curr_cell = edge_to_cell[0];

      cell_indices[0] = curr_cell;

      // bring the corners into the correct order to form a cell
      for (j = 1; j < num_adjacent_corners; ++ j) {

         for (k = 0; k < num_adjacent_corners; ++k)
            if ((edge_to_cell[k] == curr_cell) && (adjacent_corners[k] != curr_corner)) {

               curr_corner = adjacent_corners[k];
               curr_cell = edge_to_cell[k + num_adjacent_corners];
               break;
            }

         if (k == num_adjacent_corners) {
            for (; k < 2*num_adjacent_corners; ++k)
               if ((edge_to_cell[k] == curr_cell) && (k != curr_corner +
                                                      num_adjacent_corners)) {
                     k -= num_adjacent_corners;
                     curr_corner = adjacent_corners[k];
                     curr_cell = edge_to_cell[k];
                     break;
                  }
         }

         cell_indices[j] = curr_cell;
      }

      for (j = 0; j < num_adjacent_corners; ++j)
         edge_type[j] = GREAT_CIRCLE;
   }

   free(edge_to_cell);

   return j == num_adjacent_corners;
}

static struct grid * copy_grid_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   struct unstruct_grid * copy;
   double * coordinates_x, * coordinates_y;
   unsigned x_coords_size = get_size_x_coords_unstruct(grid) * sizeof(*coordinates_x);
   unsigned y_coords_size = get_size_y_coords_unstruct(grid) * sizeof(*coordinates_y);
   unsigned num_vertices;

   coordinates_x = malloc(x_coords_size);
   coordinates_y = malloc(y_coords_size);
   memcpy(coordinates_x, unstruct_grid->cell_corners_x, x_coords_size);
   memcpy(coordinates_y, unstruct_grid->cell_corners_y, y_coords_size);

   num_vertices = get_num_grid_corners_unstruct(grid);

   struct dep_list * cell_to_vertex =
      yac_copy_dep_list(unstruct_grid->cell_to_vertex);

   copy = (struct unstruct_grid *)yac_unstruct_grid_new(coordinates_x, coordinates_y,
                                                        num_vertices, cell_to_vertex);

   copy->cell_to_vertex_by_user = 1 == 0;
   copy->cell_corners_x_by_user = 1 == 0;
   copy->cell_corners_y_by_user = 1 == 0;

   return (struct grid *)copy;
}

static void pack_grid_unstruct(
   struct grid * grid, void * buffer, int buffer_size, int * position,
   struct communicator * comm) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   yac_comm_pack(&(unstruct_grid->num_vertices), 1, COMM_UINT,
                 buffer, buffer_size, position, comm);

   yac_comm_pack(unstruct_grid->cell_corners_x, unstruct_grid->num_vertices,
                 COMM_DBLE, buffer, buffer_size, position, comm);
   yac_comm_pack(unstruct_grid->cell_corners_y, unstruct_grid->num_vertices,
                 COMM_DBLE, buffer, buffer_size, position, comm);

   yac_pack_dep_list(
      unstruct_grid->cell_to_vertex, buffer, buffer_size, position, comm);
}

struct grid * yac_unpack_unstruct_grid(
  void * buffer, int buffer_size, int * position, struct communicator * comm) {

  unsigned num_vertices;
  yac_comm_unpack(buffer, buffer_size, position, &num_vertices,
                  1, COMM_UINT, comm);

  double * cell_corners_x = malloc(num_vertices * sizeof(*cell_corners_x));
  double * cell_corners_y = malloc(num_vertices * sizeof(*cell_corners_y));
  yac_comm_unpack(buffer, buffer_size, position, cell_corners_x,
                  num_vertices, COMM_DBLE, comm);
  yac_comm_unpack(buffer, buffer_size, position, cell_corners_y,
                  num_vertices, COMM_DBLE, comm);
  
  struct dep_list * cell_to_vertex =
    yac_unpack_dep_list(buffer, buffer_size, position, comm);

  struct unstruct_grid * unstruct_grid =
    (struct unstruct_grid *)yac_unstruct_grid_new(
      cell_corners_x, cell_corners_y, num_vertices, cell_to_vertex);

  unstruct_grid->cell_to_vertex_by_user = 1 == 0;
  unstruct_grid->cell_corners_x_by_user = 1 == 0;
  unstruct_grid->cell_corners_y_by_user = 1 == 0;

  return (struct grid *)unstruct_grid;
}

static int grid_pack_size_unstruct(
   struct grid * grid, struct communicator * comm) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   int pack_size = yac_comm_pack_size(1, COMM_UINT, comm);
   pack_size    += yac_comm_pack_size(2* unstruct_grid->num_vertices, COMM_DBLE, comm);
   pack_size    += yac_dep_list_pack_size(unstruct_grid->cell_to_vertex, comm);

   return pack_size;
}

static void delete_grid_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   yac_dep_list_delete(unstruct_grid->cell_to_neigh);
   if (!unstruct_grid->cell_to_vertex_by_user)
      yac_dep_list_delete(unstruct_grid->cell_to_vertex);
   yac_dep_list_delete(unstruct_grid->vertex_to_cell);
   yac_dep_list_delete(unstruct_grid->corner_to_corner);
   yac_dep_list_delete(unstruct_grid->inv_corner_to_corner);

   if (!unstruct_grid->cell_corners_x_by_user)
      free(unstruct_grid->cell_corners_x);
   if (!unstruct_grid->cell_corners_y_by_user)
      free(unstruct_grid->cell_corners_y);
   free(unstruct_grid->cell_corners_xyz);

   if (unstruct_grid->grid_search != NULL)
      yac_delete_grid_search(unstruct_grid->grid_search);

   free(corners);
   corners = NULL;
   size_corners = 0;
   free(edges);
   edges = NULL;
   size_edges = 0;
   free(cell_corners);
   cell_corners = NULL;
   size_cell_corners = 0;

   free(unstruct_grid);
}

static struct grid * generate_subgrid_unstruct(struct grid * grid,
                                               unsigned * selected_local_cell_ids,
                                               unsigned num_local_cells,
                                               unsigned ** local_cell_ids,
                                               unsigned ** local_corner_ids,
                                               unsigned ** local_edge_ids) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   unsigned i, k;

   unsigned * subgrid_corners;
   unsigned num_subgrid_corners;

   size_t * num_corners_per_cell;
   size_t total_num_cell_corners;

   //--------------------------------------------------------
   // generate a list of all corners required for the subgrid
   //--------------------------------------------------------

// NBITS is the number of bits in an unsigned
#define NBITS (sizeof(unsigned)*8)

   // the mask contains a bit (initially set to zero) for each corner of the grid
   unsigned * corner_mask;
   unsigned num_grid_corners;

   num_grid_corners = yac_get_num_grid_corners(grid);

   // an unsigned normally has 32 bit -> for a grid with 128 corners we need a
   // corner_mask with an array size of 4
   corner_mask = calloc(((num_grid_corners + NBITS - 1) / NBITS ), sizeof(*corner_mask));

   num_corners_per_cell = malloc(num_local_cells * sizeof(*num_corners_per_cell));
   total_num_cell_corners = 0;
   num_subgrid_corners = 0;

   // for all selected cells
   for (i = 0; i < num_local_cells; ++i) {

      unsigned const * corner_ids;
      size_t num_corners;

      // get the ids of corners of the current cell
      corner_ids = get_cell_corner_indices_unstruct(grid, selected_local_cell_ids[i]);
      num_corners =
         (size_t)get_num_cell_corners_unstruct(grid, selected_local_cell_ids[i]);

      num_corners_per_cell[i] = num_corners;
      total_num_cell_corners += num_corners;

      // for all the corners of the current cell
      for (size_t j = 0; j < num_corners; ++j) {

         // if the respective bit in corner_mask for the current corner is not yet set
         if (!(corner_mask[corner_ids[j] / NBITS ] & (1 << (corner_ids[j] & ( NBITS - 1))))) {

            // set the respective bit
            corner_mask[corner_ids[j] / NBITS ] |= 1 << (corner_ids[j] & ( NBITS - 1));
            num_subgrid_corners++;
         }
      }
   }

   subgrid_corners = malloc(num_subgrid_corners * sizeof(*subgrid_corners));

   num_subgrid_corners = 0;

   // read the data from corner_mask and convert the set bits into corner ids
   for (i = 0; i < (num_grid_corners + NBITS - 1) / NBITS; ++i)
      for (size_t j = 0; j < NBITS; ++j)
         if (corner_mask[i] & 1 << j)
            subgrid_corners[num_subgrid_corners++] = i * NBITS + j;

   free(corner_mask);

#undef NBITS

   //----------------------------------------------------------
   // generate a cell to vertex dependency list for the subgrid
   //----------------------------------------------------------

   unsigned curr_index;

   curr_index = 0;

   unsigned * cell_to_corner_dep;

   cell_to_corner_dep = malloc(total_num_cell_corners * sizeof(*cell_to_corner_dep));

   total_num_cell_corners = 0;

   for (i = 0; i < num_local_cells; ++i) {

      unsigned const * corner_ids;
      size_t num_corners;

      corner_ids = get_cell_corner_indices_unstruct(grid, selected_local_cell_ids[i]);
      num_corners =
         (size_t)get_num_cell_corners_unstruct(grid, selected_local_cell_ids[i]);

      for (size_t j = 0; j < num_corners; ++j) {

         while (subgrid_corners[curr_index] > corner_ids[j]) --curr_index;
         while (subgrid_corners[curr_index] < corner_ids[j]) ++curr_index;

         cell_to_corner_dep[total_num_cell_corners+j] = curr_index;
      }
      total_num_cell_corners += num_corners;
   }

   struct dep_list * cell_to_corner =
      yac_dep_list_new(
         (size_t)num_local_cells, num_corners_per_cell, cell_to_corner_dep);

   // get the corner coordinates of the subgrid

   double * cell_corners_x, * cell_corners_y;

   cell_corners_x = malloc(num_subgrid_corners * sizeof(*cell_corners_x));
   cell_corners_y = malloc(num_subgrid_corners * sizeof(*cell_corners_y));

   for (i = 0; i < num_subgrid_corners; ++i) {

      cell_corners_x[i] = unstruct_grid->cell_corners_x[subgrid_corners[i]];
      cell_corners_y[i] = unstruct_grid->cell_corners_y[subgrid_corners[i]];
   }

   // generate subgrid

   struct unstruct_grid * subgrid;

   subgrid = (struct unstruct_grid *)yac_unstruct_grid_new(cell_corners_x, cell_corners_y,
                                                           num_subgrid_corners,
                                                           cell_to_corner);

   subgrid->cell_corners_x_by_user = 1 == 0;
   subgrid->cell_corners_y_by_user = 1 == 0;
   subgrid->cell_to_vertex_by_user = 1 == 0;

   // set the local_cell_ids array

   *local_cell_ids = malloc(num_local_cells * sizeof(**local_cell_ids));

   memcpy(*local_cell_ids, selected_local_cell_ids, num_local_cells * sizeof(**local_cell_ids));

   // set the local_corner_ids array

   *local_corner_ids = subgrid_corners;

   // set the local_edge_ids array

   unsigned num_subgrid_edges;

   num_subgrid_edges = get_num_grid_edges_unstruct((struct grid*)subgrid);

   *local_edge_ids = malloc(num_subgrid_edges * sizeof(**local_edge_ids));

   k = 0;

   if (yac_get_num_elements(unstruct_grid->corner_to_corner) == 0)
      generate_unstruct_edges (grid);

   for (i = 0; i < num_subgrid_corners; ++i) {

      unsigned const * subgrid_edges =
         yac_get_dependencies_of_element(subgrid->corner_to_corner, (size_t)i);
      size_t num_curr_subgrid_edges =
         yac_get_num_dependencies_of_element(
            subgrid->corner_to_corner, (size_t)i);

      for (size_t j = 0; j < num_curr_subgrid_edges; ++j) {

         (*local_edge_ids)[k++] =
            (unsigned)yac_get_dependency_index(
               unstruct_grid->corner_to_corner, (size_t)(*local_corner_ids)[i],
               (*local_corner_ids)[subgrid_edges[j]]);
      }
   }

   return (struct grid *)subgrid;
}

// this check seems cause some problems for ICON
#define DEACTIVATE_CHECK_CELL_TO_VERTEX
#ifndef DEACTIVATE_CHECK_CELL_TO_VERTEX
static int check_cell_to_vertex(struct dep_list * cell_to_vertex) {

  // check for zero-length edges
  for (size_t i = 0; i < cell_to_vertex.num_elements; ++i) {

    unsigned const * curr_cell_vertices =
      yac_get_dependencies_of_element(cell_to_vertex, i);
    size_t curr_num_cell_vertices =
      yac_get_num_dependencies_of_element(cell_to_vertex, i);

    for (size_t j = 1; j < curr_num_cell_vertices; ++j)
      if (curr_cell_vertices[j-1] == curr_cell_vertices[j])
        return 1;
    if (curr_cell_vertices[0] == curr_cell_vertices[curr_num_cell_vertices-1])
      return 1;
  }

  return 0;
}
#endif

struct grid * yac_unstruct_grid_new(double * coordinates_x, double * coordinates_y,
                                    unsigned num_vertices,
                                    struct dep_list * cell_to_vertex) {
#ifndef DEACTIVATE_CHECK_CELL_TO_VERTEX
   if (check_cell_to_vertex(cell_to_vertex))
      yac_internal_abort_message(
        "ERROR(yac_unstruct_grid_new): zero-length edge detected (a cell in "
        "cell_to_vertex contain at least two identical consecutive "
        "vertex indices)\n", __FILE__, __LINE__);
#endif

   struct unstruct_grid * unstruct_grid;

   unstruct_grid = malloc(1 * sizeof(*unstruct_grid));

   unstruct_grid->vtable = &unstruct_grid_vtable;
   unstruct_grid->grid_type = YAC_UNSTRUCT;

   unstruct_grid->cell_to_neigh = NULL;

   unstruct_grid->cell_corners_x = coordinates_x;
   unstruct_grid->cell_corners_y = coordinates_y;
   unstruct_grid->cell_corners_xyz =
      malloc(num_vertices * sizeof(*(unstruct_grid->cell_corners_xyz)));

   for (unsigned i = 0; i < num_vertices; ++i)
      LLtoXYZ(unstruct_grid->cell_corners_x[i],
              unstruct_grid->cell_corners_y[i],
              unstruct_grid->cell_corners_xyz[i]);

   unstruct_grid->cell_to_vertex = cell_to_vertex;
   unstruct_grid->vertex_to_cell = yac_invert_dep_list(cell_to_vertex);
   unstruct_grid->num_vertices = num_vertices;

   unstruct_grid->corner_to_corner = NULL;
   unstruct_grid->inv_corner_to_corner = NULL;

   unstruct_grid->cell_to_vertex_by_user = 1 == 1;
   unstruct_grid->cell_corners_x_by_user = 1 == 1;
   unstruct_grid->cell_corners_y_by_user = 1 == 1;

   unstruct_grid->grid_search = NULL;

   return (struct grid *)unstruct_grid;
}

static struct grid_search * get_grid_search_unstruct(struct grid * grid) {

   struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

   if (unstruct_grid->grid_search == NULL)
      unstruct_grid->grid_search = yac_sphere_part_search_new(grid);

   return unstruct_grid->grid_search;
}

static int cell_contains_non_gc_edge_unstruct(
  struct grid * grid, unsigned local_cell_id) {

  UNUSED(grid);
  UNUSED(local_cell_id);

  return 0;
}

void yac_unstruct_grid_set_cell_to_vertex_by_user(
  struct grid * grid, unsigned cell_to_vertex_by_user) {

  struct unstruct_grid * unstruct_grid = (struct unstruct_grid *)grid;

  unstruct_grid->cell_to_vertex_by_user = cell_to_vertex_by_user;
}

