// A rectangular block of data of type uint8_t.  A transparent block
// cache is used for each instance of this class if necessary, so the
// image can be much bigger than will fit in memory, and pixels can
// still be accessed and written efficiently, provided subsequent
// accesses are spatially correlated.  A variety of useful methods are
// implemented (filtering, subsetting, interpolating, etc.)
//
// Don't try to access the same instance concurrently.  Split your
// images up into separate instances if you must parallelize things.
//
// For many methods, arguments of type ssize_t are used, but are not
// allowed to be negative.  This is to help prevent people from
// shooting themselves in the foor by accidently passing negative
// values which don't yield warnings and can't be caught with
// assertions.

#ifndef UINT8_IMAGE_H
#define UINT8_IMAGE_H

#ifndef solaris
#  include <stdint.h>
#endif
#include "asf_meta.h"
#include <stdio.h>
#include <sys/types.h>

#include <glib.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_histogram.h>

// sometimes we don't have these - choose conservative values
#ifndef SSIZE_MAX
#define SSIZE_MAX 32767
#endif

#ifndef UINT8_MAX
#define UINT8_MAX 255
#endif

// Instance structure.  Everything here is private and need not be
// used or understood by client code, except for the size_x and size_y
// fields.
typedef struct {
  size_t size_x, size_y;	// Image dimensions.
  size_t cache_space;		// Memory cache space in bytes.
  size_t cache_area;		// Memory cache area in pixels.
  size_t tile_size;		// Tile size in pixels on a side.
  size_t cache_size_in_tiles;	// Number of tiles in cache.
  size_t tile_count_x;		// Number of tiles in image in x direction.
  size_t tile_count_y;		// Number of tiles in image in y direction.
  size_t tile_count;		// Total number of tiles in image.
  size_t tile_area;             // Area of a tile, in pixels.
  uint8_t *cache;		// Memory cache.
  uint8_t **tile_addresses;	// Addresss of individual tiles in the cache.
  GQueue *tile_queue;		// Queue of tile offsets kept in load order.
  FILE *tile_file;              // File with tiles stored contiguously.
  GString *tile_file_name;  // Filename of the tile file
} UInt8Image;

///////////////////////////////////////////////////////////////////////////////
//
// Creating New Instances
//
///////////////////////////////////////////////////////////////////////////////

// Thaw out a previously frozen instance (produced with
// uint8_image_freeze) using data pointed to by file_pointer.  Frozen
// instances aren't portable between platforms.  After thawing
// file_pointer points to the data immediately following the data from
// the thawed instance (or to the end of the file).
UInt8Image *
uint8_image_thaw (FILE *file_pointer);

// Create a new image filled with zero pixel values.
UInt8Image *
uint8_image_new (ssize_t size_x, ssize_t size_y);

// Create a new image with pixels initialized to value.
UInt8Image *
uint8_image_new_with_value (ssize_t size_x, ssize_t size_y, uint8_t value);

// Create a new image from memory.  This pixels are assumed to be
// layed out in memory in the usual way, i.e. contiguous rows of
// pixels in the x direction are contiguous in memory.
UInt8Image *
uint8_image_new_from_memory (ssize_t size_x, ssize_t size_y, uint8_t *buffer);

// Create a new independent copy of model.
UInt8Image *
uint8_image_copy (UInt8Image *model);

// Form reduced resolution version of the model.  The scale_factor
// must be positive and odd.  The new image will be round ((double)
// model->size_x / scale_factor) pixels by round ((double)
// model->size_y / scale_factor) pixels.  Scaling is performed by
// averaging blocks of pixels together, using odd pixel reflection
// around the image edges (see the description of the apply_kernel
// method).  The average for a block of pixels is first computed as a
// double precision floating point number, then rounded with the C99
// round() function.  The upper and leftmost blocks of pixels averaged
// together are always centered at the 0 index in the direction in
// question, so reflection is always used for these edges.  Whether
// reflection is used for the right and lower edges depends on the
// relationship between the model dimensions and the scale factor.
UInt8Image *
uint8_image_new_from_model_scaled (UInt8Image *model, ssize_t scale_factor);

// Create a new image by copying the portion of model with upper left
// corner at model coordinates (x, y), width size_x, and height
// size_y.
UInt8Image *
uint8_image_new_subimage (UInt8Image *model, ssize_t x, ssize_t y,
			  ssize_t size_x, ssize_t size_y);

// Create a new image from data at byte offset in file.  The pixel
// layout in the file is assumed to be the same as for the
// uint8_image_new_from_memory method.  The byte order of individual
// pixels in the file should be byte_order.
UInt8Image *
uint8_image_new_from_file (ssize_t size_x, ssize_t size_y, const char *file,
			   off_t offset);

// The method is like new_from_file, but takes a file pointer instead
// of a file name, and the offset argument is with respect to the
// current position in the file_pointer stream.
UInt8Image *
uint8_image_new_from_file_pointer (ssize_t size_x, ssize_t size_y,
				   FILE *file_pointer, off_t offset);

// Form a low quality reduced resolution version of the
// original_size_x by original_size_y image in file.  The new image
// will be size_x by size_y pixels.  This method is like new_from_file
// method, but gets its data by sampling in each dimension using
// bilinear interpolation, and rounding the interpolated values using
// the C99 round() routine.  This is a decent way of forming quick
// thumbnails of images, or of scaling images down just slightly (by a
// factor of say 1.5 or less), but not much else.
UInt8Image *
uint8_image_new_from_file_scaled (ssize_t size_x, ssize_t size_y,
				  ssize_t original_size_x,
				  ssize_t original_size_y,
				  const char *file, off_t offset);

// The function that does it all, generating an instance of UInt8Image
// from a file and the metadata
UInt8Image *
uint8_image_new_from_metadata(meta_parameters *meta, const char *file);

// For multi-band imagery the previous function needs to be more specific.
UInt8Image *
uint8_image_band_new_from_metadata(meta_parameters *meta,
				   int band, const char *file);

///////////////////////////////////////////////////////////////////////////////
//
// Getting and Setting Image Pixels and Regions
//
///////////////////////////////////////////////////////////////////////////////

// Get pixel at 0-indexed position x, y.  A cache is used so pixel
// access does not usually involve the hard disk, provided subsequent
// pixel lookups are spatially close together.  You can probably get
// away with trating this function just as if everything was in
// memory.  For details, see the cache control methods below.
//
// The x and y arguments should always be positive, ssize_t is used
// only so people can't as easily shoot themselves in the foot by
// accidently supplying negative arguments which get promoted in some
// strange way.
uint8_t
uint8_image_get_pixel (UInt8Image *self, ssize_t x, ssize_t y);

// Set pixel at 0-indexed position x, y to value.  A cache is used to
// make this fast, as for the uint8_image_get_pixel method.
void
uint8_image_set_pixel (UInt8Image *self, ssize_t x, ssize_t y, uint8_t value);

// Get rectangular image region of size_x, size_y having upper left
// corner at x, y and copy it into already allocated buffer.  There is
// not necessarily any caching help for this method, i.e. it may
// always involve disk access and always be slow.
void
uint8_image_get_region (UInt8Image *self, ssize_t x, ssize_t y,
			ssize_t size_x, ssize_t size_y, uint8_t *buffer);

// This method is analogous to uint8_image_get_region.
void
uint8_image_set_region (UInt8Image *self, size_t x, size_t y, size_t size_x,
			size_t size_y, uint8_t *buffer);

// Get a full row of pixels, copying the data into already allocated
// buffer.  This method will be fast on the average for calls with
// sequential row numbers.
void
uint8_image_get_row (UInt8Image *self, size_t row, uint8_t *buffer);

// Get a pixel, performing odd reflection at image edges if the pixel
// indicies fall outside the image.  See the description of the
// apply_kernel method for an explanation of reflection.
uint8_t
uint8_image_get_pixel_with_reflection (UInt8Image *self, ssize_t x, ssize_t y);

///////////////////////////////////////////////////////////////////////////////
//
// Image Analysis and Statistics
//
///////////////////////////////////////////////////////////////////////////////

// Default mask value when figuring image stats
#define UINT8_IMAGE_DEFAULT_MASK (0.0)

// Finds the minimum and maximum pixel values in the image, and the
// mean and standard deviation of all pixels.  This function considers
// every pixel in the image when mask is NAN, otherwise it discounts
// all values within .00000000001 of the mask
void
uint8_image_statistics (UInt8Image *self, uint8_t *min, uint8_t *max,
			double *mean, double *standard_deviation,
			gboolean use_mask_value, uint8_t mask_value);

// Does the same thing as uint8_image_statistics() except that the
// statistics are calculated for a particular band selection within
// a UInt8Image which contains multiple bands or channels in sequential
// order.  Returns 1 on error, otherwise 0
int
uint8_image_band_statistics (UInt8Image *self, meta_stats *stats,
                             int line_count, int band_no,
                             gboolean use_mask_value, uint8_t mask_value);

// This method works like the statistics method, except values in the
// interval [interval_start, interval_end] are not considered at all
// for the purposes of determining any of the outputs.
void
uint8_image_statistics_with_mask_interval (UInt8Image *self, uint8_t *min,
					   uint8_t *max, double *mean,
					   double *standard_deviation,
					   uint8_t interval_start,
					   uint8_t interval_end);

// Compute an efficient estimate of the mean and standard deviation of
// the pixels in the image, by sampling every stride th pixel in each
// dimension, beginning with pixel (0, 0). If the mask is a non-NAN
// value, this function will discount all values within .00000000001
// of the mask
void
uint8_image_approximate_statistics (UInt8Image *self, size_t stride,
                                    double *mean, double *standard_deviation,
				    gboolean use_mask_value,
				    uint8_t mask_value);

// This method is a logical combination of the
// statistics_with_mask_interval and approximate_statistics methods.
void
uint8_image_approximate_statistics_with_mask_interval
  (UInt8Image *self, size_t stride, double *mean, double *standard_deviation,
   uint8_t interval_start, uint8_t interval_end);

// Creates a gsl_histogram with bin_count bins evenly spaced over the
// interval [min, max].  This function considers every pixel in the
// image.
gsl_histogram *
uint8_image_gsl_histogram (UInt8Image *self, double min, double max,
                           size_t bin_count);

///////////////////////////////////////////////////////////////////////////////
//
// Kernels, Interpolation, and Sampling
//
///////////////////////////////////////////////////////////////////////////////

// Apply kernel centerd at pixel x, y and return the value.  The
// kernel matrix must be have equal odd dimensions.  The values in the
// kernel are multiplied by the pixels, and the sum of the products
// returned.  When part of the kernel would fall outside the image
// extents, the values used for the out-of-image pixels are the mirror
// images of the corresponding in-image pixels, with the edge pixels
// not duplicated, i.e. reflection about the middle of the edge pixels
// is used.
double
uint8_image_apply_kernel (UInt8Image *self, ssize_t x, ssize_t y,
			  gsl_matrix *kern);

// Type used to specify whether disk files should be in big or little
// endian byte order.

// Sample method types.  These dictate how nearby pixels are
// considered when we want to find the approximate value for a point
// which falls between pixel indicies.
typedef enum {
  // Nearest pixel.
  UINT8_IMAGE_SAMPLE_METHOD_NEAREST_NEIGHBOR,
  // Linearly weited average of four nearest pixels
  UINT8_IMAGE_SAMPLE_METHOD_BILINEAR,
  // Bicubic spline interpolation (which consideres the nearest 16 pixels).
  UINT8_IMAGE_SAMPLE_METHOD_BICUBIC
} uint8_image_sample_method_t;

double
uint8_image_sample (UInt8Image *self, double x, double y,
		    uint8_image_sample_method_t sample_method);

///////////////////////////////////////////////////////////////////////////////
//
// Comparing Images
//
///////////////////////////////////////////////////////////////////////////////

// Return true iff self and other have identical sizes and pixels.
gboolean
uint8_image_equals (UInt8Image *self, UInt8Image *other);

///////////////////////////////////////////////////////////////////////////////
//
// Manipulating Images
//
///////////////////////////////////////////////////////////////////////////////

// Flip an image about a horizontal line through the center of the image
void uint8_image_flip_y(UInt8Image *self);

// Flip an image about a vertical line through the center of the image
void uint8_image_flip_x(UInt8Image *self);

///////////////////////////////////////////////////////////////////////////////
//
// Storing Images in Files
//
///////////////////////////////////////////////////////////////////////////////

// Store instance self at position pointed to by file_pointer, for
// later retrieval using uint8_image_thaw.  The serialized version of
// self is not portable between platforms.
void
uint8_image_freeze (UInt8Image *self, FILE *file_pointer);

// Store image pixels in file.  The image is stored in the usual
// order, i.e. contiguous rows of pixels in the x direction are stored
// contiguously in memory.  Individual pixels are stored in byte order
// byte_order.  Returns 0 on success, nonzero on error.
int
uint8_image_store (UInt8Image *self, const char *file);

int
uint8_image_store_ext(UInt8Image *self, const char *file, int append_flag);

int
uint8_image_band_store(UInt8Image *self, const char *file,
		       meta_parameters *meta, int append_flag);

///////////////////////////////////////////////////////////////////////////////
//
// Exporting Images in Various Image File Formats
//
///////////////////////////////////////////////////////////////////////////////

// Export image to fila as a gray scaled jpeg image, with largest
// dimension no larger than max_dimension.  The max_dimension argument
// must be less than or equal to the largest dimension of the image.
// The image may be scaled st its largest dimension is considerably
// less than max_dimension.  Scaling is performed by averaging blocks
// of pixels together, using odd pixel reflection around the image
// edges (see the description of the apply_kernel method), and
// rounding pixel averages with the C99 round() routine.  If
// use_mask_value is true, then in determining the image statistics
// (mean and standard deviation), values equal to mask are not
// considered.  This routine slurps the whole image into memory, so
// beware.  Returns 0 on success, nonzero on error.
int
uint8_image_export_as_jpeg (UInt8Image *self, const char *file,
			    size_t max_dimension, gboolean use_mask_value,
			    uint8_t mask_value);

///////////////////////////////////////////////////////////////////////////////
//
// Controlling Image Data Caching
//
// It probably isn't necessary to use these methods.  They are
// provided largely to make it clear how the cache works.  The major
// tunable parameter is the size of the in-memory cache to use.
//
// When a new image is created, the following steps are performed:
//
//      1. The image is divided up into square tiles st two full rows
//         or columns of tiles will fit in the memory cache.
//
//      2. A copy of the image is created on disk with the memory
//         layout rearranged st individual tiles are contiguous in
//         memory.  This allows tiles to be quickly retrieved later.
//
// When a pixel is accessed (read or set), the following happens:
//
//      1. If the pixel is in a tile already loaded into the cache,
//         it is simply fetched or set.
//
//      2. Otherwise, the tile containing the pixel is loaded,
//         possibly displacing an already loaded tile, and then the
//         pixel is fetched or set.  The tile displaced is the one
//         loaded longest ago (there is no most-recently-accessed
//         heuristic, as this would make pixel access too slow).
//
// Thus, using a larger memory cache will result in larger tiles being
// used, and fewer tile loads being needed.  In general, the default
// behavior is pretty good, but if you know will be performing lots of
// widely (but not too widely) scattered accesses, you might want to
// make it bigger.
//
///////////////////////////////////////////////////////////////////////////////

// Get the image memory cache size setting, in bytes.  Note that this
// is the memory cache used per image, not the class-wide cache usage.
// If you will have a lot of objects instantiated simultaneously, you
// may find it necessary to use a smaller cache for each image.
size_t
uint8_image_get_cache_size (UInt8Image *self);

// Set the image memory cache to size bytes.  Changing the cache size
// requires the tiling to be recomputed, the on-disk tile cache to be
// regenerated, and the in memory cache to be flushed, so its slow.
void
uint8_image_set_cache_size (UInt8Image *self, size_t size);

///////////////////////////////////////////////////////////////////////////////
//
// Freeing Instances
//
///////////////////////////////////////////////////////////////////////////////

// Destroy self.
void
uint8_image_free (UInt8Image *self);

#endif // #ifndef UINT8_IMAGE_H


#ifndef UTILS_H
#define UTILS_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h> // for printf
#include <stdlib.h> // for posix_memalign
#include <string.h> // for memset
#include <math.h> // for sqrt
#include <float.h> //for FLT_MAX
#include <time.h> // for clock_gettime
#include <cblas.h>

#define STACK_SIZE_TOTAL 0x8000000 // 128Mbytes

typedef struct
    {
    int index;
    float score;
    }score_index_struct;

typedef struct
    {
    float x;
    float y;
    float w;
    float h;
    }box_struct;

typedef struct
    {
    void *stack_starting_address;
    char *stack_current_address;
    unsigned int stack_current_alloc_size;
    }stack_struct;

void init_stack
    (
    void
    );

void free_stack
    (
    void
    );

void *alloc_from_stack
    (
    unsigned int len
    );

void partial_free_from_stack
    (
    unsigned int len
    );

unsigned int get_stack_current_alloc_size
    (
    void
    );

void reset_stack_ptr_to_assigned_position
    (
    unsigned int assigned_size
    );

double what_time_is_it_now
    (
    void
    );

#ifdef __cplusplus
}
#endif

#endif // UTILS_H


/*
 * Copyright 2020 Makani Technologies LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIB_JSON_LOAD_JSON_ARRAY_LOADER_H_
#define LIB_JSON_LOAD_JSON_ARRAY_LOADER_H_

#include <glog/logging.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <jansson.h>
#include <stdint.h>

#include <array>
#include <string>
#include <vector>

#include "common/c_math/vec3.h"
#include "lib/json_load/json_load_or_die.h"

// TODO: Put this under the lib namespace.
namespace json_load {

// Utility class for loading JSON arrays into various vector and array
// structures such as Vec3, gsl_vector, and gsl_matrix.
class JsonArrayLoader {
 public:
  JsonArrayLoader(int32_t num_rows, int32_t num_cols, json_t *root)
      : root_(CHECK_NOTNULL(root)),
        num_rows_(num_rows),
        num_cols_(num_cols),
        matrix_buffer_(num_rows),
        buffer_(0) {
    CHECK_LT(0, num_rows);
    CHECK_LT(0, num_cols);
  }

  virtual ~JsonArrayLoader() {}

  template <int32_t rank>
  void LoadVector(const std::string &field_name,
                  const std::array<int32_t, rank> &dims, gsl_vector *dest) {
    CHECK_NOTNULL(dest);
    int32_t size = 1;
    for (int32_t i = 0; i < rank; ++i) size *= dims[i];
    CHECK_EQ(size, dest->size);

    LoadBuffer(field_name, size, &buffer_);
    CopyVector(buffer_, dest);
  }

  void LoadVector(const std::string &field_name, gsl_vector *dest) {
    LoadVector<1>(field_name, {{static_cast<int32_t>(dest->size)}}, dest);
  }

  void LoadVec3(const std::string &field_name, Vec3 *dest) {
    LoadBuffer(field_name, 3, &buffer_);
    dest->x = buffer_[0];
    dest->y = buffer_[1];
    dest->z = buffer_[2];
  }

  void LoadMatrix(const std::string &field_name, gsl_matrix *dest) {
    CHECK_NOTNULL(dest);

    json_t *field = json_load::LoadFieldOrDie(root_, field_name);
    CHECK(json_is_array(field));

    LoadMatrixFromField(field, dest);
  }

 protected:
  void LoadMatrixFromField(json_t *field, gsl_matrix *dest) {
    for (int32_t i = 0; i < num_rows_; ++i) {
      json_t *slice = json_array_get(field, i);
      CHECK(json_is_array(slice));
      CHECK_EQ(num_cols_, json_array_size(slice));
      json_load::LoadArray1D_DoubleOrDie(slice, num_cols_, &matrix_buffer_[i]);

      for (int32_t j = 0; j < num_cols_; ++j) {
        gsl_matrix_set(dest, i, j, matrix_buffer_[i][j]);
      }
    }
  }

  json_t *root_;

 private:
  void LoadBuffer(const std::string &field_name, int32_t size,
                  std::vector<double> *buffer) {
    CHECK_NOTNULL(buffer);
    json_t *array = json_load::LoadFieldOrDie(root_, field_name);
    CHECK(json_is_array(array));
    CHECK_EQ(size, json_array_size(array));
    json_load::LoadArray1D_DoubleOrDie(array, size, buffer);
  }

  void CopyVector(const std::vector<double> &buffer, gsl_vector *dest) {
    CHECK_NOTNULL(dest);
    CHECK_EQ(buffer.size(), dest->size);
    for (uint32_t i = 0U; i < buffer.size(); ++i) {
      gsl_vector_set(dest, i, buffer[i]);
    }
  }

  const int32_t num_rows_;
  const int32_t num_cols_;

  std::vector<std::vector<double>> matrix_buffer_;
  std::vector<double> buffer_;

  DISALLOW_COPY_AND_ASSIGN(JsonArrayLoader);
};

}  // namespace json_load

#endif  // LIB_JSON_LOAD_JSON_ARRAY_LOADER_H_


#ifndef CWANNIER_DOS_UTIL
#define CWANNIER_DOS_UTIL

#include <stdlib.h>
#include <stdio.h>
#include "bstrlib/bstrlib.h"
#include <gsl/gsl_matrix.h>

gsl_matrix* parse_R_from_bs(char *b1, char *b2, char *b3);
int set_R_row(gsl_matrix *R, char *b, int row);
double* linspace(double a, double b, int num);

#endif //CWANNIER_DOS_UTIL


#ifndef LIB_INCLUDE_TICK_ARRAY_VECTOR_OPERATIONS_H_
#define LIB_INCLUDE_TICK_ARRAY_VECTOR_OPERATIONS_H_

// License: BSD 3 clause

#include <atomic>
#include <vector>
#include <numeric>
#include <algorithm>
#include <type_traits>

#include "promote.h"
#include "tick/base/defs.h"

#if defined(TICK_USE_MKL)

#include "mkl.h"

#elif defined(TICK_USE_CBLAS)

#if defined(__APPLE__)
#include <Accelerate/Accelerate.h>
// TODO(svp) Disabling this feature until we find
//  a good way to determine if ATLAS is actually available
#else
extern "C" {
#include <cblas.h>
}
#endif  // defined(__APPLE__)

#else

#include "tick/array/vector/ops_unoptimized.h"
namespace tick {
template <typename T>
using vector_operations = detail::vector_operations_unoptimized<T>;
  }

#endif

#if defined(TICK_USE_MKL) || defined(TICK_USE_CBLAS)
#include "tick/array/vector/ops_blas.h"
namespace tick {
template <typename T>
using vector_operations = detail::vector_operations_cblas<T>;
  }
#endif

#include "tick/array/vector/ops_unoptimized_impl.h"

#endif  // LIB_INCLUDE_TICK_ARRAY_VECTOR_OPERATIONS_H_


//  This random generator is a C++ wrapper for the GNU Scientific Library
//  Copyright (C) 2001 Torbjorn Vik

//  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 2 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, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef __multimin_fdfminimizer_h
#define __multimin_fdfminimizer_h

#include <gsl/gsl_errno.h>
#include <gsl/gsl_multimin.h>
#include <gslwrap/vector_double.h>

namespace gsl{

//! Create an instance of this class with a user defined function
/*!
  A template class with the function operator()(const vector& x)
  and derivative(const vector&, vector&), as well as a reference to an object of this class must be fournished

  User is responsible for deleting this reference !

 */
template <class fdf_function>
class multimin_fdf
{
 public:
	fdf_function* fct;

	//! These operators can be overridden
	virtual double operator()(const vector& x)
		{
			return (*fct)(x);
		}
	virtual void derivative(const vector& x, vector& g)
		{
			(*fct).derivative(x, g);
		}

	//! This operator can be overridden to gain performance in calculating the value and its derivative in a scoop
	virtual double fval_and_derivative(const vector&x, vector& g )
	{
		derivative(x, g);
		return (*this)(x);
	}

	//! This is the function gsl calls to calculate the value of f at x
	static double f(const gsl_vector* x, void *p)
	{
		vector_view x_view(*x);
		return (*(multimin_fdf *)p)(x_view);
	}

	//! This is the function gsl calls to calculate the value of g=f' at x
	static void df(const gsl_vector* x, void *p, gsl_vector* g)
	{
		vector_view x_view(*x);
		vector_view g_view(*g);
		(*(multimin_fdf *)p).derivative(x_view, g_view);
	}

	//! This is the function gsl calls to calculate the value of g=f' at x
	static void fdf(const gsl_vector* x, void *p, double* f, gsl_vector* g)
	{
		vector_view x_view(*x);
		vector_view g_view(*g);
		*f=(*(multimin_fdf *)p).fval_and_derivative(x_view, g_view);
	}

	//! Constructor (User is responsible for deleting the fdf_function object)
	multimin_fdf(fdf_function* _fct):fct(_fct){assert (fct!=NULL);}
};

//! Class for multiminimizing one dimensional functions.
/*!
  Usage:
       - Create with optional multiminimize type
	   - Set with function object and inital bounds
	   - Loop the  iterate function until convergence or maxIterations (extra facility)

	   - Recover multiminimum and bounds
 */
class multimin_fdfminimizer
{
 public:
//!
/*! Choose between :
  - gsl_multimin_fdfminimizer_conjugate_fr
  - gsl_multimin_fdfminimizer_conjugate_pr
  - gsl_multimin_fdfminimizer_vector_bfgs
  - gsl_multimin_fdfminimizer_steepest_descent

 */
	multimin_fdfminimizer(uint _dim,
						  const gsl_multimin_fdfminimizer_type* type=gsl_multimin_fdfminimizer_conjugate_fr) :
		dim(_dim), isSet(false), maxIterations(100), s(NULL)
	{
		s=gsl_multimin_fdfminimizer_alloc(type, dim);
		nIterations=0;
		if (!s)
		{
			//error
			//cout << "ERROR Couldn't allocate memory for multiminimizer" << endl;
			//throw ?
			exit(-1);
		}
	}
	~multimin_fdfminimizer(){if (s) gsl_multimin_fdfminimizer_free(s);}
	//! returns GSL_FAILURE if the interval does not contain a multiminimum
	template <class  fdf_function>
	int set(multimin_fdf<fdf_function>& function, const vector& initial_x, double step_size, double tol)
	{
		isSet=false;
		f.f   = &function.f;
		f.df  = &function.df;
		f.fdf = &function.fdf;
		f.n   = dim;
		f.params = &function;
		int status=	gsl_multimin_fdfminimizer_set(s, &f, initial_x.gslobj(), step_size, tol);
		if (!status)
		{
			isSet=true;
			nIterations=0;
		}
		return status;
	}
	int iterate()
	{
		assert_set();
		int status=gsl_multimin_fdfminimizer_iterate(s);
		nIterations++;
		if (status==GSL_FAILURE)
			isConverged=true;
		return status;
	}
	int restart(){return gsl_multimin_fdfminimizer_restart(s);}
  	double minimum(){assert_set();return gsl_multimin_fdfminimizer_minimum(s);}
	vector x_value(){assert_set();return vector_view(*gsl_multimin_fdfminimizer_x(s));}
	vector gradient(){assert_set();return vector_view(*gsl_multimin_fdfminimizer_gradient(s));}

	void SetMaxIterations(int n){maxIterations=n;}
	int GetNIterations(){return nIterations;}
	bool is_converged(){if (nIterations>=maxIterations) return true; if (isConverged) return true; return false;}
	//string name() const;

 private:
	void assert_set(){if (!isSet)exit(-1);} // Old problem of error handling: TODO

	uint dim;
	bool isSet;
	bool isConverged;
	int nIterations;
	int maxIterations;
	gsl_multimin_fdfminimizer* s;
	gsl_multimin_function_fdf f;
};
};	 // namespace gsl

#endif //__multimin_fdfminimizer_h


/* spgetset.c
 *
 * Copyright (C) 2012 Patrick Alken
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

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

#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spmatrix.h>

#include "avl.c"

static void *tree_find(const gsl_spmatrix *m, const size_t i, const size_t j);

double
gsl_spmatrix_get(const gsl_spmatrix *m, const size_t i, const size_t j)
{
  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0.0);
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0.0);
    }
  else if (m->nz == 0)
    {
      /* no non-zero elements added to matrix */
      return (0.0);
    }
  else
    {
      if (GSL_SPMATRIX_ISTRIPLET(m))
        {
          /* traverse binary tree to search for (i,j) element */
          void *ptr = tree_find(m, i, j);
          double x = ptr ? *(double *) ptr : 0.0;

          return x;
        }
      else if (GSL_SPMATRIX_ISCCS(m))
        {
          const size_t *mi = m->i;
          const size_t *mp = m->p;
          size_t p;

          /* loop over column j and search for row index i */
          for (p = mp[j]; p < mp[j + 1]; ++p)
            {
              if (mi[p] == i)
                return m->data[p];
            }
        }
      else if (GSL_SPMATRIX_ISCRS(m))
        {
          const size_t *mj = m->i;
          const size_t *mp = m->p;
          size_t p;

          /* loop over row i and search for column index j */
          for (p = mp[i]; p < mp[i + 1]; ++p)
            {
              if (mj[p] == j)
                return m->data[p];
            }
        }
      else
        {
          GSL_ERROR_VAL("unknown sparse matrix type", GSL_EINVAL, 0.0);
        }

      /* element not found; return 0 */
      return 0.0;
    }
} /* gsl_spmatrix_get() */

/*
gsl_spmatrix_set()
  Add an element to a matrix in triplet form

Inputs: m - spmatrix
        i - row index
        j - column index
        x - matrix value
*/

int
gsl_spmatrix_set(gsl_spmatrix *m, const size_t i, const size_t j,
                 const double x)
{
  if (!GSL_SPMATRIX_ISTRIPLET(m))
    {
      GSL_ERROR("matrix not in triplet representation", GSL_EINVAL);
    }
  else if (x == 0.0)
    {
      /* traverse binary tree to search for (i,j) element */
      void *ptr = tree_find(m, i, j);

      /*
       * just set the data element to 0; it would be easy to
       * delete the node from the tree with avl_delete(), but
       * we'd also have to delete it from the data arrays which
       * is less simple
       */
      if (ptr != NULL)
        *(double *) ptr = 0.0;

      return GSL_SUCCESS;
    }
  else
    {
      int s = GSL_SUCCESS;
      void *ptr;

      /* check if matrix needs to be realloced */
      if (m->nz >= m->nzmax)
        {
          s = gsl_spmatrix_realloc(2 * m->nzmax, m);
          if (s)
            return s;
        }

      /* store the triplet (i, j, x) */
      m->i[m->nz] = i;
      m->p[m->nz] = j;
      m->data[m->nz] = x;

      ptr = avl_insert(m->tree_data->tree, &m->data[m->nz]);
      if (ptr != NULL)
        {
          /* found duplicate entry (i,j), replace with new x */
          *((double *) ptr) = x;
        }
      else
        {
          /* no duplicate (i,j) found, update indices as needed */

          /* increase matrix dimensions if needed */
          m->size1 = GSL_MAX(m->size1, i + 1);
          m->size2 = GSL_MAX(m->size2, j + 1);

          ++(m->nz);
        }

      return s;
    }
} /* gsl_spmatrix_set() */

double *
gsl_spmatrix_ptr(gsl_spmatrix *m, const size_t i, const size_t j)
{
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL);
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL);
    }
  else
    {
      if (GSL_SPMATRIX_ISTRIPLET(m))
        {
          /* traverse binary tree to search for (i,j) element */
          void *ptr = tree_find(m, i, j);
          return (double *) ptr;
        }
      else if (GSL_SPMATRIX_ISCCS(m))
        {
          const size_t *mi = m->i;
          const size_t *mp = m->p;
          size_t p;

          /* loop over column j and search for row index i */
          for (p = mp[j]; p < mp[j + 1]; ++p)
            {
              if (mi[p] == i)
                return &(m->data[p]);
            }
        }
      else if (GSL_SPMATRIX_ISCRS(m))
        {
          const size_t *mj = m->i;
          const size_t *mp = m->p;
          size_t p;

          /* loop over row i and search for column index j */
          for (p = mp[i]; p < mp[i + 1]; ++p)
            {
              if (mj[p] == j)
                return &(m->data[p]);
            }
        }
      else
        {
          GSL_ERROR_NULL("unknown sparse matrix type", GSL_EINVAL);
        }

      /* element not found; return 0 */
      return NULL;
    }
} /* gsl_spmatrix_ptr() */

/*
tree_find()
  Find node in tree corresponding to matrix entry (i,j). Adapted
from avl_find()

Inputs: m - spmatrix
        i - row index
        j - column index

Return: pointer to tree node data if found, NULL if not found
*/

static void *
tree_find(const gsl_spmatrix *m, const size_t i, const size_t j)
{
  const struct avl_table *tree = (struct avl_table *) m->tree_data->tree;
  const struct avl_node *p;

  for (p = tree->avl_root; p != NULL; )
    {
      size_t n = (double *) p->avl_data - m->data;
      size_t pi = m->i[n];
      size_t pj = m->p[n];
      int cmp = gsl_spmatrix_compare_idx(i, j, pi, pj);

      if (cmp < 0)
        p = p->avl_link[0];
      else if (cmp > 0)
        p = p->avl_link[1];
      else /* |cmp == 0| */
        return p->avl_data;
    }

  return NULL;
} /* tree_find() */


/**
 * author: Jochen K"upper
 * created: Jan 2002
 * file: pygsl/src/statistics/longmodule.c
 * $Id: shortmodule.c,v 1.7 2004/03/24 08:40:45 schnizer Exp $
 *
 * "
 */

#include <Python.h>
#include <pygsl/error_helpers.h>
#include <pygsl/block_helpers.h>
#include <gsl/gsl_statistics.h>

/* include real functions for different data-types */

#define STATMOD_APPEND_PY_TYPE(X) X ## Int
#define STATMOD_APPEND_PYC_TYPE(X) X ## SHORT
#define STATMOD_FUNC_EXT(X, Y) X ## _short ## Y
#define STATMOD_PY_AS_C PyInt_AsLong
#define STATMOD_C_TYPE short int
#include "functions.c"

/* initialization */

PyGSL_STATISTICS_INIT(short, "short")

/*
 * Local Variables:
 * mode: c
 * c-file-style: "Stroustrup"
 * End:
 */


#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <gbpLib.h>
#include <gbpRNG.h>
#include <gbpMCMC.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_fit.h>
#include <gsl/gsl_interp.h>

void init_MCMC_arrays(MCMC_info *MCMC) {
    int           i_DS;
    MCMC_DS_info *current_DS;
    MCMC_DS_info *next_DS;

    if(MCMC->n_M == NULL) {
        SID_log("Initializing MCMC target arrays for %d dataset(s)...", SID_LOG_OPEN, MCMC->n_DS);

        // Initialize chain arrays
        MCMC->n_M       = (int *)SID_malloc(sizeof(int) * MCMC->n_DS);
        MCMC->M_new     = (double **)SID_malloc(sizeof(double *) * MCMC->n_DS);
        MCMC->M_last    = (double **)SID_malloc(sizeof(double *) * MCMC->n_DS);
        MCMC->n_M_total = 0;
        i_DS            = 0;
        current_DS      = MCMC->DS;
        while(current_DS != NULL) {
            next_DS            = current_DS->next;
            MCMC->n_M[i_DS]    = current_DS->n_M;
            MCMC->M_new[i_DS]  = (double *)SID_malloc(sizeof(double) * MCMC->n_M[i_DS]);
            MCMC->M_last[i_DS] = (double *)SID_malloc(sizeof(double) * MCMC->n_M[i_DS]);
            MCMC->n_M_total += MCMC->n_M[i_DS];
            current_DS = next_DS;
            i_DS++;
        }

        // Initialize results arrays
        MCMC->P_min   = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_max   = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_avg   = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->dP_avg  = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_best  = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_peak  = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_lo_68 = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_hi_68 = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_lo_95 = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_hi_95 = (double *)SID_malloc(sizeof(double) * MCMC->n_P);

        MCMC->ln_likelihood_DS      = (double *)SID_malloc(sizeof(double) * MCMC->n_DS);
        MCMC->ln_likelihood_DS_best = (double *)SID_malloc(sizeof(double) * MCMC->n_DS);
        MCMC->ln_likelihood_DS_peak = (double *)SID_malloc(sizeof(double) * MCMC->n_DS);
        MCMC->n_DoF_DS              = (int *)SID_malloc(sizeof(int) * MCMC->n_DS);
        MCMC->n_DoF_DS_best         = (int *)SID_malloc(sizeof(int) * MCMC->n_DS);
        MCMC->n_DoF_DS_peak         = (int *)SID_malloc(sizeof(int) * MCMC->n_DS);

        // If MCMC_MODE_MINIMIZE_IO is on, we need to allocate buffers to store the chain
        if(SID_CHECK_BITFIELD_SWITCH(MCMC->mode, MCMC_MODE_MINIMIZE_IO)) {
            MCMC->flag_success_buffer      = (char *)SID_malloc(sizeof(char) * MCMC->n_iterations * MCMC->n_avg);
            MCMC->ln_likelihood_new_buffer = (double *)SID_malloc(sizeof(double) * MCMC->n_iterations * MCMC->n_avg);
            MCMC->P_new_buffer             = (double *)SID_malloc(sizeof(double) * MCMC->n_iterations * MCMC->n_avg * MCMC->n_P);
            MCMC->M_new_buffer             = (double *)SID_malloc(sizeof(double) * MCMC->n_iterations * MCMC->n_avg * MCMC->n_M_total);
        }

        SID_log("Done.", SID_LOG_CLOSE);
    }
}


#ifndef KGS_GSLSVD_H
#define KGS_GSLSVD_H

#include "math/SVD.h"
#include <gsl/gsl_matrix.h>

class SVDGSL: public SVD {
 public:
  SVDGSL(gsl_matrix* M): SVD(M){}

 protected:

  void UpdateFromMatrix() override;
};

#endif //KGS_GSLSVD_H


#ifndef TOOLS_H
#define TOOLS_H

#include <iostream>
#include <string>
#include <vector>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <math.h>

using namespace std;

//required for TRACE / DEBUG
extern int indentLevel;
extern string BLANKLINE;

//how many blanks to indent
#define INDENTATION 2

#define INDENT BLANKLINE.substr(0,(indentLevel)*INDENTATION)
#define INDENT_INC BLANKLINE.substr(0,(indentLevel++)*INDENTATION)
#define INDENT_DEC BLANKLINE.substr(0,(--indentLevel)*INDENTATION)

#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define ABS(x) ((x)>0 ? (x) : -(x))

#ifdef STDOUT
#define OUTPUT(x) cout << INDENT << (x) <<"\n";
#define OUTPUT1(x,arg1) cout << INDENT << (x) <<arg1<<"\n";
#define OUTPUT2(x,arg1,arg2) cout << INDENT << (x) <<arg1<<" / " <<arg2<<"\n";

#else
#define OUTPUT(x) ""
#define OUTPUT1(x,arg1) ""
#define OUTPUT2(x,arg1,arg2) ""

#endif //STDOUT

#ifdef TRACE
#define TRACE_IN(x) cout << INDENT_INC << "***TRACE_IN: "<< (x) <<"()\n";
#define TRACE_IN1(x,arg1) cout << INDENT_INC << "***TRACE_IN: "<< (x) <<"("<<arg1<<")\n";
#define TRACE_OUT(x,ok) cout << INDENT_DEC << "***TRACE_OUT: "<< (x) <<"() = " << ok << "\n";

#else
#define TRACE_IN(x) ""
#define TRACE_IN1(x,arg1) ""
#define TRACE_OUT(x,ok) ""

#endif //TRACE

#ifdef TRACEV
#define TRACEV_IN(x) cout << INDENT_INC << "***TRACE_IN: "<< (x) <<"()\n";
#define TRACEV_IN1(x,arg1) cout << INDENT_INC << "***TRACE_IN: "<< (x) <<"("<<arg1<<")\n";
#define TRACEV_OUT(x,ok) cout << INDENT_DEC << "***TRACE_OUT: "<< (x) <<"() = " << ok << "\n";

#else
#define TRACEV_IN(x) ""
#define TRACEV_IN1(x,arg1) ""
#define TRACEV_OUT(x,ok) ""

#endif //TRACEV

#ifdef DBUG
#define DEBUG(x) cout << INDENT << "***DEBUG: "<< (x) <<"\n";
#define DEBUG1(x,arg1) cout << INDENT << "***DEBUG: "<< (x) <<arg1<<"\n";
#define DEBUG2(x,arg1,arg2) cout << INDENT << "***DEBUG: "<< (x) <<arg1<<" / " <<arg2<<"\n";

#else
#define DEBUG(x) ""
#define DEBUG1(x,arg1) ""
#define DEBUG2(x,arg1,arg2) ""

#endif //DBUG

//verbose debug information
//mainly exact positions (quite big with dim=30)
#ifdef DBUGV
#define DEBUGV(x) cout << "***DEBUG: "<< (x) <<"\n";
#define DEBUGV1(x,arg1) cout << "***DEBUG: "<< (x) <<arg1<<"\n";
#define DEBUGV2(x,arg1,arg2) cout << "***DEBUG: "<< (x) <<arg1<<" / " <<arg2<<"\n";

#else
#define DEBUGV(x) ""
#define DEBUGV1(x,arg1) ""
#define DEBUGV2(x,arg1,arg2) ""

#endif //DBUGV

///Little helper function calculating the maximum height
/**of a regularly built pyramid of
  *@param minDegree minimum degree*/
int getMaxHeight(int minDegree, int swarmsize);

//fix the seed for the global RNG
void fixSeed(unsigned seed);

///Free the RNG ressources
void freeRng();
///Return gaussian distributed double from [0;1)
double randDoubleGaussian(double sigma);
///Return double from [0;1)
double randDouble();
///Wrapper to GSL Function call
/**Returns double from range [min,max)*/
double randDoubleRange(double min, double max);
///Wrapper to GSL Function call
/**Returns int from range [min,max]*/
int randIntRange(int min, int max);

string printVec(const vector<double>&);

double euclideanDistance(const vector<double>& v1, const vector<double>& v2);

///Generate a reproducable sequence
/**The seed is passed and thus the sequence is repeatable*/
class SequenceGenerator {
public:
	///Pass the seed
	SequenceGenerator(unsigned seed_in);
	///Destructor
	~SequenceGenerator();
	///Return exponentially distributed random variable, with mean mu
	double nextDoubleExponential(double mu);

	///Wrapper to GSL Function call
	/**Returns double from range [min,max)*/
	double nextDoubleRange(double min, double max);
	///Wrapper to GSL Function call
	/**Returns int from range [min,max]*/
	int nextIntRange(int min, int max);
private:
	///The random number generator
	gsl_rng*  randGen;

	///The random seed defining the sequence
	unsigned seed;

};

#endif //ifndef TOOLS_H


#ifndef __GSL_MANAGED_H__
#define __GSL_MANAGED_H__

#include "cuda.h"
#include <gsl/gsl_matrix_double.h>
#include <gsl/gsl_vector_double.h>

gsl_matrix *gsl_matrix_alloc_managed(const size_t n1, const size_t n2);
gsl_matrix *gsl_matrix_calloc_managed(const size_t n1, const size_t n2);
void gsl_matrix_free_managed(gsl_matrix *m);
__host__ __device__ double *gsl_matrix_ptr_managed(gsl_matrix * m, const size_t i, const size_t j);

gsl_vector *gsl_vector_alloc_managed(const size_t n, bool readmostly);
gsl_vector *gsl_vector_calloc_managed(const size_t n, bool readmostly);
__host__ __device__ double gsl_vector_get_managed(gsl_vector *vec, const size_t i);
void gsl_vector_free_managed(gsl_vector *m);

#endif /* __GSL_MANAGED_H__ */


#include <petsc.h>
#include "plexview.h"

PetscErrorCode PlexViewFromOptions(DM plex) {
    PetscErrorCode ierr;
    PetscBool  cell_cones = PETSC_FALSE,
               closures_coords = PETSC_FALSE,
               coords = PETSC_FALSE,
               points = PETSC_FALSE,
               use_height = PETSC_FALSE,
               vertex_supports = PETSC_FALSE;

    ierr = PetscOptionsBegin(PETSC_COMM_WORLD, "plex_view_", "view options for tiny", "");CHKERRQ(ierr);
    ierr = PetscOptionsBool("-cell_cones", "print cones of each cell",
                            "tiny.c", cell_cones, &cell_cones, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsBool("-closures_coords", "print vertex and edge (centers) coordinates for each cell",
                            "tiny.c", closures_coords, &closures_coords, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsBool("-coords", "print section and local vec for vertex coordinates",
                            "tiny.c", coords, &coords, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsBool("-points", "print point index ranges for vertices,edges,cells",
                            "tiny.c", points, &points, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsBool("-use_height", "use Height instead of Depth when printing points",
                            "tiny.c", use_height, &use_height, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsBool("-vertex_supports", "print supports of each vertex",
                            "tiny.c", vertex_supports, &vertex_supports, NULL);CHKERRQ(ierr);
    ierr = PetscOptionsEnd();

    if (points) {
        ierr = PlexViewPointRanges(plex,use_height); CHKERRQ(ierr);
    }
    if (cell_cones) {
        ierr = PlexViewFans(plex,2,2,1); CHKERRQ(ierr);
    }
    if (vertex_supports) {
        ierr = PlexViewFans(plex,2,0,1); CHKERRQ(ierr);
    }
    if (coords) {
        ierr = PlexViewCoords(plex); CHKERRQ(ierr);
    }
    if (closures_coords) {
        ierr = PlexViewClosuresCoords(plex); CHKERRQ(ierr);
    }
    return 0;
}

static const char* stratanames[4][10] =
                       {{"vertex","",    "",    ""},       // dim=0 names
                        {"vertex","cell","",    ""},       // dim=1 names
                        {"vertex","edge","cell",""},       // dim=2 names
                        {"vertex","edge","face","cell"}};  // dim=3 names

PetscErrorCode PlexViewPointRanges(DM plex, PetscBool use_height) {
    PetscErrorCode ierr;
    int         dim, m, start, end;
    const char  *plexname;
    MPI_Comm    comm;
    PetscMPIInt rank,size;

    ierr = PetscObjectGetComm((PetscObject)plex,&comm); CHKERRQ(ierr);
    ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
    ierr = MPI_Comm_rank(comm,&rank); CHKERRQ(ierr);
    ierr = DMGetDimension(plex,&dim); CHKERRQ(ierr);
    ierr = PetscObjectGetName((PetscObject)plex,&plexname); CHKERRQ(ierr);
    ierr = PetscPrintf(comm,"point ranges for DMPlex %s in %dD:\n",plexname,dim); CHKERRQ(ierr);
    if (size > 1) {
        ierr = PetscSynchronizedPrintf(comm,"  [rank %d]",rank); CHKERRQ(ierr);
    }
    ierr = DMPlexGetChart(plex,&start,&end); CHKERRQ(ierr);
    if (end < 1) { // nothing on this rank
        ierr = PetscSynchronizedPrintf(comm,"\n"); CHKERRQ(ierr);
        ierr = PetscSynchronizedFlush(comm,PETSC_STDOUT); CHKERRQ(ierr);
        return 0;
    }
    ierr = PetscSynchronizedPrintf(comm,
        "  chart points %d,...,%d\n",start,end-1); CHKERRQ(ierr);
    for (m = 0; m < dim + 1; m++) {
        if (use_height) {
            ierr = DMPlexGetHeightStratum(plex,m,&start,&end); CHKERRQ(ierr);
            ierr = PetscSynchronizedPrintf(comm,
                "    height %d of size %d: %d,...,%d (%s)\n",
                m,end-start,start,end-1,dim < 4 ? stratanames[dim][2-m] : ""); CHKERRQ(ierr);
        } else {
            ierr = DMPlexGetDepthStratum(plex,m,&start,&end); CHKERRQ(ierr);
            ierr = PetscSynchronizedPrintf(comm,
                "    depth=dim %d of size %d: %d,...,%d (%s)\n",
                m,end-start,start,end-1,dim < 4 ? stratanames[dim][m] : ""); CHKERRQ(ierr);
        }
    }
    ierr = PetscSynchronizedFlush(comm,PETSC_STDOUT); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode PlexViewFans(DM plex, int dim, int basestrata, int targetstrata) {
    PetscErrorCode ierr;
    const char  *plexname;
    const int   *targets;
    int         j, m, start, end, cssize;
    MPI_Comm    comm;
    PetscMPIInt rank,size;

    ierr = PetscObjectGetComm((PetscObject)plex,&comm); CHKERRQ(ierr);
    ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
    ierr = MPI_Comm_rank(comm,&rank); CHKERRQ(ierr);
    ierr = PetscObjectGetName((PetscObject)plex,&plexname); CHKERRQ(ierr);
    ierr = PetscPrintf(comm,"fans (cones or supports) for DMPlex %s:\n",plexname); CHKERRQ(ierr);
    if (size > 1) {
        ierr = PetscSynchronizedPrintf(comm,"  [rank %d]",rank); CHKERRQ(ierr);
    }
    ierr = PetscSynchronizedPrintf(comm,
        "  %s (= %s indices) of each %s\n",
        (basestrata > targetstrata) ? "cones" : "supports",
        stratanames[dim][targetstrata],stratanames[dim][basestrata]); CHKERRQ(ierr);
    ierr = DMPlexGetDepthStratum(plex, basestrata, &start, &end); CHKERRQ(ierr);
    for (m = start; m < end; m++) {
        if (basestrata > targetstrata) {
            ierr = DMPlexGetConeSize(plex,m,&cssize); CHKERRQ(ierr);
            ierr = DMPlexGetCone(plex,m,&targets); CHKERRQ(ierr);
        } else {
            ierr = DMPlexGetSupportSize(plex,m,&cssize); CHKERRQ(ierr);
            ierr = DMPlexGetSupport(plex,m,&targets); CHKERRQ(ierr);
        }
        ierr = PetscSynchronizedPrintf(comm,
            "    %s %d: ",stratanames[dim][basestrata],m); CHKERRQ(ierr);
        for (j = 0; j < cssize-1; j++) {
            ierr = PetscSynchronizedPrintf(comm,
                "%d,",targets[j]); CHKERRQ(ierr);
        }
        ierr = PetscSynchronizedPrintf(comm,
            "%d\n",targets[cssize-1]); CHKERRQ(ierr);
    }
    ierr = PetscSynchronizedFlush(comm,PETSC_STDOUT); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode PlexViewCoords(DM plex) {
    PetscErrorCode ierr;
    PetscSection coordSection;
    Vec          coordVec;
    const char   *plexname;

    ierr = PetscObjectGetName((PetscObject)plex,&plexname); CHKERRQ(ierr);
    ierr = PetscPrintf(PETSC_COMM_WORLD,"coordinate PetscSection and Vec for DMPlex %s:\n",plexname); CHKERRQ(ierr);
    ierr = DMGetCoordinateSection(plex, &coordSection); CHKERRQ(ierr);
    if (coordSection) {
        ierr = PetscSectionView(coordSection,PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
    } else {
        ierr = PetscPrintf(PETSC_COMM_WORLD,
            "[PlexViewCoords():  vertex coordinates PetscSection has not been set]\n"); CHKERRQ(ierr);
    }
    ierr = DMGetCoordinatesLocal(plex,&coordVec); CHKERRQ(ierr);
    if (coordVec) {
        ierr = VecViewLocalStdout(coordVec,PETSC_COMM_WORLD); CHKERRQ(ierr);
    } else {
        ierr = PetscPrintf(PETSC_COMM_WORLD,
            "[PlexViewCoords():  vertex coordinates Vec not been set]\n"); CHKERRQ(ierr);
    }
    return 0;
}

PetscErrorCode PlexViewClosuresCoords(DM plex) {
    PetscErrorCode ierr;
    DM          cdm;
    Vec         coords;
    double      *acoords;
    int         numpts, *pts = NULL,
                j, p, vertexstart, vertexend, edgeend, cellstart, cellend;
    MPI_Comm    comm;
    PetscMPIInt rank,size;
    const char  *plexname;

    ierr = PetscObjectGetComm((PetscObject)plex,&comm); CHKERRQ(ierr);
    ierr = MPI_Comm_size(comm,&size);CHKERRQ(ierr);
    ierr = MPI_Comm_rank(comm,&rank); CHKERRQ(ierr);
    ierr = PetscObjectGetName((PetscObject)plex,&plexname); CHKERRQ(ierr);
    ierr = PetscPrintf(comm,
        "closure points and coordinates for each cell in DMPlex %s:\n",plexname); CHKERRQ(ierr);
    if (size > 1) {
        ierr = PetscSynchronizedPrintf(comm,"  [rank %d]",rank); CHKERRQ(ierr);
    }
    ierr = DMPlexGetHeightStratum(plex, 0, &cellstart, &cellend); CHKERRQ(ierr);
    if (cellend < 1) { // nothing on this rank
        ierr = PetscSynchronizedPrintf(comm,"\n"); CHKERRQ(ierr);
        ierr = PetscSynchronizedFlush(comm,PETSC_STDOUT); CHKERRQ(ierr);
        return 0;
    }
    ierr = DMGetCoordinateDM(plex, &cdm); CHKERRQ(ierr);
    ierr = DMGetCoordinatesLocal(plex,&coords); CHKERRQ(ierr);
    ierr = DMPlexGetDepthStratum(plex, 0, &vertexstart, &vertexend); CHKERRQ(ierr);
    ierr = DMPlexGetDepthStratum(plex, 1, NULL, &edgeend); CHKERRQ(ierr);
    ierr = VecGetArray(coords, &acoords); CHKERRQ(ierr);
    for (j = cellstart; j < cellend; j++) {
        ierr = DMPlexGetTransitiveClosure(plex, j, PETSC_TRUE, &numpts, &pts);
        ierr = PetscSynchronizedPrintf(comm,"  cell %d\n",j); CHKERRQ(ierr);
        for (p = 0; p < numpts*2; p += 2) {   // omit orientations
            if ((pts[p] >= vertexstart) && (pts[p] < edgeend)) { // omit cells in closure
                if (pts[p] < vertexend) { // get location from coords
                    int voff;
                    voff = pts[p] - vertexstart;
                    ierr = PetscSynchronizedPrintf(comm,
                        "    vertex %3d at (%g,%g)\n",
                        pts[p],acoords[2*voff+0],acoords[2*voff+1]); CHKERRQ(ierr);
                } else { // assume it is an edge ... compute center
                    const int *vertices;
                    int       voff[2];
                    double    x,y;
                    ierr = DMPlexGetCone(plex, pts[p], &vertices); CHKERRQ(ierr);
                    voff[0] = vertices[0] - vertexstart;
                    voff[1] = vertices[1] - vertexstart;
                    x = 0.5 * (acoords[2*voff[0]+0] + acoords[2*voff[1]+0]);
                    y = 0.5 * (acoords[2*voff[0]+1] + acoords[2*voff[1]+1]);
                    ierr = PetscSynchronizedPrintf(comm,
                        "    edge   %3d at (%g,%g)\n",pts[p],x,y); CHKERRQ(ierr);
                }
            }
        }
        ierr = DMPlexRestoreTransitiveClosure(plex, j, PETSC_TRUE, &numpts, &pts); CHKERRQ(ierr);
    }
    ierr = VecRestoreArray(coords, &acoords); CHKERRQ(ierr);
    ierr = PetscSynchronizedFlush(comm,PETSC_STDOUT); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode VecViewLocalStdout(Vec v, MPI_Comm gcomm) {
    PetscErrorCode ierr;
    int         m,locsize;
    PetscMPIInt rank,size;
    double      *av;
    const char  *vecname;
    ierr = MPI_Comm_size(gcomm,&size);CHKERRQ(ierr);
    ierr = MPI_Comm_rank(gcomm,&rank); CHKERRQ(ierr);
    ierr = PetscObjectGetName((PetscObject)v,&vecname); CHKERRQ(ierr);
    ierr = PetscPrintf(gcomm,"local Vec: %s %d MPI processes\n",
                       vecname,size); CHKERRQ(ierr);
    if (size > 1) {
        ierr = PetscSynchronizedPrintf(gcomm,"[rank %d]:\n",rank); CHKERRQ(ierr);
    }
    ierr = VecGetLocalSize(v,&locsize); CHKERRQ(ierr);
    ierr = VecGetArray(v, &av); CHKERRQ(ierr);
    for (m = 0; m < locsize; m++) {
        ierr = PetscSynchronizedPrintf(gcomm,"%g\n",av[m]); CHKERRQ(ierr);
    }
    ierr = VecRestoreArray(v, &av); CHKERRQ(ierr);
    ierr = PetscSynchronizedFlush(gcomm,PETSC_STDOUT); CHKERRQ(ierr);
    return 0;
}


#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_math.h>
#include "gsl_cblas.h"

#include "tests.h"

void
test_symv (void) {
const double flteps = 1e-4, dbleps = 1e-6;
  {
   int order = 101;
   int uplo = 121;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1054)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1055)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1056)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1057)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1058)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1059)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1060)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   float alpha = 1.0f;
   float beta = -1.0f;
   int N = 1;
   int lda = 1;
   float A[] = { -0.428f };
   float X[] = { -0.34f };
   int incX = -1;
   float Y[] = { -0.888f };
   int incY = -1;
   float y_expected[] = { 1.03352f };
   cblas_ssymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "ssymv(case 1061)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1062)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1063)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1064)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1065)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1066)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1067)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1068)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   double alpha = 0;
   double beta = -0.3;
   int N = 1;
   int lda = 1;
   double A[] = { 0.544 };
   double X[] = { -0.601 };
   int incX = -1;
   double Y[] = { -0.852 };
   int incY = -1;
   double y_expected[] = { 0.2556 };
   cblas_dsymv(order, uplo, N, alpha, A, lda, X, incX, beta, Y, incY);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "dsymv(case 1069)");
     }
   };
  };

}


#include <cblas.h>


/* avl.c
 *
 * Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc.
 * Copyright (C) 2018 Patrick Alken
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/* This code is originally from GNU libavl, with some modifications */

#include <config.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_bst.h>
#include <gsl/gsl_errno.h>

typedef struct gsl_bst_avl_node avl_node;
typedef gsl_bst_avl_table avl_table;
typedef gsl_bst_avl_traverser avl_traverser;

#ifndef AVL_MAX_HEIGHT
#define AVL_MAX_HEIGHT GSL_BST_AVL_MAX_HEIGHT
#endif

/* Function types. */
typedef void avl_item_func (void *avl_item, void *avl_param);
typedef void *avl_copy_func (void *avl_item, void *avl_param);

/* tree functions */
static int avl_init(const gsl_bst_allocator * allocator,
                    gsl_bst_cmp_function * compare, void * params, void * vtable);
static size_t avl_nodes (const void * vtable);
static int avl_empty (void * vtable);
static void ** avl_probe (void * item, avl_table * table);
static void * avl_insert (void * item, void * vtable);
static void * avl_find (const void *item, const void * vtable);
static void * avl_remove (const void *item, void * vtable);

/* traverser functions */
static int avl_t_init (void * vtrav, const void * vtable);
static void * avl_t_first (void * vtrav, const void * vtable);
static void * avl_t_last (void * vtrav, const void * vtable);
static void * avl_t_find (const void * item, void * vtrav, const void * vtable);
static void * avl_t_insert (void * item, void * vtrav, void * vtable);
static void * avl_t_copy (void * vtrav, const void * vsrc);
static void * avl_t_next (void * vtrav);
static void * avl_t_prev (void * vtrav);
static void * avl_t_cur (const void * vtrav);
static void * avl_t_replace (void * vtrav, void * new_item);
static void avl_trav_refresh (avl_traverser * trav);

#if 0
static avl_table * avl_copy (const avl_table *, avl_copy_func *,
                             avl_item_func *);
static void *avl_replace (avl_table *, void *);
#endif

static int
avl_init(const gsl_bst_allocator * allocator,
         gsl_bst_cmp_function * compare, void * params, void * vtable)
{
  avl_table * table = (avl_table *) vtable;

  table->avl_alloc = allocator;
  table->avl_compare = compare;
  table->avl_param = params;

  table->avl_root = NULL;
  table->avl_count = 0;
  table->avl_generation = 0;

  return GSL_SUCCESS;
}

static size_t
avl_nodes (const void * vtable)
{
  const avl_table * table = (const avl_table *) vtable;
  return table->avl_count;
}

/* empty tree (delete all nodes) but do not free the tree itself */
static int
avl_empty (void * vtable)
{
  avl_table * table = (avl_table *) vtable;
  avl_node *p, *q;

  for (p = table->avl_root; p != NULL; p = q)
    {
      if (p->avl_link[0] == NULL)
        {
          q = p->avl_link[1];
          table->avl_alloc->free (p, table->avl_param);
        }
      else
        {
          q = p->avl_link[0];
          p->avl_link[0] = q->avl_link[1];
          q->avl_link[1] = p;
        }
    }

  table->avl_root = NULL;
  table->avl_count = 0;
  table->avl_generation = 0;

  return GSL_SUCCESS;
}

/*
avl_probe()
  Inserts |item| into |tree| and returns a pointer to |item|'s address.
If a duplicate item is found in the tree, returns a pointer to the existing
item without inserting |item|.
Returns |NULL| in case of memory allocation failure.
*/

static void **
avl_probe (void * item, avl_table * table)
{
  avl_node *y, *z; /* top node to update balance factor, and parent */
  avl_node *p, *q; /* iterator, and parent */
  avl_node *n;     /* newly inserted node */
  avl_node *w;     /* new root of rebalanced subtree */
  int dir;         /* direction to descend */

  unsigned char da[AVL_MAX_HEIGHT]; /* cached comparison results */
  int k = 0;                        /* number of cached results */

  z = (avl_node *) &table->avl_root;
  y = table->avl_root;
  dir = 0;
  for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir])
    {
      int cmp = table->avl_compare (item, p->avl_data, table->avl_param);

      if (cmp == 0)
        return &p->avl_data;

      if (p->avl_balance != 0)
        z = q, y = p, k = 0;

      da[k++] = dir = cmp > 0;
    }

  /* allocate a new node */
  n = q->avl_link[dir] = table->avl_alloc->alloc (sizeof *n, table->avl_param);
  if (n == NULL)
    return NULL;

  table->avl_count++;

  n->avl_data = item;
  n->avl_link[0] = n->avl_link[1] = NULL;
  n->avl_balance = 0;

  if (y == NULL)
    return &n->avl_data;

  for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++)
    if (da[k] == 0)
      p->avl_balance--;
    else
      p->avl_balance++;

  if (y->avl_balance == -2)
    {
      avl_node *x = y->avl_link[0];
      if (x->avl_balance == -1)
        {
          w = x;
          y->avl_link[0] = x->avl_link[1];
          x->avl_link[1] = y;
          x->avl_balance = y->avl_balance = 0;
        }
      else
        {
          w = x->avl_link[1];
          x->avl_link[1] = w->avl_link[0];
          w->avl_link[0] = x;
          y->avl_link[0] = w->avl_link[1];
          w->avl_link[1] = y;
          if (w->avl_balance == -1)
            x->avl_balance = 0, y->avl_balance = +1;
          else if (w->avl_balance == 0)
            x->avl_balance = y->avl_balance = 0;
          else /* |w->avl_balance == +1| */
            x->avl_balance = -1, y->avl_balance = 0;
          w->avl_balance = 0;
        }
    }
  else if (y->avl_balance == +2)
    {
      avl_node *x = y->avl_link[1];
      if (x->avl_balance == +1)
        {
          w = x;
          y->avl_link[1] = x->avl_link[0];
          x->avl_link[0] = y;
          x->avl_balance = y->avl_balance = 0;
        }
      else
        {
          w = x->avl_link[0];
          x->avl_link[0] = w->avl_link[1];
          w->avl_link[1] = x;
          y->avl_link[1] = w->avl_link[0];
          w->avl_link[0] = y;
          if (w->avl_balance == +1)
            x->avl_balance = 0, y->avl_balance = -1;
          else if (w->avl_balance == 0)
            x->avl_balance = y->avl_balance = 0;
          else /* |w->avl_balance == -1| */
            x->avl_balance = +1, y->avl_balance = 0;
          w->avl_balance = 0;
        }
    }
  else
    return &n->avl_data;

  z->avl_link[y != z->avl_link[0]] = w;
  table->avl_generation++;

  return &n->avl_data;
}

/*
avl_insert()
  Inserts |item| into |table|. Returns NULL if item successfully inserted,
or if a memory allocation error occurred. Otherwise, return duplicate
item.
*/

static void *
avl_insert (void * item, void * vtable)
{
  void **p = avl_probe (item, vtable);
  return p == NULL || *p == item ? NULL : *p;
}

/*
avl_find()
  Search for |item| in |table| and return a pointer to item if found.
Return NULL if not found.
*/

static void *
avl_find (const void * item, const void * vtable)
{
  const avl_table * table = (const avl_table *) vtable;
  avl_node *p;

  for (p = table->avl_root; p != NULL; )
    {
      int cmp = table->avl_compare (item, p->avl_data, table->avl_param);

      if (cmp < 0)
        p = p->avl_link[0];
      else if (cmp > 0)
        p = p->avl_link[1];
      else /* |cmp == 0| */
        return p->avl_data;
    }

  return NULL;
}

/*
avl_remove()
  Deletes from |table| and returns an item matching |item|.
Returns a null pointer if no matching item found.
*/

static void *
avl_remove (const void * item, void * vtable)
{
  avl_table * table = (avl_table *) vtable;

  /* stack of nodes */
  avl_node *pa[AVL_MAX_HEIGHT];      /* nodes */
  unsigned char da[AVL_MAX_HEIGHT];  /* |link[]| indexes */
  int k;                             /* stack pointer */

  avl_node *p;                       /* traverses tree to find node to delete */
  int cmp;                           /* result of comparison between |item| and |p| */

  k = 0;
  p = (avl_node *) &table->avl_root;
  for (cmp = -1; cmp != 0;
       cmp = table->avl_compare (item, p->avl_data, table->avl_param))
    {
      int dir = cmp > 0;

      pa[k] = p;
      da[k++] = dir;

      p = p->avl_link[dir];
      if (p == NULL)
        return NULL;
    }

  item = p->avl_data;

  if (p->avl_link[1] == NULL)
    pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0];
  else
    {
      avl_node *r = p->avl_link[1];
      if (r->avl_link[0] == NULL)
        {
          r->avl_link[0] = p->avl_link[0];
          r->avl_balance = p->avl_balance;
          pa[k - 1]->avl_link[da[k - 1]] = r;
          da[k] = 1;
          pa[k++] = r;
        }
      else
        {
          avl_node *s;
          int j = k++;

          for (;;)
            {
              da[k] = 0;
              pa[k++] = r;
              s = r->avl_link[0];
              if (s->avl_link[0] == NULL)
                break;

              r = s;
            }

          s->avl_link[0] = p->avl_link[0];
          r->avl_link[0] = s->avl_link[1];
          s->avl_link[1] = p->avl_link[1];
          s->avl_balance = p->avl_balance;

          pa[j - 1]->avl_link[da[j - 1]] = s;
          da[j] = 1;
          pa[j] = s;
        }
    }

  table->avl_alloc->free (p, table->avl_param);

  while (--k > 0)
    {
      avl_node *y = pa[k];

      if (da[k] == 0)
        {
          y->avl_balance++;
          if (y->avl_balance == +1)
            break;
          else if (y->avl_balance == +2)
            {
              avl_node *x = y->avl_link[1];
              if (x->avl_balance == -1)
                {
                  avl_node *w;
                  w = x->avl_link[0];
                  x->avl_link[0] = w->avl_link[1];
                  w->avl_link[1] = x;
                  y->avl_link[1] = w->avl_link[0];
                  w->avl_link[0] = y;
                  if (w->avl_balance == +1)
                    x->avl_balance = 0, y->avl_balance = -1;
                  else if (w->avl_balance == 0)
                    x->avl_balance = y->avl_balance = 0;
                  else /* |w->avl_balance == -1| */
                    x->avl_balance = +1, y->avl_balance = 0;
                  w->avl_balance = 0;
                  pa[k - 1]->avl_link[da[k - 1]] = w;
                }
              else
                {
                  y->avl_link[1] = x->avl_link[0];
                  x->avl_link[0] = y;
                  pa[k - 1]->avl_link[da[k - 1]] = x;
                  if (x->avl_balance == 0)
                    {
                      x->avl_balance = -1;
                      y->avl_balance = +1;
                      break;
                    }
                  else
                    x->avl_balance = y->avl_balance = 0;
                }
            }
        }
      else
        {
          y->avl_balance--;
          if (y->avl_balance == -1)
            break;
          else if (y->avl_balance == -2)
            {
              avl_node *x = y->avl_link[0];
              if (x->avl_balance == +1)
                {
                  avl_node *w;
                  w = x->avl_link[1];
                  x->avl_link[1] = w->avl_link[0];
                  w->avl_link[0] = x;
                  y->avl_link[0] = w->avl_link[1];
                  w->avl_link[1] = y;
                  if (w->avl_balance == -1)
                    x->avl_balance = 0, y->avl_balance = +1;
                  else if (w->avl_balance == 0)
                    x->avl_balance = y->avl_balance = 0;
                  else /* |w->avl_balance == +1| */
                    x->avl_balance = -1, y->avl_balance = 0;
                  w->avl_balance = 0;
                  pa[k - 1]->avl_link[da[k - 1]] = w;
                }
              else
                {
                  y->avl_link[0] = x->avl_link[1];
                  x->avl_link[1] = y;
                  pa[k - 1]->avl_link[da[k - 1]] = x;
                  if (x->avl_balance == 0)
                    {
                      x->avl_balance = +1;
                      y->avl_balance = -1;
                      break;
                    }
                  else
                    x->avl_balance = y->avl_balance = 0;
                }
            }
        }
    }

  table->avl_count--;
  table->avl_generation++;

  return (void *) item;
}

#if 0
/* Inserts |item| into |table|, replacing any duplicate item.
   Returns |NULL| if |item| was inserted without replacing a duplicate,
   or if a memory allocation error occurred.
   Otherwise, returns the item that was replaced. */
static void *
avl_replace (avl_table *table, void *item)
{
  void **p = avl_probe (table, item);
  if (p == NULL || *p == item)
    return NULL;
  else
    {
      void *r = *p;
      *p = item;
      return r;
    }
}

/* Destroys |new| with |avl_destroy (new, destroy)|,
   first setting right links of nodes in |stack| within |new|
   to null pointers to avoid touching uninitialized data. */
static void
copy_error_recovery (avl_node **stack, int height,
                     avl_table *new, avl_item_func *destroy)
{
  for (; height > 2; height -= 2)
    stack[height - 1]->avl_link[1] = NULL;
  avl_destroy (new, destroy);
}

/* Copies |org| to a newly created tree, which is returned.
   If |copy != NULL|, each data item in |org| is first passed to |copy|,
   and the return values are inserted into the tree,
   with |NULL| return values taken as indications of failure.
   On failure, destroys the partially created new tree,
   applying |destroy|, if non-null, to each item in the new tree so far,
   and returns |NULL|.
   If |allocator != NULL|, it is used for allocation in the new tree.
   Otherwise, the same allocator used for |org| is used. */
static avl_table *
avl_copy (const avl_table *org, avl_copy_func *copy,
          avl_item_func *destroy, struct libavl_allocator *allocator)
{
  avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)];
  int height = 0;

  avl_table *new;
  const avl_node *x;
  avl_node *y;

  new = avl_alloc (org->avl_compare, org->avl_param,
                   allocator != NULL ? allocator : org->avl_alloc);
  if (new == NULL)
    return NULL;
  new->avl_count = org->avl_count;
  if (new->avl_count == 0)
    return new;

  x = (const avl_node *) &org->avl_root;
  y = (avl_node *) &new->avl_root;
  for (;;)
    {
      while (x->avl_link[0] != NULL)
        {
          y->avl_link[0] =
            new->allocator->alloc (sizeof *y->avl_link[0],
                                   new->avl_param);
          if (y->avl_link[0] == NULL)
            {
              if (y != (avl_node *) &new->avl_root)
                {
                  y->avl_data = NULL;
                  y->avl_link[1] = NULL;
                }

              copy_error_recovery (stack, height, new, destroy);
              return NULL;
            }

          stack[height++] = (avl_node *) x;
          stack[height++] = y;
          x = x->avl_link[0];
          y = y->avl_link[0];
        }
      y->avl_link[0] = NULL;

      for (;;)
        {
          y->avl_balance = x->avl_balance;
          if (copy == NULL)
            y->avl_data = x->avl_data;
          else
            {
              y->avl_data = copy (x->avl_data, org->avl_param);
              if (y->avl_data == NULL)
                {
                  y->avl_link[1] = NULL;
                  copy_error_recovery (stack, height, new, destroy);
                  return NULL;
                }
            }

          if (x->avl_link[1] != NULL)
            {
              y->avl_link[1] =
                new->allocator->alloc (sizeof *y->avl_link[1],
                                       new->avl_param);
              if (y->avl_link[1] == NULL)
                {
                  copy_error_recovery (stack, height, new, destroy);
                  return NULL;
                }

              x = x->avl_link[1];
              y = y->avl_link[1];
              break;
            }
          else
            y->avl_link[1] = NULL;

          if (height <= 2)
            return new;

          y = stack[--height];
          x = stack[--height];
        }
    }
}

#endif

/*
avl_t_init()
  Initializes |trav| for use with |tree| and selects the null node.
*/

static int
avl_t_init (void * vtrav, const void * vtable)
{
  avl_traverser * trav = (avl_traverser *) vtrav;
  const avl_table * table = (const avl_table *) vtable;

  trav->avl_table = table;
  trav->avl_node = NULL;
  trav->avl_height = 0;
  trav->avl_generation = table->avl_generation;

  return GSL_SUCCESS;
}

/*
avl_t_first()
  Initializes |trav| for |tree| and selects and returns a pointer to its least-valued item.
  Returns |NULL| if |tree| contains no nodes.
*/

static void *
avl_t_first (void * vtrav, const void * vtable)
{
  const avl_table * table = (const avl_table *) vtable;
  avl_traverser * trav = (avl_traverser *) vtrav;
  avl_node *x;

  trav->avl_table = table;
  trav->avl_height = 0;
  trav->avl_generation = table->avl_generation;

  x = table->avl_root;
  if (x != NULL)
    {
      while (x->avl_link[0] != NULL)
        {
          if (trav->avl_height >= AVL_MAX_HEIGHT)
            {
              GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
            }

          trav->avl_stack[trav->avl_height++] = x;
          x = x->avl_link[0];
        }
    }

  trav->avl_node = x;

  return x != NULL ? x->avl_data : NULL;
}

/*
avl_t_last()
  Initializes |trav| for |tree| and selects and returns a pointer to its greatest-valued item.
  Returns |NULL| if |tree| contains no nodes.
*/

static void *
avl_t_last (void * vtrav, const void * vtable)
{
  const avl_table * table = (const avl_table *) vtable;
  avl_traverser * trav = (avl_traverser *) vtrav;
  avl_node *x;

  trav->avl_table = table;
  trav->avl_height = 0;
  trav->avl_generation = table->avl_generation;

  x = table->avl_root;
  if (x != NULL)
    {
      while (x->avl_link[1] != NULL)
        {
          if (trav->avl_height >= AVL_MAX_HEIGHT)
            {
              GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
            }

          trav->avl_stack[trav->avl_height++] = x;
          x = x->avl_link[1];
        }
    }

  trav->avl_node = x;

  return x != NULL ? x->avl_data : NULL;
}

/*
avl_t_find()
  Searches for |item| in |table|. If found, initializes |trav| to
the item found and returns the item as well.  If there is no matching
item, initializes |trav| to the null item and returns |NULL|.
*/

static void *
avl_t_find (const void * item, void * vtrav, const void * vtable)
{
  const avl_table * table = (const avl_table *) vtable;
  avl_traverser * trav = (avl_traverser *) vtrav;
  avl_node *p, *q;

  trav->avl_table = table;
  trav->avl_height = 0;
  trav->avl_generation = table->avl_generation;

  for (p = table->avl_root; p != NULL; p = q)
    {
      int cmp = table->avl_compare (item, p->avl_data, table->avl_param);

      if (cmp < 0)
        q = p->avl_link[0];
      else if (cmp > 0)
        q = p->avl_link[1];
      else /* |cmp == 0| */
        {
          trav->avl_node = p;
          return p->avl_data;
        }

      if (trav->avl_height >= AVL_MAX_HEIGHT)
        {
          GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
        }

      trav->avl_stack[trav->avl_height++] = p;
    }

  trav->avl_height = 0;
  trav->avl_node = NULL;

  return NULL;
}

/*
avl_t_insert()
  Attempts to insert |item| into |table|.  If |item| is inserted
successfully, it is returned and |trav| is initialized to its location.
If a duplicate is found, it is returned and |trav| is initialized to
its location.  No replacement of the item occurs.
If a memory allocation failure occurs, |NULL| is returned and |trav|
is initialized to the null item.
*/

static void *
avl_t_insert (void * item, void * vtrav, void * vtable)
{
  avl_table * table = (avl_table *) vtable;
  avl_traverser * trav = (avl_traverser *) vtrav;
  void **p;

  p = avl_probe (item, table);
  if (p != NULL)
    {
      trav->avl_table = table;
      trav->avl_node = ((avl_node *) ((char *) p - offsetof (avl_node, avl_data)));
      trav->avl_generation = table->avl_generation - 1;
      return *p;
    }
  else
    {
      avl_t_init (vtrav, vtable);
      return NULL;
    }
}

/*
avl_t_copy()
  Initializes |trav| to have the same current node as |src|.
*/

static void *
avl_t_copy (void * vtrav, const void * vsrc)
{
  const avl_traverser * src = (const avl_traverser *) vsrc;
  avl_traverser * trav = (avl_traverser *) vtrav;

  if (trav != src)
    {
      trav->avl_table = src->avl_table;
      trav->avl_node = src->avl_node;
      trav->avl_generation = src->avl_generation;
      if (trav->avl_generation == trav->avl_table->avl_generation)
        {
          trav->avl_height = src->avl_height;
          memcpy (trav->avl_stack, (const void *) src->avl_stack,
                  sizeof *trav->avl_stack * trav->avl_height);
        }
    }

  return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL;
}

/*
avl_t_next()
  Returns the next data item in in-order within the tree
being traversed with |trav|, or if there are no more data items returns NULL.
*/

static void *
avl_t_next (void * vtrav)
{
  avl_traverser * trav = (avl_traverser *) vtrav;
  avl_node *x;

  if (trav->avl_generation != trav->avl_table->avl_generation)
    avl_trav_refresh (trav);

  x = trav->avl_node;
  if (x == NULL)
    {
      return avl_t_first (vtrav, trav->avl_table);
    }
  else if (x->avl_link[1] != NULL)
    {
      if (trav->avl_height >= AVL_MAX_HEIGHT)
        {
          GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
        }

      trav->avl_stack[trav->avl_height++] = x;
      x = x->avl_link[1];

      while (x->avl_link[0] != NULL)
        {
          if (trav->avl_height >= AVL_MAX_HEIGHT)
            {
              GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
            }

          trav->avl_stack[trav->avl_height++] = x;
          x = x->avl_link[0];
        }
    }
  else
    {
      avl_node *y;

      do
        {
          if (trav->avl_height == 0)
            {
              trav->avl_node = NULL;
              return NULL;
            }

          y = x;
          x = trav->avl_stack[--trav->avl_height];
        }
      while (y == x->avl_link[1]);
    }

  trav->avl_node = x;

  return x->avl_data;
}

/*
avl_t_prev()
  Returns the previous data item in inorder within the tree being
traversed with |trav|, or if there are no more data items returns NULL.
*/

static void *
avl_t_prev (void * vtrav)
{
  avl_traverser * trav = (avl_traverser *) vtrav;
  avl_node *x;

  if (trav->avl_generation != trav->avl_table->avl_generation)
    avl_trav_refresh (trav);

  x = trav->avl_node;
  if (x == NULL)
    {
      return avl_t_last (vtrav, trav->avl_table);
    }
  else if (x->avl_link[0] != NULL)
    {
      if (trav->avl_height >= AVL_MAX_HEIGHT)
        {
          GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
        }

      trav->avl_stack[trav->avl_height++] = x;
      x = x->avl_link[0];

      while (x->avl_link[1] != NULL)
        {
          if (trav->avl_height >= AVL_MAX_HEIGHT)
            {
              GSL_ERROR_NULL("traverser height exceeds maximum", GSL_ETABLE);
            }

          trav->avl_stack[trav->avl_height++] = x;
          x = x->avl_link[1];
        }
    }
  else
    {
      avl_node *y;

      do
        {
          if (trav->avl_height == 0)
            {
              trav->avl_node = NULL;
              return NULL;
            }

          y = x;
          x = trav->avl_stack[--trav->avl_height];
        }
      while (y == x->avl_link[0]);
    }

  trav->avl_node = x;

  return x->avl_data;
}

static void *
avl_t_cur (const void * vtrav)
{
  const avl_traverser * trav = (const avl_traverser *) vtrav;
  return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL;
}

/*
avl_t_replace()
  Replaces the current item in |trav| by |new| and returns the item replaced.
|trav| must not have the null item selected. The new item must not upset the
ordering of the tree.
*/

static void *
avl_t_replace (void * vtrav, void * new_item)
{
  avl_traverser * trav = (avl_traverser *) vtrav;
  void *old;

  old = trav->avl_node->avl_data;
  trav->avl_node->avl_data = new_item;

  return old;
}

/*
avl_trav_refresh()
  Refreshes the stack of parent pointers in |trav| and updates its generation number
*/

static void
avl_trav_refresh (avl_traverser * trav)
{
  trav->avl_generation = trav->avl_table->avl_generation;

  if (trav->avl_node != NULL)
    {
      gsl_bst_cmp_function *cmp = trav->avl_table->avl_compare;
      void *param = trav->avl_table->avl_param;
      avl_node *node = trav->avl_node;
      avl_node *i;

      trav->avl_height = 0;
      for (i = trav->avl_table->avl_root; i != node; )
        {
          if (trav->avl_height >= AVL_MAX_HEIGHT)
            {
              GSL_ERROR_VOID("traverser height exceeds maximum", GSL_ETABLE);
            }

          trav->avl_stack[trav->avl_height++] = i;
          i = i->avl_link[cmp (node->avl_data, i->avl_data, param) > 0];
        }
    }
}

static const gsl_bst_type avl_tree_type =
{
  "AVL",
  sizeof(avl_node),
  avl_init,
  avl_nodes,
  avl_insert,
  avl_find,
  avl_remove,
  avl_empty,

  avl_t_init,
  avl_t_first,
  avl_t_last,
  avl_t_find,
  avl_t_insert,
  avl_t_copy,
  avl_t_next,
  avl_t_prev,
  avl_t_cur,
  avl_t_replace
};

const gsl_bst_type * gsl_bst_avl = &avl_tree_type;


#pragma once

#include <cuda/runtime_api.hpp>
#include <gsl/gsl-lite.hpp>

namespace thrustshift {

namespace kernel {

template <typename A, class RangeX, class RangeY, class RangeD>
__global__ void axmy(A a, RangeX x, RangeY y, RangeD d) {
	const auto gtid = threadIdx.x + blockIdx.x * blockDim.x;
	gsl_Expects(x.size() == y.size());
	gsl_Expects(y.size() == d.size());
	if (gtid < x.size()) {
		d[gtid] = a * x[gtid] * y[gtid];
	}
}

} // namespace kernel

namespace async {

template <typename A, class RangeX, class RangeY, class RangeD>
void axmy(cuda::stream_t& stream, A a, RangeX&& x, RangeY&& y, RangeD&& d) {

	gsl_Expects(x.size() == y.size());
	gsl_Expects(y.size() == d.size());
	constexpr cuda::grid::block_dimension_t block_dim = 256;
	const cuda::grid::dimension_t grid_dim =
	    (x.size() + block_dim - 1) / block_dim;

	using RangeXD = typename std::remove_reference<RangeX>::type;
	using RangeYD = typename std::remove_reference<RangeY>::type;
	using RangeDD = typename std::remove_reference<RangeD>::type;

	cuda::enqueue_launch(kernel::axmy<A, RangeXD, RangeYD, RangeDD>,
	                     stream,
	                     cuda::make_launch_config(grid_dim, block_dim),
	                     a,
	                     x,
	                     y,
	                     d);
}

} // namespace async

} // namespace thrustshift


//===--- Sudoku/Solver_set_option.h                                     ---===//
//
// Helper functions
//===----------------------------------------------------------------------===//
#pragma once

#include "Board.h"
#include "Location.h"
#include "Location_Utilities.h" // is_same_*
#include "Options.h"
#include "Size.h"
#include "Solvers_find.h"
#include "Solvers_remove_option.h"
#include "Value.h"
#include "exceptions.h"
#include "traits.h"

#include <gsl/gsl>

#include <vector>

#include <algorithm>   // find_if
#include <iterator>    // next
#include <stdexcept>   // logic_error
#include <type_traits> // is_base_of

#include "Solver.fwd.h" // Forward declaration - single_option

#include <cassert>

namespace Sudoku
{
//===----------------------------------------------------------------------===//
template<int N, typename Options = Options<elem_size<N>>>
int set_Value(Board<Options, N>&, Location<N>, Value);
template<int N, typename Options = Options<elem_size<N>>, typename ItrT>
int set_Value(Board<Options, N>&, ItrT begin, ItrT end);

template<int N, typename Options = Options<elem_size<N>>, typename SectionT>
int set_unique(Board<Options, N>&, SectionT, Value);
template<int N, typename Options = Options<elem_size<N>>, typename SectionT>
int set_uniques(Board<Options, N>&, SectionT, Options worker);

template<int N, typename Options = Options<elem_size<N>>, typename SectionT>
int set_section_locals(
	Board<Options, N>&, SectionT, int rep_count, Options values);
template<int N, typename Options = Options<elem_size<N>>>
int set_section_locals(
	Board<Options, N>&,
	Board_Section::Block<Options, N>,
	int rep_count,
	Options values);

//===----------------------------------------------------------------------===//

// IF a possible option, Make [value] the answer for [loc]
template<int N, typename Options>
inline int set_Value(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const Location<N> loc,
	const Value value)
{
	assert(is_valid(loc));
	assert(is_valid<N>(value));

	int changes{0};
	auto& elem = board.at(loc);
	if (not elem.test(value)) // value is option nor answer
		throw error::invalid_Board();

	if (not is_answer(elem))
	{
		changes = gsl::narrow_cast<int>(elem.count_all());
		elem.set_nocheck(value);
	}
	return changes;
}

// set board_ using a transferable container of values
template<int N, typename Options, typename ItrT>
int set_Value(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const ItrT begin,
	const ItrT end)
{
	{
		static_assert(traits::is_forward<ItrT>);
		assert(end - begin == full_size<N>);
	}
	int changes{0};
	int n{0};
	for (auto itr = begin; itr != end; ++itr)
	{
		const Location<N> loc(n++); // start at 0!

		// handle different input types
		Value value{};
		if constexpr (traits::iterator_to<ItrT, Value>)
		{
			value = *itr;
		}
		else
		{
			value = to_Value<N>(*itr);
		}

		if (value != Value{0})
		{
			if (not is_valid<N>(value))
			{
				throw std::domain_error{"Invalid Value"};
			}

			if (is_option(board.at(loc), value))
			{ // update options on board
				changes += single_option(board, loc, value);
			}
			assert(is_answer(board.at(loc), value));
		}
	}
	assert(n == full_size<N>);
	return changes;
}

// Set unique values in section as answer
template<int N, typename Options, typename SectionT>
inline int set_uniques(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const SectionT section,
	const Options worker)
{
	{
		static_assert(Board_Section::traits::is_Section_v<SectionT>);
		static_assert(std::is_same_v<typename SectionT::value_type, Options>);
	}
	int changes{0};

	if (worker.count_all() > 0)
	{
		for (Value val{1}; val < Value{worker.size()}; ++val)
		{
			if (worker[val])
			{
				changes += set_unique(board, section, val);
			}
		}
	}
	return changes;
}

// Set unique value in section as answer
template<int N, typename Options, typename SectionT>
inline int set_unique(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const SectionT section,
	const Value value)
{
	{
		static_assert(Board_Section::traits::is_Section_v<SectionT>);
		static_assert(std::is_same_v<typename SectionT::value_type, Options>);
		static_assert(traits::is_input<typename SectionT::iterator>);
		assert(is_valid<N>(value));
	}
	const auto end       = section.cend();
	const auto condition = [value](const Options& O) {
		return O.test(value);
	};

	const auto itr = std::find_if(section.cbegin(), end, condition);

	if (itr == end)
	{ // option not available in section
		throw error::invalid_Board();
	}
	assert(&(*itr) == &board[itr.location()]); // section must be part of board
	assert(std::find_if(std::next(itr), end, condition) == end); // unique

	return single_option(board, itr.location(), value);
}

// for [row/col] per value: if all in same block, remove from rest block
template<int N, typename Options, typename SectionT>
inline int set_section_locals(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const SectionT section,
	const int rep_count,
	const Options values)
{
	{
		static_assert(Board_Section::traits::is_Section_v<SectionT>);
		static_assert(std::is_same_v<typename SectionT::value_type, Options>);
		assert(rep_count > 1);  // should have been caught by caller
								// use the set_uniques specialization
		assert(rep_count <= N); // won't fit in single block-row/col
		assert(values.count_all() > 0);
	}
	int changes{0};
	std::vector<Location<N>> locations{};

	// start at 1, to skip the answer-bit
	for (Value value{1}; value < Value{values.size()}; ++value)
	{
		if (values[value])
		{
			locations = list_where_option<N>(section, value, rep_count);
			if (locations.size() != gsl::narrow_cast<size_t>(rep_count))
			{
				assert(changes > 0); // changed by earlier value in values
			}
			else if (is_same_block<N>(locations.cbegin(), locations.cend()))
			{ // remove from rest of block
				changes += remove_option_section(
					board, board.block(locations[0]), locations, value);
			}
		}
	}
	return changes;
}

// per value in [block]: if all in same row/col, remove from rest row/col
template<int N, typename Options>
inline int set_section_locals(
	Board<Options, N>& board, // NOLINT(runtime/references)
	const Board_Section::Block<Options, N> block,
	const int rep_count,
	const Options values)
{
	{
		assert(rep_count > 1);  // should have been caught by caller
								// use the set_uniques specialization
		assert(rep_count <= N); // won't fit in single block-row/col
		assert(values.count_all() > 0);
	}
	int changes{0};
	std::vector<Location<N>> locations{};

	// start at 1, to skip the answer-bit
	for (Value value{1}; value < Value{values.size()}; ++value)
	{
		if (values[value])
		{
			locations = list_where_option<N>(block, value, rep_count);
			if (locations.size() != gsl::narrow_cast<size_t>(rep_count))
			{
				assert(changes > 0); // changed by earlier value in values
			}
			else if (is_same_row<N>(locations.cbegin(), locations.cend()))
			{ // remove from rest of row
				changes += remove_option_outside_block(
					board, board.row(locations[0]), locations[0], value);
			}
			else if (is_same_col<N>(locations.cbegin(), locations.cend()))
			{ // remove from rest of col
				changes += remove_option_outside_block(
					board, board.col(locations[0]), locations[0], value);
			}
		}
	}
	return changes;
}

} // namespace Sudoku