/* matrix/gsl_matrix_ushort.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_USHORT_H__
#define __GSL_MATRIX_USHORT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_ushort.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned short * data;
  gsl_block_ushort * block;
  int owner;
} gsl_matrix_ushort;

typedef struct
{
  gsl_matrix_ushort matrix;
} _gsl_matrix_ushort_view;

typedef _gsl_matrix_ushort_view gsl_matrix_ushort_view;

typedef struct
{
  gsl_matrix_ushort matrix;
} _gsl_matrix_ushort_const_view;

typedef const _gsl_matrix_ushort_const_view gsl_matrix_ushort_const_view;

/* Allocation */

GSL_EXPORT
gsl_matrix_ushort *
gsl_matrix_ushort_alloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_ushort *
gsl_matrix_ushort_calloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_ushort *
gsl_matrix_ushort_alloc_from_block (gsl_block_ushort * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_EXPORT
gsl_matrix_ushort *
gsl_matrix_ushort_alloc_from_matrix (gsl_matrix_ushort * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
gsl_vector_ushort *
gsl_vector_ushort_alloc_row_from_matrix (gsl_matrix_ushort * m,
                                        const size_t i);

GSL_EXPORT
gsl_vector_ushort *
gsl_vector_ushort_alloc_col_from_matrix (gsl_matrix_ushort * m,
                                        const size_t j);

GSL_EXPORT void gsl_matrix_ushort_free (gsl_matrix_ushort * m);

/* Views */

GSL_EXPORT
_gsl_matrix_ushort_view
gsl_matrix_ushort_submatrix (gsl_matrix_ushort * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_ushort_view
gsl_matrix_ushort_row (gsl_matrix_ushort * m, const size_t i);

GSL_EXPORT
_gsl_vector_ushort_view
gsl_matrix_ushort_column (gsl_matrix_ushort * m, const size_t j);

GSL_EXPORT
_gsl_vector_ushort_view
gsl_matrix_ushort_diagonal (gsl_matrix_ushort * m);

GSL_EXPORT
_gsl_vector_ushort_view
gsl_matrix_ushort_subdiagonal (gsl_matrix_ushort * m, const size_t k);

GSL_EXPORT
_gsl_vector_ushort_view
gsl_matrix_ushort_superdiagonal (gsl_matrix_ushort * m, const size_t k);

GSL_EXPORT
_gsl_matrix_ushort_view
gsl_matrix_ushort_view_array (unsigned short * base,
                             const size_t n1,
                             const size_t n2);

GSL_EXPORT
_gsl_matrix_ushort_view
gsl_matrix_ushort_view_array_with_tda (unsigned short * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_EXPORT
_gsl_matrix_ushort_view
gsl_matrix_ushort_view_vector (gsl_vector_ushort * v,
                              const size_t n1,
                              const size_t n2);

GSL_EXPORT
_gsl_matrix_ushort_view
gsl_matrix_ushort_view_vector_with_tda (gsl_vector_ushort * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_EXPORT
_gsl_matrix_ushort_const_view
gsl_matrix_ushort_const_submatrix (const gsl_matrix_ushort * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_ushort_const_view
gsl_matrix_ushort_const_row (const gsl_matrix_ushort * m,
                            const size_t i);

GSL_EXPORT
_gsl_vector_ushort_const_view
gsl_matrix_ushort_const_column (const gsl_matrix_ushort * m,
                               const size_t j);

GSL_EXPORT
_gsl_vector_ushort_const_view
gsl_matrix_ushort_const_diagonal (const gsl_matrix_ushort * m);

GSL_EXPORT
_gsl_vector_ushort_const_view
gsl_matrix_ushort_const_subdiagonal (const gsl_matrix_ushort * m,
                                    const size_t k);

GSL_EXPORT
_gsl_vector_ushort_const_view
gsl_matrix_ushort_const_superdiagonal (const gsl_matrix_ushort * m,
                                      const size_t k);

GSL_EXPORT
_gsl_matrix_ushort_const_view
gsl_matrix_ushort_const_view_array (const unsigned short * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_EXPORT
_gsl_matrix_ushort_const_view
gsl_matrix_ushort_const_view_array_with_tda (const unsigned short * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_EXPORT
_gsl_matrix_ushort_const_view
gsl_matrix_ushort_const_view_vector (const gsl_vector_ushort * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
_gsl_matrix_ushort_const_view
gsl_matrix_ushort_const_view_vector_with_tda (const gsl_vector_ushort * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_EXPORT unsigned short   gsl_matrix_ushort_get(const gsl_matrix_ushort * m, const size_t i, const size_t j);
GSL_EXPORT void    gsl_matrix_ushort_set(gsl_matrix_ushort * m, const size_t i, const size_t j, const unsigned short x);

GSL_EXPORT unsigned short * gsl_matrix_ushort_ptr(gsl_matrix_ushort * m, const size_t i, const size_t j);
GSL_EXPORT const unsigned short * gsl_matrix_ushort_const_ptr(const gsl_matrix_ushort * m, const size_t i, const size_t j);

GSL_EXPORT void gsl_matrix_ushort_set_zero (gsl_matrix_ushort * m);
GSL_EXPORT void gsl_matrix_ushort_set_identity (gsl_matrix_ushort * m);
GSL_EXPORT void gsl_matrix_ushort_set_all (gsl_matrix_ushort * m, unsigned short x);

GSL_EXPORT int gsl_matrix_ushort_fread (FILE * stream, gsl_matrix_ushort * m) ;
GSL_EXPORT int gsl_matrix_ushort_fwrite (FILE * stream, const gsl_matrix_ushort * m) ;
GSL_EXPORT int gsl_matrix_ushort_fscanf (FILE * stream, gsl_matrix_ushort * m);
GSL_EXPORT int gsl_matrix_ushort_fprintf (FILE * stream, const gsl_matrix_ushort * m, const char * format);

GSL_EXPORT int gsl_matrix_ushort_memcpy(gsl_matrix_ushort * dest, const gsl_matrix_ushort * src);
GSL_EXPORT int gsl_matrix_ushort_swap(gsl_matrix_ushort * m1, gsl_matrix_ushort * m2);

GSL_EXPORT int gsl_matrix_ushort_swap_rows(gsl_matrix_ushort * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_ushort_swap_columns(gsl_matrix_ushort * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_ushort_swap_rowcol(gsl_matrix_ushort * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_ushort_transpose (gsl_matrix_ushort * m);
GSL_EXPORT int gsl_matrix_ushort_transpose_memcpy (gsl_matrix_ushort * dest, const gsl_matrix_ushort * src);

GSL_EXPORT unsigned short gsl_matrix_ushort_max (const gsl_matrix_ushort * m);
GSL_EXPORT unsigned short gsl_matrix_ushort_min (const gsl_matrix_ushort * m);
GSL_EXPORT void gsl_matrix_ushort_minmax (const gsl_matrix_ushort * m, unsigned short * min_out, unsigned short * max_out);

GSL_EXPORT void gsl_matrix_ushort_max_index (const gsl_matrix_ushort * m, size_t * imax, size_t *jmax);
GSL_EXPORT void gsl_matrix_ushort_min_index (const gsl_matrix_ushort * m, size_t * imin, size_t *jmin);
GSL_EXPORT void gsl_matrix_ushort_minmax_index (const gsl_matrix_ushort * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_EXPORT int gsl_matrix_ushort_isnull (const gsl_matrix_ushort * m);

GSL_EXPORT int gsl_matrix_ushort_add (gsl_matrix_ushort * a, const gsl_matrix_ushort * b);
GSL_EXPORT int gsl_matrix_ushort_sub (gsl_matrix_ushort * a, const gsl_matrix_ushort * b);
GSL_EXPORT int gsl_matrix_ushort_mul_elements (gsl_matrix_ushort * a, const gsl_matrix_ushort * b);
GSL_EXPORT int gsl_matrix_ushort_div_elements (gsl_matrix_ushort * a, const gsl_matrix_ushort * b);
GSL_EXPORT int gsl_matrix_ushort_scale (gsl_matrix_ushort * a, const double x);
GSL_EXPORT int gsl_matrix_ushort_add_constant (gsl_matrix_ushort * a, const double x);
GSL_EXPORT int gsl_matrix_ushort_add_diagonal (gsl_matrix_ushort * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_EXPORT int gsl_matrix_ushort_get_row(gsl_vector_ushort * v, const gsl_matrix_ushort * m, const size_t i);
GSL_EXPORT int gsl_matrix_ushort_get_col(gsl_vector_ushort * v, const gsl_matrix_ushort * m, const size_t j);
GSL_EXPORT int gsl_matrix_ushort_set_row(gsl_matrix_ushort * m, const size_t i, const gsl_vector_ushort * v);
GSL_EXPORT int gsl_matrix_ushort_set_col(gsl_matrix_ushort * m, const size_t j, const gsl_vector_ushort * v);

/* inline functions if you are using GCC */

#ifdef HAVE_INLINE
extern inline
unsigned short
gsl_matrix_ushort_get(const gsl_matrix_ushort * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
    }
#endif
  return m->data[i * m->tda + j] ;
}

extern inline
void
gsl_matrix_ushort_set(gsl_matrix_ushort * m, const size_t i, const size_t j, const unsigned short x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  m->data[i * m->tda + j] = x ;
}

extern inline
unsigned short *
gsl_matrix_ushort_ptr(gsl_matrix_ushort * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  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) ;
    }
#endif
  return (unsigned short *) (m->data + (i * m->tda + j)) ;
}

extern inline
const unsigned short *
gsl_matrix_ushort_const_ptr(const gsl_matrix_ushort * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  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) ;
    }
#endif
  return (const unsigned short *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_USHORT_H__ */


#ifndef TTT_AUDIOCACHE_H
#define TTT_AUDIOCACHE_H

#include <SDL_mixer.h>

#include <gsl/util>

#include <vector>
#include <memory>
#include <string_view>

namespace ttt
{
	// A class for caching sounds and music.
	class AudioCache final
	{
	public:
		using SizeType = gsl::index;

		Mix_Music *loadMusic(std::string_view path);
		Mix_Chunk *loadChunk(std::string_view path);

		void unloadMusic(SizeType index) noexcept;
		void unloadChunk(SizeType index) noexcept;

		void eraseMusic(SizeType index) noexcept;
		void eraseChunk(SizeType index) noexcept;

		void clearMusic() noexcept;
		void clearChunks() noexcept;
		void clear() noexcept;

		[[nodiscard]] Mix_Music *getMusic(SizeType index) const noexcept;
		[[nodiscard]] Mix_Chunk *getChunk(SizeType index) const noexcept;

	private:
		std::vector<std::unique_ptr<Mix_Music, decltype(&Mix_FreeMusic)>> music;
		std::vector<std::unique_ptr<Mix_Chunk, decltype(&Mix_FreeChunk)>> chunks;
	};

	inline void AudioCache::unloadMusic(SizeType index) noexcept { gsl::at(music, index).reset(); }
	inline void AudioCache::unloadChunk(SizeType index) noexcept { gsl::at(chunks, index).reset(); }

	inline void AudioCache::eraseMusic(SizeType index) noexcept { music.erase(music.cbegin() + index); }
	inline void AudioCache::eraseChunk(SizeType index) noexcept { chunks.erase(chunks.cbegin() + index); }

	inline void AudioCache::clearMusic() noexcept { music.clear(); }
	inline void AudioCache::clearChunks() noexcept { chunks.clear(); }

	inline Mix_Music *AudioCache::getMusic(SizeType index) const noexcept { return gsl::at(music, index).get(); }
	inline Mix_Chunk *AudioCache::getChunk(SizeType index) const noexcept { return gsl::at(chunks, index).get(); }
}

#endif


#pragma once

#include "table.h"
#include <gsl/span>
#include <new>

template <typename T>
T* CreateTypeBuffer(std::size_t rowCount) {
    return new T[rowCount]();
}

template <typename T>
struct CreateTypeBuffers { };

template <typename... Ts>
struct CreateTypeBuffers<std::tuple<Ts...>> {
    static void** exec(std::size_t rowCount) {
        return new void* [sizeof...(Ts)]{ CreateTypeBuffer<Ts>(rowCount)... };
    }
};

template <typename... Ts>
Table CreateTable(std::size_t rowCount) {
    using ColumnList = ColumnListT<Ts...>;
    return {
        .columns = ColumnList(),
        .instanceData = CreateTypeBuffers<typename ColumnList::types>::exec(rowCount),
        .rowCount = 0,
        .rowCapacity = rowCount,
        .cellMetas = ColumnList::metas.data(),
    };
}

class TableFactory {
public:
    Table Alloc(gsl::span<const CellMeta> metas) {
        const auto rowCapacity = 128;
        const auto columnCount = metas.size();

        auto columns = static_cast<ColumnId*>(malloc(columnCount * sizeof(ColumnId)));
        auto instances = new void*[columnCount];
        auto cellMetas = static_cast<CellMeta*>(malloc(columnCount * sizeof(CellMeta)));

        auto index = 0;
        for (const auto& meta : metas) {
            new(columns + index) ColumnId(meta.id);
            instances[index] = std::calloc(rowCapacity, meta.storageBytes);
            new(cellMetas + index) CellMeta(meta);
            ++index;
        }

        Table ret = {};
        ret.columns = ColumnList(columns, columnCount);
        ret.cellMetas = cellMetas;
        ret.instanceData = instances;
        ret.rowCapacity = rowCapacity;
        return ret;
    }

    void Free(const Table& /*table*/) {
    }
};


#pragma once

#include <halley/utils/utils.h>
#include <vector>
#include <gsl/gsl>
#include "halley/data_structures/flat_map.h"
#include "halley/maths/vector2.h"
#include "halley/maths/rect.h"
#include "halley/file/path.h"
#include <map>
#include <unordered_map>
#include <cstdint>
#include <utility>
#include <set>
#include "halley/data_structures/maybe.h"
#include "halley/maths/colour.h"
#include "halley/maths/vector4.h"

namespace Halley {
	class String;

	class SerializerOptions {
	public:
		constexpr static int maxVersion = 1;

		int version = 0;
		bool exhaustiveDictionary = false;
		std::function<std::optional<size_t>(const String& string)> stringToIndex;
		std::function<const String&(size_t index)> indexToString;

		SerializerOptions() = default;
		SerializerOptions(int version)
			: version(version)
		{}
	};

	class SerializerState {};

	class ByteSerializationBase {
	public:
		ByteSerializationBase(SerializerOptions options)
			: options(std::move(options))
		{}

		SerializerState* setState(SerializerState* state);

		template <typename T>
		T* getState() const
		{
			return static_cast<T*>(state);
		}

		int getVersion() const { return version; }
		void setVersion(int v) { version = v; }

	protected:
		SerializerOptions options;

	private:
		SerializerState* state = nullptr;
		int version = 0;
	};

	class Serializer : public ByteSerializationBase {
	public:
		Serializer(SerializerOptions options);
		explicit Serializer(gsl::span<gsl::byte> dst, SerializerOptions options);

		template <typename T, typename std::enable_if<std::is_convertible<T, std::function<void(Serializer&)>>::value, int>::type = 0>
		static Bytes toBytes(const T& f, SerializerOptions options = {})
		{
			auto dry = Serializer(options);
			f(dry);
			Bytes result(dry.getSize());
			auto s = Serializer(gsl::as_writable_bytes(gsl::span<Halley::Byte>(result)), options);
			f(s);
			return result;
		}

		template <typename T, typename std::enable_if<!std::is_convertible<T, std::function<void(Serializer&)>>::value, int>::type = 0>
		static Bytes toBytes(const T& value, SerializerOptions options = {})
		{
			return toBytes([&value](Serializer& s) { s << value; }, options);
		}

		size_t getSize() const { return size; }

		Serializer& operator<<(bool val) { return serializePod(val); }
		Serializer& operator<<(int8_t val) { return serializeInteger(val); }
		Serializer& operator<<(uint8_t val) { return serializeInteger(val); }
		Serializer& operator<<(int16_t val) { return serializeInteger(val); }
		Serializer& operator<<(uint16_t val) { return serializeInteger(val); }
		Serializer& operator<<(int32_t val) { return serializeInteger(val); }
		Serializer& operator<<(uint32_t val) { return serializeInteger(val); }
		Serializer& operator<<(int64_t val) { return serializeInteger(val); }
		Serializer& operator<<(uint64_t val) { return serializeInteger(val); }
		Serializer& operator<<(float val) { return serializePod(val); }
		Serializer& operator<<(double val) { return serializePod(val); }

		Serializer& operator<<(const std::string& str);
		Serializer& operator<<(const String& str);
		Serializer& operator<<(const StringUTF32& str);
		Serializer& operator<<(const Path& path);
		Serializer& operator<<(gsl::span<const gsl::byte> span);
		Serializer& operator<<(const Bytes& bytes);

		template <typename T>
		Serializer& operator<<(const std::vector<T>& val)
		{
			unsigned int sz = static_cast<unsigned int>(val.size());
			*this << sz;
			for (unsigned int i = 0; i < sz; i++) {
				*this << val[i];
			}
			return *this;
		}

		template <typename T>
		Serializer& operator<<(const std::list<T>& val)
		{
			unsigned int sz = static_cast<unsigned int>(val.size());
			*this << sz;
			for (const auto& v: val) {
				*this << v;
			}
			return *this;
		}

		template <typename T, typename U>
		Serializer& operator<<(const FlatMap<T, U>& val)
		{
			*this << static_cast<unsigned int>(val.size());
			for (auto& kv : val) {
				*this << kv.first << kv.second;
			}
			return *this;
		}

		template <typename K, typename V, typename Cmp, typename Allocator>
		Serializer& operator<<(const std::map<K, V, Cmp, Allocator>& val)
		{
			*this << static_cast<unsigned int>(val.size());
			for (auto& kv : val) {
				*this << kv.first << kv.second;
			}
			return *this;
		}

		template <typename T, typename U>
		Serializer& operator<<(const std::unordered_map<T, U>& val)
		{
			std::map<T, U> m;
			for (auto& kv: val) {
				m[kv.first] = kv.second;
			}
			return (*this << m);
		}

		template <typename T>
		Serializer& operator<<(const std::set<T>& val)
		{
			unsigned int sz = static_cast<unsigned int>(val.size());
			*this << sz;
			for (auto& v: val) {
				*this << v;
			}
			return *this;
		}

		template <typename T>
		Serializer& operator<<(const Vector2D<T>& val)
		{
			return *this << val.x << val.y;
		}

		template <typename T>
		Serializer& operator<<(const Vector4D<T>& val)
		{
			return *this << val.x << val.y << val.z << val.w;
		}

		template <typename T>
		Serializer& operator<<(const Colour4<T>& val)
		{
			return *this << val.r << val.g << val.b << val.a;
		}

		template <typename T>
		Serializer& operator<<(const Rect2D<T>& val)
		{
			return *this << val.getTopLeft() << val.getBottomRight();
		}

		template <typename T, typename U>
		Serializer& operator<<(const std::pair<T, U>& p)
		{
			return *this << p.first << p.second;
		}

		template <typename T>
		Serializer& operator<<(const std::optional<T>& p)
		{
			if (p) {
				return *this << true << p.value();
			} else {
				return *this << false;
			}
		}

		template <typename T>
		Serializer& operator<<(const Range<T>& p)
		{
			return *this << p.start << p.end;
		}

		template <typename T, std::enable_if_t<std::is_enum<T>::value == true, int> = 0>
		Serializer& operator<<(const T& val)
		{
			using B = typename std::underlying_type<T>::type;
			return *this << B(val);
		}

		template <typename T, std::enable_if_t<std::is_enum<T>::value == false, int> = 0>
		Serializer& operator<<(const T& val)
		{
			val.serialize(*this);
			return *this;
		}

		size_t getPosition() const { return size; }

	private:
		size_t size = 0;
		gsl::span<gsl::byte> dst;
		bool dryRun;

		template <typename T>
		Serializer& serializePod(T val)
		{
			if (!dryRun) {
				memcpy(dst.data() + size, &val, sizeof(T));
			}
			size += sizeof(T);
			return *this;
		}

		template <typename T>
		Serializer& serializeInteger(T val)
		{
			if (options.version >= 1) {
				// Variable-length
				if constexpr (std::is_signed_v<T>) {
					serializeVariableInteger(static_cast<uint64_t>(val >= 0 ? val : -(val + 1)), val < 0);
				} else {
					serializeVariableInteger(val, {});
				}
				return *this;
			} else {
				// Fixed length
				return serializePod(val);
			}
		}

		void serializeVariableInteger(uint64_t val, std::optional<bool> sign);
	};

	class Deserializer : public ByteSerializationBase {
	public:
		Deserializer(gsl::span<const gsl::byte> src, SerializerOptions options = {});
		Deserializer(const Bytes& src, SerializerOptions options = {});

		template <typename T>
		static T fromBytes(const Bytes& src, SerializerOptions options = {})
		{
			T result;
			Deserializer s(src, std::move(options));
			s >> result;
			return result;
		}

		template <typename T>
		static T fromBytes(gsl::span<const gsl::byte> src, SerializerOptions options = {})
		{
			T result;
			Deserializer s(src, std::move(options));
			s >> result;
			return result;
		}

		template <typename T>
		static void fromBytes(T& target, const Bytes& src, SerializerOptions options = {})
		{
			Deserializer s(src, std::move(options));
			s >> target;
		}

		template <typename T>
		static void fromBytes(T& target, gsl::span<const gsl::byte> src, SerializerOptions options = {})
		{
			Deserializer s(src, std::move(options));
			s >> target;
		}

		Deserializer& operator>>(bool& val) { return deserializePod(val); }
		Deserializer& operator>>(int8_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(uint8_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(int16_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(uint16_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(int32_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(uint32_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(int64_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(uint64_t& val) { return deserializeInteger(val); }
		Deserializer& operator>>(float& val) { return deserializePod(val); }
		Deserializer& operator>>(double& val) { return deserializePod(val); }

		Deserializer& operator>>(std::string& str);
		Deserializer& operator>>(String& str);
		Deserializer& operator>>(StringUTF32& str);
		Deserializer& operator>>(Path& p);
		Deserializer& operator>>(gsl::span<gsl::byte> span);
		Deserializer& operator>>(Bytes& bytes);

		template <typename T>
		Deserializer& operator>>(std::vector<T>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(sz); // Expect at least one byte per vector entry

			val.clear();
			val.reserve(sz);
			for (unsigned int i = 0; i < sz; i++) {
				val.push_back(T());
				*this >> val[i];
			}
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(std::list<T>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(sz); // Expect at least one byte per vector entry

			val.clear();
			for (unsigned int i = 0; i < sz; i++) {
				T v;
				*this >> v;
				val.push_back(std::move(v));
			}
			return *this;
		}

		template <typename T, typename U>
		Deserializer& operator>>(FlatMap<T, U>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(sz * 2); // Expect at least two bytes per map entry

			std::vector<std::pair<T, U>> tmpData(sz);
			for (unsigned int i = 0; i < sz; i++) {
				*this >> tmpData[i].first >> tmpData[i].second;
			}
			val = FlatMap<T, U>(boost::container::ordered_unique_range_t(), tmpData.begin(), tmpData.end());
			return *this;
		}

		template <typename K, typename V, typename Cmp, typename Allocator>
		Deserializer& operator >> (std::map<K, V, Cmp, Allocator>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(size_t(sz) * 2); // Expect at least two bytes per map entry

			for (unsigned int i = 0; i < sz; i++) {
				K key;
				V value;
				*this >> key >> value;
				val[key] = std::move(value);
			}
			return *this;
		}

		template <typename T, typename U>
		Deserializer& operator >> (std::unordered_map<T, U>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(sz * 2); // Expect at least two bytes per map entry

			for (unsigned int i = 0; i < sz; i++) {
				T key;
				U value;
				*this >> key >> value;
				val[key] = std::move(value);
			}
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(std::set<T>& val)
		{
			unsigned int sz;
			*this >> sz;
			ensureSufficientBytesRemaining(sz); // Expect at least one byte per set entry

			val.clear();
			for (unsigned int i = 0; i < sz; i++) {
				T v;
				*this >> v;
				val.insert(std::move(v));
			}
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(Vector2D<T>& val)
		{
			*this >> val.x;
			*this >> val.y;
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(Vector4D<T>& val)
		{
			*this >> val.x;
			*this >> val.y;
			*this >> val.z;
			*this >> val.w;
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(Colour4<T>& val)
		{
			*this >> val.r;
			*this >> val.g;
			*this >> val.b;
			*this >> val.a;
			return *this;
		}

		template <typename T>
		Deserializer& operator>>(Rect2D<T>& val)
		{
			Vector2D<T> p1, p2;
			*this >> p1;
			*this >> p2;
			val = Rect2D<T>(p1, p2);
			return *this;
		}

		template <typename T, typename U>
		Deserializer& operator>>(std::pair<T, U>& p)
		{
			return *this >> p.first >> p.second;
		}

		template <typename T>
		Deserializer& operator>>(Range<T>& p)
		{
			return *this >> p.start >> p.end;
		}

		template <typename T>
		Deserializer& operator>>(std::optional<T>& p)
		{
			bool present;
			*this >> present;
			if (present) {
				T tmp;
				*this >> tmp;
				p = tmp;
			} else {
				p = std::optional<T>();
			}
			return *this;
		}

		template <typename T, std::enable_if_t<std::is_enum<T>::value == true, int> = 0>
		Deserializer& operator>>(T& val)
		{
			typename std::underlying_type<T>::type tmp;
			*this >> tmp;
			val = T(tmp);
			return *this;
		}

		template <typename T, std::enable_if_t<std::is_enum<T>::value == false, int> = 0>
		Deserializer& operator>>(T& val)
		{
			val.deserialize(*this);
			return *this;
		}

		template <typename T>
		void peek(T& val)
		{
			const auto oldPos = pos;
			*this >> val;
			pos = oldPos;
		}

		size_t getPosition() const { return pos; }

	private:
		size_t pos = 0;
		gsl::span<const gsl::byte> src;

		template <typename T>
		Deserializer& deserializePod(T& val)
		{
			ensureSufficientBytesRemaining(sizeof(T));
			memcpy(&val, src.data() + pos, sizeof(T));
			pos += sizeof(T);
			return *this;
		}

		template <typename T>
		Deserializer& deserializeInteger(T& val)
		{
			if (options.version >= 1) {
				// Variable-length
				bool sign;
				uint64_t temp;
				deserializeVariableInteger(temp, sign, std::is_signed_v<T>);
				if (sign) {
					int64_t signedTemp = -int64_t(temp) - 1;
					val = static_cast<T>(signedTemp);
				} else {
					val = static_cast<T>(temp);
				}
				return *this;
			} else {
				// Fixed length
				return deserializePod(val);
			}
		}

		void deserializeVariableInteger(uint64_t& val, bool& sign, bool isSigned);

		void ensureSufficientBytesRemaining(size_t bytes);
		size_t getBytesRemaining() const;
	};
}


/*
 * Subroutines that deal with the aXe configuration file.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_vector.h>
#include "aper_conf.h"
#include "disp_conf.h"
#include "aXe_grism.h"
#include "spc_cfg.h"
#include "disp_conf.h"
#include "aXe_utils.h"

#define MAX(x,y) (((x)>(y))?(x):(y))

/**
 *  Function: get_aperture_descriptor
 *  Read a configuration file and populate an aperture_conf structure
 *  containing a description of the optical set up.
 *  This function populates the geometrical description of all the beams in
 *  all the apertures listed in the configuration file.
 *
 *  Parameters:
 *  @param actinfo  - the header structure with the column names
 *  @param filename - the complete filename to the axe configuration file
 *
 *  Returns:
 *  @return config - the configuration structure created
 */
aperture_conf *
get_aperture_descriptor (char *filename)
{
  char beam[MAXCHAR] = "\0";
  aperture_conf *config;
  int i, ix;
  gsl_vector *v;

  struct CfgStrings AperConfig[] = {
    {"INSTRUMENT", NULL},
    {"CAMERA", NULL},
    {"SCIENCE_EXT", NULL},
    {"ERRORS_EXT", NULL},
    {"DQ_EXT",NULL},
    {"DQMASK",NULL},
    {"EXPTIME",NULL},
    {"GAIN",NULL},
    {"OPTKEY1",NULL},
    {"OPTVAL1",NULL},
    {"FFNAME",NULL},
    {"REFX",NULL},
    {"REFY",NULL},
    /* aXe1.4:*/
    {"DRZRESOLA",NULL},
    {"DRZSCALE",NULL},
    {"DRZLAMB0",NULL},
    {"DRZXINI",NULL},
    {"DRZPFRAC",NULL},
    {"DRZKERNEL",NULL},
    /* aXe1.5: */
    {"PSFCOEFFS",NULL},
    {"PSFRANGE",NULL},
    {"RDNOISE",NULL},
    /* NICMOS HLA */
    {"IPIXFUNCTION",NULL},
    {"POBJSIZE",NULL},
    {"SMFACTOR",NULL},

    {NULL, NULL},
    {NULL, NULL}                /* array terminator. REQUIRED !!! */
  };

  struct CfgStrings BeamConfig[] = {
    {NULL, NULL},
    {NULL, NULL}
  };

  AperConfig[25].name = beam;
  BeamConfig[0].name = beam;

  config = malloc (sizeof (aperture_conf));
  if (config == NULL)
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "Could not allocate memory for aperture configuration");
    }

  CfgRead (filename, AperConfig);

  /* Initialize optional keywords to default values*/
  sprintf(config->instrument,"None");
  sprintf(config->camera,"None");
  sprintf(config->science_ext,"None");
  sprintf(config->errors_ext,"None");
  sprintf(config->dq_ext,"None");
  sprintf(config->exptimekey,"None");
  sprintf(config->gainkey,"None");
  config->dqmask=0;
  sprintf(config->optkey1,"None");
  sprintf(config->optval1,"None");
  sprintf(config->FFname,"None");
  sprintf(config->IPIXfunc,"None");
  sprintf(config->drz_kernel,"square");
  config->refx       = 0;
  config->refy       = 0;
  config->drz_resol  = 0.0;
  config->drz_scale  = 0.0;
  config->drz_lamb0  = 0.0;
  config->drz_xstart = 15.0;
  config->drz_pfrac  = 1.0;
  config->psfcoeffs  = NULL;
  config->psfrange   = NULL;
  config->rdnoise    = 0.0;
  config->pobjsize   = -1.0;
  config->smfactor   = -1.0;

  for (ix = 0; ix < 26; ix++)
    {

      /* Name of the instrument */
      if (!strcmp (AperConfig[ix].name, "INSTRUMENT"))
        {
          if (AperConfig[ix].data == NULL)
            aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                         "INSTRUMENT tag was not read from %s\n",
                         filename);
          sprintf (config->instrument, "%s", AperConfig[ix].data);
        }

      /* Name of the camera */
      if (!strcmp (AperConfig[ix].name, "CAMERA"))
        {
          if (AperConfig[ix].data == NULL)
            aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                         "camera tag was not read from %s\n",
                         filename);
          sprintf (config->camera, "%s", AperConfig[ix].data);
        }

      /* Name of the Science data extension */
      if (!strcmp (AperConfig[ix].name, "SCIENCE_EXT"))
        {
          if (AperConfig[ix].data != NULL)
            sprintf (config->science_ext, "%s", AperConfig[ix].data);
        }

      /* Name of the optional Error FITS extension */
      if (!strcmp (AperConfig[ix].name, "ERRORS_EXT"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->errors_ext, "%s", AperConfig[ix].data);
            }
        }

      /* Name of the optional Data Quality FITS extension */
      if (!strcmp (AperConfig[ix].name, "DQ_EXT"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->dq_ext, "%s", AperConfig[ix].data);
            }
        }

      /* Name of the optional EXPTIME FITS keyword */
      if (!strcmp (AperConfig[ix].name, "EXPTIME"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->exptimekey, "%s", AperConfig[ix].data);
            }
        }

      /* Red in the readout noise */
      if (!strcmp (AperConfig[ix].name, "RDNOISE"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->rdnoise = atof(AperConfig[ix].data);
            }
        }

      /* Name of the optional GAIN FITS keyword */
      if (!strcmp (AperConfig[ix].name, "GAIN"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->gainkey, "%s", AperConfig[ix].data);
            }
        }

      /* Gett the Data Quality Mask */
      if (!strcmp (AperConfig[ix].name, "DQMASK"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->dqmask = atoi(AperConfig[ix].data);
            }
        }

      /* Optional OPTional 1st FITS keyword selection */
      if (!strcmp (AperConfig[ix].name, "OPTKEY1"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->optkey1, "%s", AperConfig[ix].data);
            }
        }

      /* Optional OPTional 1st FITS keyword value */
      if (!strcmp (AperConfig[ix].name, "OPTVAL1"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->optval1, "%s", AperConfig[ix].data);
            }
        }

      /* Optional FFname keyword value */
      if (!strcmp (AperConfig[ix].name, "FFNAME"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->FFname, "%s", AperConfig[ix].data);
            }
        }

      /* Optional intra pixel correction keyword value */
      if (!strcmp (AperConfig[ix].name, "IPIXFUNCTION"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->IPIXfunc, "%s", AperConfig[ix].data);
            }
        }

      /* Optional REFX keyword value */
      if (!strcmp (AperConfig[ix].name, "REFX"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->refx = atoi(AperConfig[ix].data);
            }
        }

      /* Optional REFY keyword value */
      if (!strcmp (AperConfig[ix].name, "REFY"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->refy = atoi(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZRESOLA"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->drz_resol = atof(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZSCALE"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->drz_scale = atof(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZLAMB0"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->drz_lamb0 = atof(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZXINI"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->drz_xstart = atof(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZPFRAC"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->drz_pfrac = atof(AperConfig[ix].data);
            }
        }
      if (!strcmp (AperConfig[ix].name, "DRZKERNEL"))
        {
          if (AperConfig[ix].data != NULL)
            {
              sprintf (config->drz_kernel, "%s", AperConfig[ix].data);
            }
        }

      if (!strcmp (AperConfig[ix].name, "PSFCOEFFS"))
        {
          if (AperConfig[ix].data != NULL)
            {
              //              sprintf (config->drz_kernel, "%s", AperConfig[ix].data);
              v = string_to_gsl_array (AperConfig[ix].data);
              config->psfcoeffs = v;
            }
        }

      if (!strcmp (AperConfig[ix].name, "PSFRANGE"))
        {
          if (AperConfig[ix].data != NULL)
            {
              v = string_to_gsl_array (AperConfig[ix].data);
              if (v->size != 2)
                aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                             "configuration file: two items for PSFRANGE, lambda_min and lambda_max must be given, not %i\n", v->size);
              config->psfrange = v;
            }
        }

      // read in the size of point-like objects
      if (!strcmp (AperConfig[ix].name, "POBJSIZE"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->pobjsize = atof(AperConfig[ix].data);
            }
        }

      // read in the adjustment for smoothed flux conversion
      if (!strcmp (AperConfig[ix].name, "SMFACTOR"))
        {
          if (AperConfig[ix].data != NULL)
            {
              config->smfactor = atof(AperConfig[ix].data);
            }
        }

    }

  // release memory
  i=0;
  while(AperConfig[i].name!=NULL)
    free(AperConfig[i++].data);

  /* Looking for up to MAX_BEAMS beams */
  config->nbeams = 0;
  for (i = 0; i < MAX_BEAMS; i++)
    {
      sprintf (beam, "BEAM%c", BEAM (i));
      CfgRead (filename, BeamConfig);
      if (BeamConfig[0].data != NULL)
        {
          v = string_to_gsl_array (BeamConfig[0].data);
          if (v->size != 2)
            {
              aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                           "tag %s does not have the proper format in file %s.\n"
                           "Should be of the form %s [integer] [integer]\n",
                           BeamConfig[0].data, filename,beam);
              //                           beam, filename,beam);
            }
          (config->beam[config->nbeams]).offset.dx0 =
            (int) gsl_vector_get (v, 0);
          (config->beam[config->nbeams]).offset.dx1 =
            (int) gsl_vector_get (v, 1);

          // release the memory
          gsl_vector_free(v);

          (config->beam[config->nbeams]).ID = i;

          // release memory
          ix=0;
          while(BeamConfig[ix].name!=NULL)
            free(BeamConfig[ix++].data);
          BeamConfig[0].data = NULL;

          (config->beam[config->nbeams]).mmag_extract = get_beam_mmag_extract (filename, i);
          (config->beam[config->nbeams]).mmag_mark = get_beam_mmag_mark (filename, i);

          sprintf (beam, "PSF_OFFSET_%c", BEAM (i));
          CfgRead (filename, BeamConfig);
          if (BeamConfig[0].data != NULL)
            {
              (config->beam[config->nbeams]).psf_offset = atof(BeamConfig[0].data);
            }
          else
            {
              (config->beam[config->nbeams]).psf_offset = 0.0;
            }

          // release memory
          ix=0;
          while(BeamConfig[ix].name!=NULL)
            free(BeamConfig[ix++].data);

          BeamConfig[0].data = NULL;

          config->nbeams = config->nbeams + 1;
        }
    }

  if (config->nbeams == 0)
    {
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
                   "No beam was found in %s. At least one beam must be found.\n",
                   filename);
    }
  return config;
}

/**
 * Function: aperture_conf_fprintf
 * Function to output the content of an aperture_conf structure.
 *
 * Parameters:
 * @param file - a pointer to an output stream or file
 * @param conf - a pointer to an existing aperture_conf structure
 */
void
aperture_conf_fprintf (FILE * file, aperture_conf * conf)
{
     int i = 0, id;

     fprintf (file, "Aperture_conf: INSTRUMENT: %s\n", conf->instrument);
     fprintf (file, "Aperture_conf: NBEAMS: %d\n", conf->nbeams);
     for (i = 0; i < conf->nbeams; i++)
       {
         id = conf->beam[i].ID;
         fprintf (file, "Aperture_conf: BEAM%c: %d %d\n", BEAM (id),
                  conf->beam[i].offset.dx0, conf->beam[i].offset.dx1);
       }
     fprintf (file, "FITS SCI extension name: %s, number: %d\n",
              conf->science_ext, conf->science_numext);
     fprintf (file, "FITS ERR extension name: %s, number: %d\n",
              conf->errors_ext, conf->errors_numext);
     fprintf (file, "FITS DQ extension name: %s, number: %d\n",
              conf->dq_ext, conf->dq_numext);
     fprintf (file, "DQ mask: %d\n",conf->dqmask);
     fprintf (file, "FF filename: %s\n",conf->FFname);
     fprintf (file, "Refx: %d\n",conf->refx);
     fprintf (file, "Refy: %d\n",conf->refy);
     fprintf (file, "DRZRESO: %10.3f\n",conf->drz_resol);
     fprintf (file, "DRZSCALE: %10.3f\n",conf->drz_scale);
     fprintf (file, "DRZLAMB0: %10.3f\n",conf->drz_lamb0);
     fprintf (file, "DRZXINI: %10.3f\n",conf->drz_xstart);
     fprintf (file, "DRZPFRAC: %10.3f\n",conf->drz_pfrac);
     fprintf (file, "DRZKERNEL: %s\n",conf->drz_kernel);

}

/**
 * Function: get_extension_numbers
 * This function assigns HDU numbers to an aperture_conf
 * structues after looking for the location of named
 * extension in a given FITS file. Optional values not
 * found are set to -1.
 *
 * Parameters:
 * @param filename - a pointer to a string containing the name
 *                   of an existing FITS file
 * @param conf     - a pointer to a populated aperture_conf structure
 * @param keyword  - a pointer to a string containing the name of
 *                   an optional keyword to read
 * @param keyval   - a pointer to a string containing the value of
 *                   the optional keyword to look for
 */
void
get_extension_numbers(char filename[], aperture_conf * conf,
                      char keyword[],char keyval[])
{
  int extver = -1;

  conf->science_numext = get_hdunum_from_hduname(filename,
                                                 conf->science_ext,keyword
                                                 ,keyval,extver);
  if (conf->science_numext > -1)
    extver = (int)get_float_from_keyword(filename, conf->science_numext, "EXTVER");

  if (strcmp(conf->errors_ext,"None"))
  {
    conf->errors_numext = get_hdunum_from_hduname(filename,
                                                  conf->errors_ext,keyword,
                                                  keyval,extver);
  } else {
    conf->errors_numext = -1;
  }
  if (strcmp(conf->dq_ext,"None")) {
    conf->dq_numext = get_hdunum_from_hduname(filename,conf->dq_ext,
                                              keyword,keyval,extver);
  } else {
    conf->dq_numext = -1;
  }
}

/**
 * Function: free_aperture_conf
 * The function releases all memory allocated
 * in a configuration structure.
 *
 * Parameters:
 * @param conf - the configuration structure
 */
void
free_aperture_conf(aperture_conf * conf)
{
  if (conf->psfcoeffs)
    gsl_vector_free(conf->psfcoeffs);
  if (conf->psfrange)
    gsl_vector_free(conf->psfrange);
  free(conf);
  conf = NULL;
}

/**
 * Function: get_psf_offset
 * The function extracts and returns the psf-offset
 * stored in a given configuration structure and
 * a given beam
 *
 * Parameters:
 * @param conf - the configuration structure
 * @param beam - the beam
 *
 * Returns:
 * @return psf_offset - the psf-offset for the beam
 */
double
get_psf_offset(aperture_conf * conf, const beam actbeam)
{
  // initialize the offset
  double psf_offset=0.0;

  int i=0;

  // go over all beams in the configuration structure
  for (i = 0; i < conf->nbeams; i++)
    {
      // check whether the current beam is the
      // right one
      if (conf->beam[i].ID == actbeam.ID)
        // return the offset
        return conf->beam[i].psf_offset;
    }

  // return the default offset
  return psf_offset;
}

/**
 * Function: get_psf_offset
 * The function extracts and returns the psf-offset
 * stored in a given configuration structure and
 * a given beam
 *
 * Parameters:
 * @param conf - the configuration structure
 * @param beam - the beam
 *
 * Returns:
 * @return psf_offset - the psf-offset for the beam
 */
double
get_max_offset(aperture_conf * conf)
{
  // initialize the offset
  double max_offset=0.0;

  int i=0;

  // go over all beams in the configuration structure
  for (i = 0; i < conf->nbeams; i++)
    {
      // check whether the current beam is the
      // right one
      max_offset = MAX(conf->beam[i].psf_offset, max_offset);
    }

  // return the default offset
  return max_offset;
}

/**
 * Function: extend_config_beams
 * The function extends the size of the beams in a
 * configuration structure.
 *
 * Parameters:
 * @param conf - the configuration structure
 */
void
extend_config_beams(aperture_conf *conf)
{
  int index;

  // go over all beams in the configuration structure
  for (index=0; index < conf->nbeams; index++)
    {
      // lower the left border
      conf->beam[index].offset.dx0 -= BCK_BEAM_EXT;

      // enhance the right border
      conf->beam[index].offset.dx1 += BCK_BEAM_EXT;
    }
}


#ifndef __GSL_SPMATRIX_H__
#define __GSL_SPMATRIX_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

enum
{
  GSL_SPMATRIX_COO = 0, /* coordinate/triplet representation */
  GSL_SPMATRIX_CSC = 1, /* compressed sparse column */
  GSL_SPMATRIX_CSR = 2, /* compressed sparse row */
  GSL_SPMATRIX_TRIPLET = GSL_SPMATRIX_COO,
  GSL_SPMATRIX_CCS = GSL_SPMATRIX_CSC,
  GSL_SPMATRIX_CRS = GSL_SPMATRIX_CSR
};

/* memory pool for binary tree node allocation */
struct gsl_spmatrix_pool_node
{
  struct gsl_spmatrix_pool_node * next;
  void * block_ptr;          /* pointer to memory block, of size n*tree_node_size */
  unsigned char * free_slot; /* pointer to next available slot */
};

typedef struct gsl_spmatrix_pool_node gsl_spmatrix_pool;

#define GSL_SPMATRIX_ISCOO(m)         ((m)->sptype == GSL_SPMATRIX_COO)
#define GSL_SPMATRIX_ISCSC(m)         ((m)->sptype == GSL_SPMATRIX_CSC)
#define GSL_SPMATRIX_ISCSR(m)         ((m)->sptype == GSL_SPMATRIX_CSR)

#define GSL_SPMATRIX_ISTRIPLET(m)     GSL_SPMATRIX_ISCOO(m)
#define GSL_SPMATRIX_ISCCS(m)         GSL_SPMATRIX_ISCSC(m)
#define GSL_SPMATRIX_ISCRS(m)         GSL_SPMATRIX_ISCSR(m)

#define GSL_SPMATRIX_FLG_GROW         (1 << 0) /* allow size of matrix to grow as elements are added */
#define GSL_SPMATRIX_FLG_FIXED        (1 << 1) /* sparsity pattern is fixed */

/* compare matrix entries (ia,ja) and (ib,jb) - sort by rows first, then by columns */
#define GSL_SPMATRIX_COMPARE_ROWCOL(m,ia,ja,ib,jb)   ((ia) < (ib) ? -1 : ((ia) > (ib) ? 1 : ((ja) < (jb) ? -1 : ((ja) > (jb)))))

/* common/utility functions */

GSL_FUN void gsl_spmatrix_cumsum(const size_t n, int * c);

#include <gsl/gsl_spmatrix_complex_long_double.h>
#include <gsl/gsl_spmatrix_complex_double.h>
#include <gsl/gsl_spmatrix_complex_float.h>

#include <gsl/gsl_spmatrix_long_double.h>
#include <gsl/gsl_spmatrix_double.h>
#include <gsl/gsl_spmatrix_float.h>

#include <gsl/gsl_spmatrix_ulong.h>
#include <gsl/gsl_spmatrix_long.h>

#include <gsl/gsl_spmatrix_uint.h>
#include <gsl/gsl_spmatrix_int.h>

#include <gsl/gsl_spmatrix_ushort.h>
#include <gsl/gsl_spmatrix_short.h>

#include <gsl/gsl_spmatrix_uchar.h>
#include <gsl/gsl_spmatrix_char.h>

#endif /* __GSL_SPMATRIX_H__ */


/**
 * This file is part of the "libterminal" project
 *   Copyright (c) 2019-2020 Christian Parpart <christian@parpart.family>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * 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.
 */
#pragma once

#include <terminal/GraphicsAttributes.h>
#include <terminal/Line.h>
#include <terminal/primitives.h>

#include <crispy/algorithm.h>
#include <crispy/assert.h>
#include <crispy/ring.h>

#include <unicode/convert.h>

#include <range/v3/algorithm/copy.hpp>
#include <range/v3/iterator/insert_iterators.hpp>
#include <range/v3/view/iota.hpp>

#include <gsl/span>
#include <gsl/span_ext>

#include <algorithm>
#include <array>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>

namespace terminal
{

// {{{ Margin
struct Margin
{
    struct Horizontal
    {
        ColumnOffset from;
        ColumnOffset
            to; // TODO: call it begin and end and have end point to to+1 to avoid unnecessary +1's later

        [[nodiscard]] constexpr ColumnCount length() const noexcept
        {
            return unbox<ColumnCount>(to - from) + ColumnCount(1);
        }
        [[nodiscard]] constexpr bool contains(ColumnOffset _value) const noexcept
        {
            return from <= _value && _value <= to;
        }
        [[nodiscard]] constexpr bool operator==(Horizontal rhs) const noexcept
        {
            return from == rhs.from && to == rhs.to;
        }
        [[nodiscard]] constexpr bool operator!=(Horizontal rhs) const noexcept { return !(*this == rhs); }
    };

    struct Vertical
    {
        LineOffset from;
        // TODO: call it begin and end and have end point to to+1 to avoid unnecessary +1's later
        LineOffset to;

        [[nodiscard]] constexpr LineCount length() const noexcept
        {
            return unbox<LineCount>(to - from) + LineCount(1);
        }
        [[nodiscard]] constexpr bool contains(LineOffset _value) const noexcept
        {
            return from <= _value && _value <= to;
        }
        [[nodiscard]] constexpr bool operator==(Vertical const& rhs) const noexcept
        {
            return from == rhs.from && to == rhs.to;
        }
        [[nodiscard]] constexpr bool operator!=(Vertical const& rhs) const noexcept
        {
            return !(*this == rhs);
        }
    };

    Vertical vertical {};     // top-bottom
    Horizontal horizontal {}; // left-right
};

constexpr bool operator==(Margin const& a, PageSize b) noexcept
{
    return a.horizontal.from.value == 0 && a.horizontal.to.value + 1 == b.columns.value
           && a.vertical.from.value == 0 && a.vertical.to.value + 1 == b.lines.value;
}

constexpr bool operator!=(Margin const& a, PageSize b) noexcept
{
    return !(a == b);
}
// }}}

template <typename Cell>
using Lines = crispy::ring<Line<Cell>>;

/**
 * Represents a logical grid line, i.e. a sequence lines that were written without
 * an explicit linefeed, triggering an auto-wrap.
 */
template <typename Cell>
struct LogicalLine
{
    LineOffset top {};
    LineOffset bottom {};
    std::vector<std::reference_wrapper<Line<Cell>>> lines {};

    [[nodiscard]] Line<Cell> joinWithRightTrimmed() const
    {
        // TODO: determine final line's column count and pass it to ctor.
        typename Line<Cell>::Buffer output;
        auto lineFlags = lines.front().get().flags();
        for (Line<Cell> const& line: lines)
            for (Cell const& cell: line.cells())
                output.emplace_back(cell);

        while (!output.empty() && output.back().empty())
            output.pop_back();

        return Line<Cell>(output, lineFlags);
    }

    [[nodiscard]] std::string text() const
    {
        std::string output;
        for (auto const& line: lines)
            output += line.get().toUtf8();
        return output;
    }
};

template <typename Cell>
bool operator==(LogicalLine<Cell> const& a, LogicalLine<Cell> const& b) noexcept
{
    return a.top == b.top && a.bottom == b.bottom;
}

template <typename Cell>
bool operator!=(LogicalLine<Cell> const& a, LogicalLine<Cell> const& b) noexcept
{
    return !(a == b);
}

template <typename Cell>
struct LogicalLines
{
    LineOffset topMostLine;
    LineOffset bottomMostLine;
    std::reference_wrapper<Lines<Cell>> lines;

    struct iterator // {{{
    {
        std::reference_wrapper<Lines<Cell>> lines;
        LineOffset top;
        LineOffset next; // index to next logical line's beginning
        LineOffset bottom;
        LogicalLine<Cell> current;

        iterator(std::reference_wrapper<Lines<Cell>> _lines,
                 LineOffset _top,
                 LineOffset _next,
                 LineOffset _bottom):
            lines { _lines }, top { _top }, next { _next }, bottom { _bottom }
        {
            Require(_top <= next);
            Require(next <= _bottom + 1);
            ++*this;
        }

        LogicalLine<Cell> const& operator*() const noexcept { return current; }
        LogicalLine<Cell> const* operator->() const noexcept { return &current; }

        iterator& operator++()
        {
            if (next == bottom + 1)
            {
                current.top = next;
                current.bottom = next;
                return *this;
            }

            Require(!lines.get()[unbox<int>(next)].wrapped());

            current.top = LineOffset::cast_from(next);
            current.lines.clear();
            do
                current.lines.emplace_back(lines.get()[unbox<int>(next++)]);
            while (next <= bottom && lines.get()[unbox<int>(next)].wrapped());

            current.bottom = LineOffset::cast_from(next - 1);

            return *this;
        }

        iterator& operator--()
        {
            if (next == top - 1)
            {
                current.top = top - 1;
                current.bottom = top - 1;
                return *this;
            }

            auto const bottomMost = next - 1;
            do
                --next;
            while (lines.get()[unbox<int>(next)].wrapped());
            auto const topMost = next;

            current.top = topMost;
            current.bottom = bottomMost;

            current.lines.clear();
            for (auto i = topMost; i <= bottomMost; ++i)
                current.lines.emplace_back(lines.get()[unbox<int>(i)]);

            return *this;
        }

        iterator& operator++(int)
        {
            auto c = *this;
            ++*this;
            return c;
        }
        iterator& operator--(int)
        {
            auto c = *this;
            --*this;
            return c;
        }

        bool operator==(iterator const& other) const noexcept { return current == other.current; }
        bool operator!=(iterator const& other) const noexcept { return current != other.current; }
    }; // }}}

    iterator begin() const { return iterator(lines, topMostLine, topMostLine, bottomMostLine); }
    iterator end() const { return iterator(lines, topMostLine, bottomMostLine + 1, bottomMostLine); }
};

template <typename Cell>
struct ReverseLogicalLines
{
    LineOffset topMostLine;
    LineOffset bottomMostLine;
    std::reference_wrapper<Lines<Cell>> lines;

    struct iterator // {{{
    {
        std::reference_wrapper<Lines<Cell>> lines;
        LineOffset top;
        LineOffset next; // index to next logical line's beginning
        LineOffset bottom;
        LogicalLine<Cell> current;

        iterator(std::reference_wrapper<Lines<Cell>> _lines,
                 LineOffset _top,
                 LineOffset _next,
                 LineOffset _bottom):
            lines { _lines }, top { _top }, next { _next }, bottom { _bottom }
        {
            Require(_top - 1 <= next);
            Require(next <= _bottom);
            ++*this;
        }

        LogicalLine<Cell> const& operator*() const noexcept { return current; }

        iterator& operator--()
        {
            if (next == bottom + 1)
            {
                current.top = bottom + 1;
                current.bottom = bottom + 1;
                return *this;
            }

            Require(!lines.get()[unbox<int>(next)].wrapped());

            current.top = LineOffset::cast_from(next);
            current.lines.clear();
            do
                current.lines.emplace_back(lines.get()[unbox<int>(next++)]);
            while (next <= bottom && lines.get()[unbox<int>(next)].wrapped());

            current.bottom = LineOffset::cast_from(next - 1);

            return *this;
        }

        iterator& operator++()
        {
            if (next == top - 1)
            {
                current.top = next;
                current.bottom = next;
                return *this;
            }

            auto const bottomMost = next;
            while (lines.get()[unbox<int>(next)].wrapped())
                --next;
            auto const topMost = next;
            --next; // jump to next logical line's bottom line above the current logical one

            current.top = topMost;
            current.bottom = bottomMost;

            current.lines.clear();
            for (auto i = topMost; i <= bottomMost; ++i)
                current.lines.emplace_back(lines.get()[unbox<int>(i)]);

            return *this;
        }

        iterator& operator++(int)
        {
            auto c = *this;
            ++*this;
            return c;
        }
        iterator& operator--(int)
        {
            auto c = *this;
            --*this;
            return c;
        }

        bool operator==(iterator const& other) const noexcept { return current == other.current; }
        bool operator!=(iterator const& other) const noexcept { return current != other.current; }
    }; // }}}

    iterator begin() const { return iterator(lines, topMostLine, bottomMostLine, bottomMostLine); }
    iterator end() const { return iterator(lines, topMostLine, topMostLine - 1, bottomMostLine); }
};

/**
 * Manages the screen grid buffer (main screen + scrollback history).
 *
 *
Future motivations

 *
 *

 *
manages text reflow upon resize
 *
manages underlying disk storage for very old scrollback history lines.
 *

 *
 *
Layout

 *
 *

 *      +0========================-3+   <-- scrollback top
 *      |1                        -2|
 *      |2   Scrollback history   -1|
 *      |3                         0|   <-- scrollback bottom
 *      +4-------------------------1+   <-- main page top
 *      |5                         2|
 *      |6   main page area        3|
 *      |7                         4|   <-- main page bottom
 *      +---------------------------+
 *       ^                          ^
 *       1                          pageSize.columns
 *

 */
template <typename Cell>
class Grid
{
    // TODO: Rename all "History" to "Scrollback"?
  public:
    Grid(PageSize _pageSize, bool _reflowOnResize, LineCount _maxHistoryLineCount);

    Grid(): Grid(PageSize { LineCount(25), ColumnCount(80) }, false, LineCount(0)) {}

    void reset();

    // {{{ grid global properties
    [[nodiscard]] LineCount maxHistoryLineCount() const noexcept { return maxHistoryLineCount_; }
    void setMaxHistoryLineCount(LineCount _maxHistoryLineCount);

    [[nodiscard]] LineCount totalLineCount() const noexcept { return maxHistoryLineCount_ + pageSize_.lines; }

    [[nodiscard]] LineCount historyLineCount() const noexcept
    {
        return std::min(maxHistoryLineCount_, linesUsed_ - pageSize_.lines);
    }

    [[nodiscard]] bool reflowOnResize() const noexcept { return reflowOnResize_; }
    void setReflowOnResize(bool _enabled) { reflowOnResize_ = _enabled; }

    [[nodiscard]] PageSize pageSize() const noexcept { return pageSize_; }

    /// Resizes the main page area of the grid and adapts the scrollback area's width accordingly.
    ///
    /// @param _pageSize          new size of the main page area
    /// @param _currentCursorPos  current cursor position
    /// @param _wrapPending       AutoWrap is on and a wrap is pending
    ///
    /// @returns updated cursor position.
    [[nodiscard]] CellLocation resize(PageSize _pageSize, CellLocation _currentCursorPos, bool _wrapPending);
    // }}}

    // {{{ Line API
    /// @returns reference to Line at given relative offset @p _line.
    Line<Cell>& lineAt(LineOffset _line) noexcept;
    Line<Cell> const& lineAt(LineOffset _line) const noexcept;

    gsl::span<Cell const> lineBuffer(LineOffset _line) const noexcept { return lineAt(_line).cells(); }
    gsl::span<Cell const> lineBufferRightTrimmed(LineOffset _line) const noexcept;

    [[nodiscard]] std::string lineText(LineOffset _line) const;
    [[nodiscard]] std::string lineTextTrimmed(LineOffset _line) const;
    [[nodiscard]] std::string lineText(Line<Cell> const& _line) const;

    void setLineText(LineOffset _line, std::string_view _text);

    // void resetLine(LineOffset _line, GraphicsAttributes _attribs) noexcept
    // { lineAt(_line).reset(_attribs); }

    [[nodiscard]] ColumnCount lineLength(LineOffset _line) const noexcept { return lineAt(_line).size(); }
    [[nodiscard]] bool isLineBlank(LineOffset _line) const noexcept;
    [[nodiscard]] bool isLineWrapped(LineOffset _line) const noexcept;

    [[nodiscard]] int computeLogicalLineNumberFromBottom(LineCount _n) const noexcept;

    [[nodiscard]] size_t zero_index() const noexcept { return lines_.zero_index(); }
    // }}}

    /// Gets a reference to the cell relative to screen origin (top left, 0:0).
    [[nodiscard]] Cell& useCellAt(LineOffset _line, ColumnOffset _column) noexcept;
    [[nodiscard]] Cell& at(LineOffset _line, ColumnOffset _column) noexcept;
    [[nodiscard]] Cell const& at(LineOffset _line, ColumnOffset _column) const noexcept;

    // page view API
    gsl::span<Line<Cell>> pageAtScrollOffset(ScrollOffset _scrollOffset);
    gsl::span<Line<Cell> const> pageAtScrollOffset(ScrollOffset _scrollOffset) const;
    gsl::span<Line<Cell>> mainPage();
    gsl::span<Line<Cell> const> mainPage() const;

    LogicalLines<Cell> logicalLines()
    {
        return LogicalLines<Cell> { boxed_cast<LineOffset>(-historyLineCount()),
                                    boxed_cast<LineOffset>(pageSize_.lines - 1),
                                    lines_ };
    }

    ReverseLogicalLines<Cell> logicalLinesReverse()
    {
        return ReverseLogicalLines<Cell> { boxed_cast<LineOffset>(-historyLineCount()),
                                           boxed_cast<LineOffset>(pageSize_.lines - 1),
                                           lines_ };
    }

    // {{{ buffer manipulation

    /// Completely deletes all scrollback lines.
    void clearHistory();

    /// Scrolls up by @p _n lines within the given margin.
    ///
    /// @param _n number of lines to scroll up within the given margin.
    /// @param _defaultAttributes SGR attributes the newly created grid cells will be initialized with.
    /// @param _margin the margin coordinates to perform the scrolling action into.
    LineCount scrollUp(LineCount _n, GraphicsAttributes _defaultAttributes, Margin _margin) noexcept;

    /// Scrolls up main page by @p _n lines and re-initializes grid cells with @p _defaultAttributes.
    LineCount scrollUp(LineCount _n, GraphicsAttributes _defaultAttributes = {}) noexcept;

    /// Scrolls down by @p _n lines within the given margin.
    ///
    /// @param _n number of lines to scroll down within the given margin.
    /// @param _defaultAttributes SGR attributes the newly created grid cells will be initialized with.
    /// @param _margin the margin coordinates to perform the scrolling action into.
    void scrollDown(LineCount _n, GraphicsAttributes const& _defaultAttributes, Margin const& _margin);

    // Scrolls the data within the margins to the left filling the new space on the right with empty cells.
    void scrollLeft(GraphicsAttributes _defaultAttributes, Margin _margin) noexcept;
    // }}}

    // {{{ Rendering API
    /// Renders the full screen by passing every grid cell to the callback.
    template <typename RendererT>
    void render(RendererT&& _render, ScrollOffset _scrollOffset = {}) const;

    /// Takes text-screenshot of the main page.
    [[nodiscard]] std::string renderMainPageText() const;

    /// Renders the full grid's text characters.
    ///
    /// Empty cells are represented as strings and lines split by LF.
    [[nodiscard]] std::string renderAllText() const;
    // }}}

    [[nodiscard]] constexpr LineFlags defaultLineFlags() const noexcept;

    [[nodiscard]] constexpr LineCount linesUsed() const noexcept;

    void verifyState() const;

  private:
    CellLocation growLines(LineCount _newHeight, CellLocation _cursor);
    void appendNewLines(LineCount _count, GraphicsAttributes _attr);
    void clampHistory();

    // {{{ buffer helpers
    void resizeBuffers(PageSize _newSize)
    {
        auto const newTotalLineCount = historyLineCount() + _newSize.lines;
        lines_.resize(unbox<size_t>(newTotalLineCount));
        pageSize_ = _newSize;
    }

    void rezeroBuffers() noexcept { lines_.rezero(); }

    void rotateBuffers(int offset) noexcept { lines_.rotate(offset); }

    void rotateBuffersLeft(LineCount count) noexcept { lines_.rotate_left(unbox<size_t>(count)); }

    void rotateBuffersRight(LineCount count) noexcept { lines_.rotate_right(unbox<size_t>(count)); }
    // }}}

    // private fields
    //
    PageSize pageSize_;
    bool reflowOnResize_ = false;
    LineCount maxHistoryLineCount_;

    // Number of lines is at least the sum of maxHistoryLineCount_ + pageSize_.lines,
    // because shrinking the page height does not necessarily
    // have to resize the array (as optimization).
    Lines<Cell> lines_;

    // Number of lines used in the Lines buffer.
    LineCount linesUsed_;
};

template <typename Cell>
std::ostream& dumpGrid(std::ostream& os, Grid<Cell> const& grid);

template <typename Cell>
std::string dumpGrid(Grid<Cell> const& grid);

// {{{ impl
template <typename Cell>
constexpr LineFlags Grid<Cell>::defaultLineFlags() const noexcept
{
    return reflowOnResize_ ? LineFlags::Wrappable : LineFlags::None;
}

template <typename Cell>
constexpr LineCount Grid<Cell>::linesUsed() const noexcept
{
    return linesUsed_;
}

template <typename Cell>
bool Grid<Cell>::isLineWrapped(LineOffset _line) const noexcept
{
    return _line >= -boxed_cast<LineOffset>(historyLineCount())
           && boxed_cast<LineCount>(_line) < pageSize_.lines && lineAt(_line).wrapped();
}

template <typename Cell>
template <typename RendererT>
void Grid<Cell>::render(RendererT&& _render, ScrollOffset _scrollOffset) const
{
    assert(!_scrollOffset || unbox<LineCount>(_scrollOffset) <= historyLineCount());

    auto y = LineOffset(0);
    for (int i = -*_scrollOffset, e = i + *pageSize_.lines; i != e; ++i, ++y)
    {
        auto x = ColumnOffset(0);
        Line<Cell> const& line = lines_[i];
        if (line.isTrivialBuffer())
            _render.renderTrivialLine(line.trivialBuffer(), y);
        else
        {
            _render.startLine(y);
            for (Cell const& cell: line.cells())
                _render.renderCell(cell, y, x++);
            _render.endLine();
        }
    }
    _render.finish();
}
// }}}

} // namespace terminal

// {{{ fmt formatter
namespace fmt
{

template <>
struct formatter<terminal::Margin::Horizontal>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(const terminal::Margin::Horizontal range, FormatContext& ctx)
    {
        return fmt::format_to(ctx.out(), "{}..{}", range.from, range.to);
    }
};

template <>
struct formatter<terminal::Margin::Vertical>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(const terminal::Margin::Vertical range, FormatContext& ctx)
    {
        return fmt::format_to(ctx.out(), "{}..{}", range.from, range.to);
    }
};

} // namespace fmt
// }}}


// The various memcached commands that we support.

#include "commands.h"
#include "connections.h"
#include "protocol.h"
#include "server.h"
#include "utils.h"

#include <gsl/gsl_randist.h>

#include <assert.h>
#include <stdio.h>

// process a memcached get(s) command. (we don't support CAS).
void process_get_command(conn *c, token_t *tokens, size_t ntokens,
                                bool return_cas) {
	char *key;
	size_t nkey;
	int i = 0;
	item *it;
	token_t *key_token = &tokens[KEY_TOKEN];
	char *suffix;

	assert(c != NULL);

	// process the whole command line, (only part of it may be tokenized right now)
	do {
		// process all tokenized keys at this stage.
		while(key_token->length != 0) {
			key = key_token->value;
			nkey = key_token->length;

			if(nkey > KEY_MAX_LENGTH) {
				out_string(c, "CLIENT_ERROR bad command line format");
				return;
			}

			// lookup key-value.
			it = item_get(key, nkey);

			// hit.
			if (it) {
				if (i >= c->isize && !conn_expand_items(c)) {
					item_remove(it);
					break;
				}

				// Construct the response. Each hit adds three elements to the
				// outgoing data list:
				//   "VALUE <key> <flags> <data_length>\r\n"
				//   "<data>\r\n"
				// The <data> element is stored on the connection item list, not on
				// the iov list.
				if (!conn_add_iov(c, "VALUE ", 6) != 0 ||
				    !conn_add_iov(c, ITEM_key(it), it->nkey) != 0 ||
				    !conn_add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0) {
					item_remove(it);
					break;
				}

				if (config.verbose > 1) {
					fprintf(stderr, ">%d sending key %s\n", c->sfd, key);
				}

				// add item to remembered list (i.e., we've taken ownership of them
				// through refcounting and later must release them once we've
				// written out the iov associated with them).
				item_update(it);
				*(c->ilist + i) = it;
				i++;
			}

			key_token++;
		}

		/*
		 * If the command string hasn't been fully processed, get the next set
		 * of tokens.
		 */
		if(key_token->value != NULL) {
			ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
			key_token = tokens;
		}

	} while(key_token->value != NULL);

	c->icurr = c->ilist;
	c->ileft = i;

	if (config.verbose > 1) {
		fprintf(stderr, ">%d END\n", c->sfd);
	}

	// If the loop was terminated because of out-of-memory, it is not reliable
	// to add END\r\n to the buffer, because it might not end in \r\n. So we
	// send SERVER_ERROR instead.
	if (key_token->value != NULL || !conn_add_iov(c, "END\r\n", 5) != 0) {
		out_string(c, "SERVER_ERROR out of memory writing get response");
	} else {
		if (config.use_dist) {
			double r = config.dist_arg1 + gsl_ran_gaussian(config.r, config.dist_arg2);
			if (config.verbose > 0) {
				fprintf(stderr, "delay: %f\n", r);
			}
			conn_set_state(c, conn_timeout);
			c->after_timeout = conn_mwrite;
			c->timeout = r;
			c->msgcurr = 0;
		} else {
			conn_set_state(c, conn_mwrite);
		}
	}
}

// process a memcached set command.
void process_update_command(conn *c, token_t *tokens,
                            const size_t ntokens,
                            int comm, bool handle_cas) {
	int vlen;
	assert(c != NULL);

	if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH ||
	    !safe_strtol(tokens[4].value, (int32_t *)&vlen)) {
		out_string(c, "CLIENT_ERROR bad command line format");
		return;
	}

	if (vlen < 0) {
		out_string(c, "CLIENT_ERROR bad command line format");
		return;
	}

	// setup value to be read
	c->sbytes = vlen + 2; // for \r\n consumption.
	conn_set_state(c, conn_read_value);
}


#include <bindings.cmacros.h>
#include <gsl/gsl_monte_plain.h>
#include <gsl/gsl_monte_miser.h>
#include <gsl/gsl_monte_vegas.h>

BC_INLINE2(GSL_MONTE_FN_EVAL,gsl_monte_function*,double*,double)


#ifndef TRANS_UTIL
#define TRANS_UTIL

#define prefetch(x) __builtin_prefetch(x)
#define prefetch_for_r(x) __builtin_prefetch(x, 0, 3)
#define prefetch_for_w(x) __builtin_prefetch(x, 1, 3)

#include <stdarg.h>
#include <gsl/gsl_vector.h>
#include "../include/type.h"

int setulb_(integer *, integer *, doublereal *,
            doublereal *, doublereal *, integer *, doublereal *, doublereal *,
            doublereal *, doublereal *, doublereal *, integer *, char *,
            integer *, char *, logical *, integer *, doublereal *, integer *);

double cuscumsum(gsl_vector *vec, double (*fun) (double, va_list), int argc, ...);

int parse_title(char* str, char** output);

int str_to_vector(char* str, gsl_vector** vec);

int parse_line(char* str, char** id, size_t* id_n, gsl_vector** inc1,
               gsl_vector** skp1, gsl_vector** inc2, gsl_vector** skp2,
               int* inclu_len, int* skip_len);

int parse_file(const char* filename, diff_list_node* list, char** title);

double logit(double i);

double cumprod(const gsl_vector* vec);

double sum_for_multivar(const double i, va_list argv);

double sum_for_multivar_der(const double i, va_list argv);

double myfunc_marginal_2der(const double x, const double I, const double S,
                            const double beta, const double var,
                            const int inclu_len, const int skip_len);

double sum_for_marginal(const double i, va_list argv);

double sum_for_marginal_der(const double i, va_list argv);

odiff* diff_alloc(gsl_vector* inc1, gsl_vector* inc2,
                  gsl_vector* skp1, gsl_vector* skp2,
                  int inclu_len, int skip_len, int flag, char* id);

int diff_append(diff_list_node* header, odiff* data);

void mp_threadpool(int nthread, int ntask, void* (*func)(void *), void** datum, void **ret);

double l_bfgs_b_wrapper(integer n, integer m, doublereal x[], doublereal l[],
                        doublereal u[], integer nbd[],
                        double (*fp) (const double x[], va_list argv),
                        void (*gp) (const double x[], double res[], va_list argv),
                        doublereal factr, doublereal pgtol, integer iprint,
                        int maxfun, int maxiter, int argc, ...);

#endif


/**
 * Created: Wed Aug 31 20:27:45 CST 2016
 * @author: Lie Yan
 * @email: robin.lie.yan@outlook.com
 *
 */

#pragma once

#include <gsl.h>

namespace formulae {
namespace interm {

enum class ItemTag : uint8_t { INoad, IPen, ISpace, IStyle };

struct Item {
  virtual ItemTag tag() const = 0;
  virtual gsl::owner<Item *> clone() const = 0;
};

class ItemRef {
public:
  ItemRef(gsl::owner<Item *> itemPtr) : _itemPtr(itemPtr) {}
  ItemRef(const ItemRef &rhs) { _itemPtr.reset(rhs._itemPtr->clone()); }
  ItemRef &operator=(const ItemRef &rhs) {
    _itemPtr.reset(rhs._itemPtr->clone());
    return *this;
  }
  ItemRef(ItemRef &&rhs) = default;
  ItemRef &operator=(ItemRef &&rhs) = default;
  virtual ~ItemRef() = default;

  /* -------------------------------------------------------- */
  ItemTag tag() const { return _itemPtr->tag(); }

  template<typename T>
  T& as() const { return *dynamic_cast<T*>(_itemPtr.get()); }

private:
  std::unique_ptr<Item> _itemPtr;
};

using ilist_t = list_t<ItemRef>;

}
}


/* vector/gsl_vector_double.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_DOUBLE_H__
#define __GSL_VECTOR_DOUBLE_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_double.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  double *data;
  gsl_block *block;
  int owner;
}
gsl_vector;

typedef struct
{
  gsl_vector vector;
} _gsl_vector_view;

typedef _gsl_vector_view gsl_vector_view;

typedef struct
{
  gsl_vector vector;
} _gsl_vector_const_view;

typedef const _gsl_vector_const_view gsl_vector_const_view;

/* Allocation */

GSL_EXPORT gsl_vector *gsl_vector_alloc (const size_t n);
GSL_EXPORT gsl_vector *gsl_vector_calloc (const size_t n);

GSL_EXPORT gsl_vector *gsl_vector_alloc_from_block (gsl_block * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_EXPORT gsl_vector *gsl_vector_alloc_from_vector (gsl_vector * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_EXPORT void gsl_vector_free (gsl_vector * v);

/* Views */

GSL_EXPORT
_gsl_vector_view
gsl_vector_view_array (double *v, size_t n);

GSL_EXPORT
_gsl_vector_view
gsl_vector_view_array_with_stride (double *base,
                                         size_t stride,
                                         size_t n);

GSL_EXPORT
_gsl_vector_const_view
gsl_vector_const_view_array (const double *v, size_t n);

GSL_EXPORT
_gsl_vector_const_view
gsl_vector_const_view_array_with_stride (const double *base,
                                               size_t stride,
                                               size_t n);

GSL_EXPORT
_gsl_vector_view
gsl_vector_subvector (gsl_vector *v,
                            size_t i,
                            size_t n);

GSL_EXPORT
_gsl_vector_view
gsl_vector_subvector_with_stride (gsl_vector *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_EXPORT
_gsl_vector_const_view
gsl_vector_const_subvector (const gsl_vector *v,
                                  size_t i,
                                  size_t n);

GSL_EXPORT
_gsl_vector_const_view
gsl_vector_const_subvector_with_stride (const gsl_vector *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_EXPORT double gsl_vector_get (const gsl_vector * v, const size_t i);
GSL_EXPORT void gsl_vector_set (gsl_vector * v, const size_t i, double x);

GSL_EXPORT double *gsl_vector_ptr (gsl_vector * v, const size_t i);
GSL_EXPORT const double *gsl_vector_const_ptr (const gsl_vector * v, const size_t i);

GSL_EXPORT void gsl_vector_set_zero (gsl_vector * v);
GSL_EXPORT void gsl_vector_set_all (gsl_vector * v, double x);
GSL_EXPORT int gsl_vector_set_basis (gsl_vector * v, size_t i);

GSL_EXPORT int gsl_vector_fread (FILE * stream, gsl_vector * v);
GSL_EXPORT int gsl_vector_fwrite (FILE * stream, const gsl_vector * v);
GSL_EXPORT int gsl_vector_fscanf (FILE * stream, gsl_vector * v);
GSL_EXPORT int gsl_vector_fprintf (FILE * stream, const gsl_vector * v,
                                   const char *format);

GSL_EXPORT int gsl_vector_memcpy (gsl_vector * dest, const gsl_vector * src);

GSL_EXPORT int gsl_vector_reverse (gsl_vector * v);

GSL_EXPORT int gsl_vector_swap (gsl_vector * v, gsl_vector * w);
GSL_EXPORT int gsl_vector_swap_elements (gsl_vector * v, const size_t i, const size_t j);

GSL_EXPORT double gsl_vector_max (const gsl_vector * v);
GSL_EXPORT double gsl_vector_min (const gsl_vector * v);
GSL_EXPORT void gsl_vector_minmax (const gsl_vector * v, double * min_out, double * max_out);

GSL_EXPORT size_t gsl_vector_max_index (const gsl_vector * v);
GSL_EXPORT size_t gsl_vector_min_index (const gsl_vector * v);
GSL_EXPORT void gsl_vector_minmax_index (const gsl_vector * v, size_t * imin, size_t * imax);

GSL_EXPORT int gsl_vector_add (gsl_vector * a, const gsl_vector * b);
GSL_EXPORT int gsl_vector_sub (gsl_vector * a, const gsl_vector * b);
GSL_EXPORT int gsl_vector_mul (gsl_vector * a, const gsl_vector * b);
GSL_EXPORT int gsl_vector_div (gsl_vector * a, const gsl_vector * b);
GSL_EXPORT int gsl_vector_scale (gsl_vector * a, const double x);
GSL_EXPORT int gsl_vector_add_constant (gsl_vector * a, const double x);

GSL_EXPORT int gsl_vector_isnull (const gsl_vector * v);

#ifdef HAVE_INLINE

extern inline
double
gsl_vector_get (const gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_set (gsl_vector * v, const size_t i, double x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
double *
gsl_vector_ptr (gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (double *) (v->data + i * v->stride);
}

extern inline
const double *
gsl_vector_const_ptr (const gsl_vector * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const double *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_DOUBLE_H__ */


/* vector/gsl_vector_long.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_LONG_H__
#define __GSL_VECTOR_LONG_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_long.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  long *data;
  gsl_block_long *block;
  int owner;
}
gsl_vector_long;

typedef struct
{
  gsl_vector_long vector;
} _gsl_vector_long_view;

typedef _gsl_vector_long_view gsl_vector_long_view;

typedef struct
{
  gsl_vector_long vector;
} _gsl_vector_long_const_view;

typedef const _gsl_vector_long_const_view gsl_vector_long_const_view;

/* Allocation */

GSL_EXPORT gsl_vector_long *gsl_vector_long_alloc (const size_t n);
GSL_EXPORT gsl_vector_long *gsl_vector_long_calloc (const size_t n);

GSL_EXPORT gsl_vector_long *gsl_vector_long_alloc_from_block (gsl_block_long * b,
                                                                const size_t offset,
                                                                const size_t n,
                                                                const size_t stride);

GSL_EXPORT gsl_vector_long *gsl_vector_long_alloc_from_vector (gsl_vector_long * v,
                                                                 const size_t offset,
                                                                 const size_t n,
                                                                 const size_t stride);

GSL_EXPORT void gsl_vector_long_free (gsl_vector_long * v);

/* Views */

GSL_EXPORT
_gsl_vector_long_view
gsl_vector_long_view_array (long *v, size_t n);

GSL_EXPORT
_gsl_vector_long_view
gsl_vector_long_view_array_with_stride (long *base,
                                         size_t stride,
                                         size_t n);

GSL_EXPORT
_gsl_vector_long_const_view
gsl_vector_long_const_view_array (const long *v, size_t n);

GSL_EXPORT
_gsl_vector_long_const_view
gsl_vector_long_const_view_array_with_stride (const long *base,
                                               size_t stride,
                                               size_t n);

GSL_EXPORT
_gsl_vector_long_view
gsl_vector_long_subvector (gsl_vector_long *v,
                            size_t i,
                            size_t n);

GSL_EXPORT
_gsl_vector_long_view
gsl_vector_long_subvector_with_stride (gsl_vector_long *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_EXPORT
_gsl_vector_long_const_view
gsl_vector_long_const_subvector (const gsl_vector_long *v,
                                  size_t i,
                                  size_t n);

GSL_EXPORT
_gsl_vector_long_const_view
gsl_vector_long_const_subvector_with_stride (const gsl_vector_long *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_EXPORT long gsl_vector_long_get (const gsl_vector_long * v, const size_t i);
GSL_EXPORT void gsl_vector_long_set (gsl_vector_long * v, const size_t i, long x);

GSL_EXPORT long *gsl_vector_long_ptr (gsl_vector_long * v, const size_t i);
GSL_EXPORT const long *gsl_vector_long_const_ptr (const gsl_vector_long * v, const size_t i);

GSL_EXPORT void gsl_vector_long_set_zero (gsl_vector_long * v);
GSL_EXPORT void gsl_vector_long_set_all (gsl_vector_long * v, long x);
GSL_EXPORT int gsl_vector_long_set_basis (gsl_vector_long * v, size_t i);

GSL_EXPORT int gsl_vector_long_fread (FILE * stream, gsl_vector_long * v);
GSL_EXPORT int gsl_vector_long_fwrite (FILE * stream, const gsl_vector_long * v);
GSL_EXPORT int gsl_vector_long_fscanf (FILE * stream, gsl_vector_long * v);
GSL_EXPORT int gsl_vector_long_fprintf (FILE * stream, const gsl_vector_long * v,
                                         const char *format);

GSL_EXPORT int gsl_vector_long_memcpy (gsl_vector_long * dest, const gsl_vector_long * src);

GSL_EXPORT int gsl_vector_long_reverse (gsl_vector_long * v);

GSL_EXPORT int gsl_vector_long_swap (gsl_vector_long * v, gsl_vector_long * w);
GSL_EXPORT int gsl_vector_long_swap_elements (gsl_vector_long * v, const size_t i, const size_t j);

GSL_EXPORT long gsl_vector_long_max (const gsl_vector_long * v);
GSL_EXPORT long gsl_vector_long_min (const gsl_vector_long * v);
GSL_EXPORT void gsl_vector_long_minmax (const gsl_vector_long * v, long * min_out, long * max_out);

GSL_EXPORT size_t gsl_vector_long_max_index (const gsl_vector_long * v);
GSL_EXPORT size_t gsl_vector_long_min_index (const gsl_vector_long * v);
GSL_EXPORT void gsl_vector_long_minmax_index (const gsl_vector_long * v, size_t * imin, size_t * imax);

GSL_EXPORT int gsl_vector_long_add (gsl_vector_long * a, const gsl_vector_long * b);
GSL_EXPORT int gsl_vector_long_sub (gsl_vector_long * a, const gsl_vector_long * b);
GSL_EXPORT int gsl_vector_long_mul (gsl_vector_long * a, const gsl_vector_long * b);
GSL_EXPORT int gsl_vector_long_div (gsl_vector_long * a, const gsl_vector_long * b);
GSL_EXPORT int gsl_vector_long_scale (gsl_vector_long * a, const double x);
GSL_EXPORT int gsl_vector_long_add_constant (gsl_vector_long * a, const double x);

GSL_EXPORT int gsl_vector_long_isnull (const gsl_vector_long * v);

#ifdef HAVE_INLINE

extern inline
long
gsl_vector_long_get (const gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_long_set (gsl_vector_long * v, const size_t i, long x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
long *
gsl_vector_long_ptr (gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (long *) (v->data + i * v->stride);
}

extern inline
const long *
gsl_vector_long_const_ptr (const gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const long *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_LONG_H__ */


/* vector/gsl_vector_complex_float.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_COMPLEX_FLOAT_H__
#define __GSL_VECTOR_COMPLEX_FLOAT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_float.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_float.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  float *data;
  gsl_block_complex_float *block;
  int owner;
} gsl_vector_complex_float;

typedef struct
{
  gsl_vector_complex_float vector;
} _gsl_vector_complex_float_view;

typedef _gsl_vector_complex_float_view gsl_vector_complex_float_view;

typedef struct
{
  gsl_vector_complex_float vector;
} _gsl_vector_complex_float_const_view;

typedef const _gsl_vector_complex_float_const_view gsl_vector_complex_float_const_view;

/* Allocation */

gsl_vector_complex_float *gsl_vector_complex_float_alloc (const size_t n);
gsl_vector_complex_float *gsl_vector_complex_float_calloc (const size_t n);

gsl_vector_complex_float *
gsl_vector_complex_float_alloc_from_block (gsl_block_complex_float * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

gsl_vector_complex_float *
gsl_vector_complex_float_alloc_from_vector (gsl_vector_complex_float * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

void gsl_vector_complex_float_free (gsl_vector_complex_float * v);

/* Views */

_gsl_vector_complex_float_view
gsl_vector_complex_float_view_array (float *base,
                                     size_t n);

_gsl_vector_complex_float_view
gsl_vector_complex_float_view_array_with_stride (float *base,
                                                 size_t stride,
                                                 size_t n);

_gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_view_array (const float *base,
                                           size_t n);

_gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_view_array_with_stride (const float *base,
                                                       size_t stride,
                                                       size_t n);

_gsl_vector_complex_float_view
gsl_vector_complex_float_subvector (gsl_vector_complex_float *base,
                                         size_t i,
                                         size_t n);

_gsl_vector_complex_float_view
gsl_vector_complex_float_subvector_with_stride (gsl_vector_complex_float *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

_gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_subvector (const gsl_vector_complex_float *base,
                                               size_t i,
                                               size_t n);

_gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_subvector_with_stride (const gsl_vector_complex_float *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

_gsl_vector_float_view
gsl_vector_complex_float_real (gsl_vector_complex_float *v);

_gsl_vector_float_view
gsl_vector_complex_float_imag (gsl_vector_complex_float *v);

_gsl_vector_float_const_view
gsl_vector_complex_float_const_real (const gsl_vector_complex_float *v);

_gsl_vector_float_const_view
gsl_vector_complex_float_const_imag (const gsl_vector_complex_float *v);

/* Operations */

gsl_complex_float
gsl_vector_complex_float_get (const gsl_vector_complex_float * v, const size_t i);

void gsl_vector_complex_float_set (gsl_vector_complex_float * v, const size_t i,
                                   gsl_complex_float z);

gsl_complex_float
*gsl_vector_complex_float_ptr (gsl_vector_complex_float * v, const size_t i);

const gsl_complex_float
*gsl_vector_complex_float_const_ptr (const gsl_vector_complex_float * v, const size_t i);

void gsl_vector_complex_float_set_zero (gsl_vector_complex_float * v);
void gsl_vector_complex_float_set_all (gsl_vector_complex_float * v,
                                       gsl_complex_float z);
int gsl_vector_complex_float_set_basis (gsl_vector_complex_float * v, size_t i);

int gsl_vector_complex_float_fread (FILE * stream,
                                    gsl_vector_complex_float * v);
int gsl_vector_complex_float_fwrite (FILE * stream,
                                     const gsl_vector_complex_float * v);
int gsl_vector_complex_float_fscanf (FILE * stream,
                                     gsl_vector_complex_float * v);
int gsl_vector_complex_float_fprintf (FILE * stream,
                                      const gsl_vector_complex_float * v,
                                      const char *format);

int gsl_vector_complex_float_memcpy (gsl_vector_complex_float * dest, const gsl_vector_complex_float * src);

int gsl_vector_complex_float_reverse (gsl_vector_complex_float * v);

int gsl_vector_complex_float_swap (gsl_vector_complex_float * v, gsl_vector_complex_float * w);
int gsl_vector_complex_float_swap_elements (gsl_vector_complex_float * v, const size_t i, const size_t j);

int gsl_vector_complex_float_isnull (const gsl_vector_complex_float * v);
int gsl_vector_complex_float_ispos (const gsl_vector_complex_float * v);
int gsl_vector_complex_float_isneg (const gsl_vector_complex_float * v);

#ifdef HAVE_INLINE

extern inline
gsl_complex_float
gsl_vector_complex_float_get (const gsl_vector_complex_float * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      gsl_complex_float zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_FLOAT_AT (v, i);
}

extern inline
void
gsl_vector_complex_float_set (gsl_vector_complex_float * v,
                              const size_t i, gsl_complex_float z)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_FLOAT_AT (v, i) = z;
}

extern inline
gsl_complex_float *
gsl_vector_complex_float_ptr (gsl_vector_complex_float * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_FLOAT_AT (v, i);
}

extern inline
const gsl_complex_float *
gsl_vector_complex_float_const_ptr (const gsl_vector_complex_float * v,
                                    const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_FLOAT_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_FLOAT_H__ */