/* matrix/gsl_matrix_short.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_SHORT_H__
#define __GSL_MATRIX_SHORT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_short.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;
  short * data;
  gsl_block_short * block;
  int owner;
} gsl_matrix_short;

typedef struct
{
  gsl_matrix_short matrix;
} _gsl_matrix_short_view;

typedef _gsl_matrix_short_view gsl_matrix_short_view;

typedef struct
{
  gsl_matrix_short matrix;
} _gsl_matrix_short_const_view;

typedef const _gsl_matrix_short_const_view gsl_matrix_short_const_view;

/* Allocation */

GSL_EXPORT
gsl_matrix_short *
gsl_matrix_short_alloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_short *
gsl_matrix_short_calloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_short *
gsl_matrix_short_alloc_from_block (gsl_block_short * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_EXPORT
gsl_matrix_short *
gsl_matrix_short_alloc_from_matrix (gsl_matrix_short * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
gsl_vector_short *
gsl_vector_short_alloc_row_from_matrix (gsl_matrix_short * m,
                                        const size_t i);

GSL_EXPORT
gsl_vector_short *
gsl_vector_short_alloc_col_from_matrix (gsl_matrix_short * m,
                                        const size_t j);

GSL_EXPORT void gsl_matrix_short_free (gsl_matrix_short * m);

/* Views */

GSL_EXPORT
_gsl_matrix_short_view
gsl_matrix_short_submatrix (gsl_matrix_short * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_short_view
gsl_matrix_short_row (gsl_matrix_short * m, const size_t i);

GSL_EXPORT
_gsl_vector_short_view
gsl_matrix_short_column (gsl_matrix_short * m, const size_t j);

GSL_EXPORT
_gsl_vector_short_view
gsl_matrix_short_diagonal (gsl_matrix_short * m);

GSL_EXPORT
_gsl_vector_short_view
gsl_matrix_short_subdiagonal (gsl_matrix_short * m, const size_t k);

GSL_EXPORT
_gsl_vector_short_view
gsl_matrix_short_superdiagonal (gsl_matrix_short * m, const size_t k);

GSL_EXPORT
_gsl_matrix_short_view
gsl_matrix_short_view_array (short * base,
                             const size_t n1,
                             const size_t n2);

GSL_EXPORT
_gsl_matrix_short_view
gsl_matrix_short_view_array_with_tda (short * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_EXPORT
_gsl_matrix_short_view
gsl_matrix_short_view_vector (gsl_vector_short * v,
                              const size_t n1,
                              const size_t n2);

GSL_EXPORT
_gsl_matrix_short_view
gsl_matrix_short_view_vector_with_tda (gsl_vector_short * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_EXPORT
_gsl_matrix_short_const_view
gsl_matrix_short_const_submatrix (const gsl_matrix_short * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_short_const_view
gsl_matrix_short_const_row (const gsl_matrix_short * m,
                            const size_t i);

GSL_EXPORT
_gsl_vector_short_const_view
gsl_matrix_short_const_column (const gsl_matrix_short * m,
                               const size_t j);

GSL_EXPORT
_gsl_vector_short_const_view
gsl_matrix_short_const_diagonal (const gsl_matrix_short * m);

GSL_EXPORT
_gsl_vector_short_const_view
gsl_matrix_short_const_subdiagonal (const gsl_matrix_short * m,
                                    const size_t k);

GSL_EXPORT
_gsl_vector_short_const_view
gsl_matrix_short_const_superdiagonal (const gsl_matrix_short * m,
                                      const size_t k);

GSL_EXPORT
_gsl_matrix_short_const_view
gsl_matrix_short_const_view_array (const short * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_EXPORT
_gsl_matrix_short_const_view
gsl_matrix_short_const_view_array_with_tda (const short * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_EXPORT
_gsl_matrix_short_const_view
gsl_matrix_short_const_view_vector (const gsl_vector_short * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
_gsl_matrix_short_const_view
gsl_matrix_short_const_view_vector_with_tda (const gsl_vector_short * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_EXPORT short   gsl_matrix_short_get(const gsl_matrix_short * m, const size_t i, const size_t j);
GSL_EXPORT void    gsl_matrix_short_set(gsl_matrix_short * m, const size_t i, const size_t j, const short x);

GSL_EXPORT short * gsl_matrix_short_ptr(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_EXPORT const short * gsl_matrix_short_const_ptr(const gsl_matrix_short * m, const size_t i, const size_t j);

GSL_EXPORT void gsl_matrix_short_set_zero (gsl_matrix_short * m);
GSL_EXPORT void gsl_matrix_short_set_identity (gsl_matrix_short * m);
GSL_EXPORT void gsl_matrix_short_set_all (gsl_matrix_short * m, short x);

GSL_EXPORT int gsl_matrix_short_fread (FILE * stream, gsl_matrix_short * m) ;
GSL_EXPORT int gsl_matrix_short_fwrite (FILE * stream, const gsl_matrix_short * m) ;
GSL_EXPORT int gsl_matrix_short_fscanf (FILE * stream, gsl_matrix_short * m);
GSL_EXPORT int gsl_matrix_short_fprintf (FILE * stream, const gsl_matrix_short * m, const char * format);

GSL_EXPORT int gsl_matrix_short_memcpy(gsl_matrix_short * dest, const gsl_matrix_short * src);
GSL_EXPORT int gsl_matrix_short_swap(gsl_matrix_short * m1, gsl_matrix_short * m2);

GSL_EXPORT int gsl_matrix_short_swap_rows(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_short_swap_columns(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_short_swap_rowcol(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_short_transpose (gsl_matrix_short * m);
GSL_EXPORT int gsl_matrix_short_transpose_memcpy (gsl_matrix_short * dest, const gsl_matrix_short * src);

GSL_EXPORT short gsl_matrix_short_max (const gsl_matrix_short * m);
GSL_EXPORT short gsl_matrix_short_min (const gsl_matrix_short * m);
GSL_EXPORT void gsl_matrix_short_minmax (const gsl_matrix_short * m, short * min_out, short * max_out);

GSL_EXPORT void gsl_matrix_short_max_index (const gsl_matrix_short * m, size_t * imax, size_t *jmax);
GSL_EXPORT void gsl_matrix_short_min_index (const gsl_matrix_short * m, size_t * imin, size_t *jmin);
GSL_EXPORT void gsl_matrix_short_minmax_index (const gsl_matrix_short * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_EXPORT int gsl_matrix_short_isnull (const gsl_matrix_short * m);

GSL_EXPORT int gsl_matrix_short_add (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_EXPORT int gsl_matrix_short_sub (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_EXPORT int gsl_matrix_short_mul_elements (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_EXPORT int gsl_matrix_short_div_elements (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_EXPORT int gsl_matrix_short_scale (gsl_matrix_short * a, const double x);
GSL_EXPORT int gsl_matrix_short_add_constant (gsl_matrix_short * a, const double x);
GSL_EXPORT int gsl_matrix_short_add_diagonal (gsl_matrix_short * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_EXPORT int gsl_matrix_short_get_row(gsl_vector_short * v, const gsl_matrix_short * m, const size_t i);
GSL_EXPORT int gsl_matrix_short_get_col(gsl_vector_short * v, const gsl_matrix_short * m, const size_t j);
GSL_EXPORT int gsl_matrix_short_set_row(gsl_matrix_short * m, const size_t i, const gsl_vector_short * v);
GSL_EXPORT int gsl_matrix_short_set_col(gsl_matrix_short * m, const size_t j, const gsl_vector_short * v);

/* inline functions if you are using GCC */

#ifdef HAVE_INLINE
extern inline
short
gsl_matrix_short_get(const gsl_matrix_short * 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_short_set(gsl_matrix_short * m, const size_t i, const size_t j, const 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
short *
gsl_matrix_short_ptr(gsl_matrix_short * 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 (short *) (m->data + (i * m->tda + j)) ;
}

extern inline
const short *
gsl_matrix_short_const_ptr(const gsl_matrix_short * 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 short *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_SHORT_H__ */


#ifndef __GSL_VECTOR_H__
#define __GSL_VECTOR_H__

#include <gsl/gsl_vector_complex_long_double.h>
#include <gsl/gsl_vector_complex_double.h>
#include <gsl/gsl_vector_complex_float.h>

#include <gsl/gsl_vector_long_double.h>
#include <gsl/gsl_vector_double.h>
#include <gsl/gsl_vector_float.h>

#include <gsl/gsl_vector_ulong.h>
#include <gsl/gsl_vector_long.h>

#include <gsl/gsl_vector_uint.h>
#include <gsl/gsl_vector_int.h>

#include <gsl/gsl_vector_ushort.h>
#include <gsl/gsl_vector_short.h>

#include <gsl/gsl_vector_uchar.h>
#include <gsl/gsl_vector_char.h>

#endif /* __GSL_VECTOR_H__ */


#pragma once

#include <cstddef> // std::byte
#include <map>
#include <memory> // std::align
#include <memory_resource>
#include <vector>

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

#include <cuda/runtime_api.hpp>

namespace thrustshift {

namespace pmr {

//! Unified CUDA Memory Resource. Buffers cannet be deallocated partially. Alignment is ignored.
class managed_resource_type : public std::pmr::memory_resource {
	void* do_allocate(std::size_t bytes,
	                  [[maybe_unused]] std::size_t alignment) override {
		auto region = cuda::memory::managed::detail::allocate(bytes);
		return region.get();
	}

	void do_deallocate(void* p,
	                   [[maybe_unused]] std::size_t bytes,
	                   [[maybe_unused]] std::size_t alignment) override {
		cuda::memory::managed::detail::free(p);
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}
};

//! Device CUDA Memory Resource. Buffers cannot be deallocated partially. Alignment is ignored.
class device_resource_type : public std::pmr::memory_resource {
	void* do_allocate(std::size_t bytes,
	                  [[maybe_unused]] std::size_t alignment) override {
		auto region = cuda::memory::device::detail::allocate(bytes);
		return region.get();
	}

	void do_deallocate(void* p,
	                   [[maybe_unused]] std::size_t bytes,
	                   [[maybe_unused]] std::size_t alignment) override {
		cuda::memory::device::free(p);
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}
};

class host_resource_type : public std::pmr::memory_resource {
	void* do_allocate(std::size_t bytes,
	                  [[maybe_unused]] std::size_t alignment) override {
		return malloc(bytes);
	}

	void do_deallocate(void* p,
	                   [[maybe_unused]] std::size_t bytes,
	                   [[maybe_unused]] std::size_t alignment) override {
		free(p);
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}
};

namespace detail {

struct page_id_type {
	size_t bytes;
	size_t alignment;
};

inline bool operator<(const page_id_type& a, const page_id_type& b) {
	if (a.bytes == b.bytes) {
		return a.alignment < b.alignment;
	}
	return a.bytes < b.bytes;
}

} // namespace detail

/*! \brief Allocates only if requested buffer size is currently not in the free pool.
 *
 *  This pool is useful for functions, which require temporary GPU memory. The
 *  host can allocate memory via this pool, launch the kernel with the corresponding
 *  pointer and exit the function without deallocating the memory because the
 *  host is not aware about the runtime of the GPU kernel and when the kernel
 *  may read the temporary required memory. This pool should not be used if the
 *  byte size of the allocations differ often becaues every time an allocation
 *  is called it actually results into a new allocation if not the exact same
 *  size was allocated previously.
 *
 *  This class is useful if functions delegate the memory resource to other functions, which
 *  are maybe called iteratively and if the function requires different buffers of
 *  the same size.
 *
 *  \note std::pmr compatible
 */
template <class Upstream>
class delayed_pool_type : public std::pmr::memory_resource {
   private:
	struct page_item_type {
		void* ptr;
		bool allocated;
	};

   public:
	delayed_pool_type() = default;

	~delayed_pool_type() noexcept {
		for (auto& [page_id, book_page] : book_) {
			for (auto& page_item : book_page) {
				res_.deallocate(
				    page_item.ptr, page_id.bytes, page_id.alignment);
			}
		}
	}

	auto& get_book() const {
		return book_;
	}

   private:
	void* do_allocate(size_t bytes, size_t alignment) override {
		if (bytes == 0) {
			return nullptr;
		}
		const detail::page_id_type page_id({bytes, alignment});
		if (auto it = book_.find(page_id); it != book_.end()) {
			for (auto& page_item : it->second) {
				if (!page_item.allocated) {
					page_item.allocated = true;
					return page_item.ptr;
				}
			}
			// no page item was free
			void* ptr = res_.allocate(bytes, alignment);
			it->second.push_back({ptr, true});
			return ptr;
		}
		// the required size was never allocated before
		void* ptr = res_.allocate(bytes, alignment);
		book_[page_id] = {{ptr, true}};
		return ptr;
	}

	void do_deallocate(void* ptr,
	                   size_t bytes,
	                   size_t alignment) noexcept override {
		const detail::page_id_type page_id({bytes, alignment});
		if (ptr == nullptr || bytes == 0) {
			return;
		}
		for (auto& page_item : book_[page_id]) {
			if (page_item.ptr == ptr && page_item.allocated) {
				page_item.allocated = false;
				return;
			}
		}
		// The pointer which should be deallocated was never allocated before
		std::terminate();
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}

	Upstream res_;
	// size in bytes -> (ptr, alignment)
	std::map<detail::page_id_type, std::vector<page_item_type>> book_;
};

/*! \brief Allocates only if there is no buffer in the free pool which is of the required size or larger.
 *
 *  This pool searches for the smallest buffer in the pool which has the required alignment and is
 *  of equal or larger byte size than the required buffer and returns that buffer if do_allocate is called.
 *  Morevover, this pool has the same 'delayed' properties than the `delayed_pool_type`.
 *
 *  \sa delayed_pool_type
 *  \note std::pmr compatible
 */
template <class Upstream>
class delayed_fragmenting_pool_type : public std::pmr::memory_resource {
   private:
	struct page_item_type {
		void* ptr;
		bool allocated;
	};

   public:
	delayed_fragmenting_pool_type() = default;

	~delayed_fragmenting_pool_type() noexcept {
		for (auto& [page_id, book_page] : book_) {
			for (auto& page_item : book_page) {
				res_.deallocate(
				    page_item.ptr, page_id.bytes, page_id.alignment);
			}
		}
	}

	auto& get_book() const {
		return book_;
	}

   private:
	void* do_allocate(size_t bytes, size_t alignment) override {
		if (bytes == 0) {
			return nullptr;
		}
		const detail::page_id_type page_id({bytes, alignment});
		for (auto& [k, v] : book_) {
			if (k.alignment == alignment && k.bytes >= bytes) {
				for (auto& p : v) {
					if (!p.allocated) {
						p.allocated = true;
						return p.ptr;
					}
				}
			}
		}
		// no page item was free
		if (auto it = book_.find(page_id); it != book_.end()) {
			void* ptr = res_.allocate(bytes, alignment);
			it->second.push_back({ptr, true});
			return ptr;
		}
		// the required size was never allocated before
		void* ptr = res_.allocate(bytes, alignment);
		book_[page_id] = {{ptr, true}};
		return ptr;
	}

	void do_deallocate(void* ptr,
	                   size_t bytes,
	                   size_t alignment) noexcept override {
		const detail::page_id_type page_id({bytes, alignment});

		if (ptr == nullptr || bytes == 0) {
			return;
		}

		for (auto& [k, v] : book_) {
			if (k.alignment == alignment && k.bytes >= bytes) {
				for (auto& p : v) {
					// no comparison based on the bytes because the buffer can be larger
					// than the amount of requested bytes.
					if (p.ptr == ptr && p.allocated) {
						p.allocated = false;
						return;
					}
				}
			}
		}

		// The pointer which should be deallocated was never allocated before
		std::terminate();
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}

	Upstream res_;
	// size in bytes -> (ptr, alignment)
	std::map<detail::page_id_type, std::vector<page_item_type>> book_;
};

/*! \brief Wraps a memory resource around an existing buffer of fixed size.
 *
 *  This class takes a pointer and a size of an existing buffer and wraps a memory
 *  resource around that buffer. The resource can provide parts of that buffer by
 *  calling `do_allocate` and simply returns subsequently aligned parts of the buffer.
 *  If the buffer size is exceeded `gsl_FailFast()` is called.
 *
 *  \note std::pmr compatible
 */
class wrapping_resource_type : public std::pmr::memory_resource {

   public:
	wrapping_resource_type(void* ptr, size_t size_in_bytes)
	    : size_in_bytes_(size_in_bytes), ptr_(ptr) {
		static_assert(sizeof(std::byte) == 1);
	}

   private:
	void* do_allocate(size_t bytes, size_t alignment) noexcept override {
		if (bytes == 0) {
			return nullptr;
		}
		void* ptr = std::align(alignment, bytes, ptr_, size_in_bytes_);
		gsl_Expects(ptr); // not nullptr
		gsl_Expects(
		    ptr ==
		    ptr_); // this is how I understand the function documentation of std::align
		gsl_Expects(size_in_bytes_ >= bytes);
		size_in_bytes_ -= bytes;
		ptr_ = reinterpret_cast<std::byte*>(ptr_) + bytes;
		return ptr;
	}

	void do_deallocate([[maybe_unused]] void* ptr,
	                   [[maybe_unused]] size_t bytes,
	                   [[maybe_unused]] size_t alignment) noexcept override {
	}

	bool do_is_equal(
	    const std::pmr::memory_resource& other) const noexcept override {
		return this == &other;
	}

	size_t size_in_bytes_;
	void* ptr_;
};

static managed_resource_type default_resource;

} // namespace pmr

} // namespace thrustshift


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN

#include <array>
#include <iomanip>
#include <optional>
#include <sstream>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <d2d1.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <dwrite_3.h>
#include <dcomp.h>
#include <dxgi1_3.h>
#include <dxgidebug.h>
#include <VersionHelpers.h>

#include <gsl/gsl_util>
#include <gsl/pointers>
#include <gsl/span>
#include <wil/com.h>
#include <wil/filesystem.h>
#include <wil/result_macros.h>
#include <wil/stl.h>
#include <wil/win32_helpers.h>

// Dynamic Bitset (optional dependency on LibPopCnt for perf at bit counting)
// Variable-size compressed-storage header-only bit flag storage library.
#pragma warning(push)
#pragma warning(disable : 4702) // unreachable code
#include <dynamic_bitset.hpp>
#pragma warning(pop)

// Chromium Numerics (safe math)
#pragma warning(push)
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
#pragma warning(disable : 26812) // The enum type '...' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#include <base/numerics/safe_math.h>
#pragma warning(pop)

#include <til.h>
#include <til/bit.h>


/* matrix/gsl_matrix_short.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 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 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_SHORT_H__
#define __GSL_MATRIX_SHORT_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

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_short.h>
#include <gsl/gsl_blas_types.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;
  short * data;
  gsl_block_short * block;
  int owner;
} gsl_matrix_short;

typedef struct
{
  gsl_matrix_short matrix;
} _gsl_matrix_short_view;

typedef _gsl_matrix_short_view gsl_matrix_short_view;

typedef struct
{
  gsl_matrix_short matrix;
} _gsl_matrix_short_const_view;

typedef const _gsl_matrix_short_const_view gsl_matrix_short_const_view;

/* Allocation */

GSL_FUN gsl_matrix_short *
gsl_matrix_short_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_short *
gsl_matrix_short_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_short *
gsl_matrix_short_alloc_from_block (gsl_block_short * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_FUN gsl_matrix_short *
gsl_matrix_short_alloc_from_matrix (gsl_matrix_short * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN gsl_vector_short *
gsl_vector_short_alloc_row_from_matrix (gsl_matrix_short * m,
                                        const size_t i);

GSL_FUN gsl_vector_short *
gsl_vector_short_alloc_col_from_matrix (gsl_matrix_short * m,
                                        const size_t j);

GSL_FUN void gsl_matrix_short_free (gsl_matrix_short * m);

/* Views */

GSL_FUN _gsl_matrix_short_view
gsl_matrix_short_submatrix (gsl_matrix_short * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_row (gsl_matrix_short * m, const size_t i);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_column (gsl_matrix_short * m, const size_t j);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_diagonal (gsl_matrix_short * m);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_subdiagonal (gsl_matrix_short * m, const size_t k);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_superdiagonal (gsl_matrix_short * m, const size_t k);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_subrow (gsl_matrix_short * m, const size_t i,
                         const size_t offset, const size_t n);

GSL_FUN _gsl_vector_short_view
gsl_matrix_short_subcolumn (gsl_matrix_short * m, const size_t j,
                            const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_short_view
gsl_matrix_short_view_array (short * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_short_view
gsl_matrix_short_view_array_with_tda (short * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_short_view
gsl_matrix_short_view_vector (gsl_vector_short * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_short_view
gsl_matrix_short_view_vector_with_tda (gsl_vector_short * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_short_const_view
gsl_matrix_short_const_submatrix (const gsl_matrix_short * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_row (const gsl_matrix_short * m,
                            const size_t i);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_column (const gsl_matrix_short * m,
                               const size_t j);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_diagonal (const gsl_matrix_short * m);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_subdiagonal (const gsl_matrix_short * m,
                                    const size_t k);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_superdiagonal (const gsl_matrix_short * m,
                                      const size_t k);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_subrow (const gsl_matrix_short * m, const size_t i,
                               const size_t offset, const size_t n);

GSL_FUN _gsl_vector_short_const_view
gsl_matrix_short_const_subcolumn (const gsl_matrix_short * m, const size_t j,
                                  const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_short_const_view
gsl_matrix_short_const_view_array (const short * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_short_const_view
gsl_matrix_short_const_view_array_with_tda (const short * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_short_const_view
gsl_matrix_short_const_view_vector (const gsl_vector_short * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_short_const_view
gsl_matrix_short_const_view_vector_with_tda (const gsl_vector_short * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_short_set_zero (gsl_matrix_short * m);
GSL_FUN void gsl_matrix_short_set_identity (gsl_matrix_short * m);
GSL_FUN void gsl_matrix_short_set_all (gsl_matrix_short * m, short x);

GSL_FUN int gsl_matrix_short_fread (FILE * stream, gsl_matrix_short * m) ;
GSL_FUN int gsl_matrix_short_fwrite (FILE * stream, const gsl_matrix_short * m) ;
GSL_FUN int gsl_matrix_short_fscanf (FILE * stream, gsl_matrix_short * m);
GSL_FUN int gsl_matrix_short_fprintf (FILE * stream, const gsl_matrix_short * m, const char * format);

GSL_FUN int gsl_matrix_short_memcpy(gsl_matrix_short * dest, const gsl_matrix_short * src);
GSL_FUN int gsl_matrix_short_swap(gsl_matrix_short * m1, gsl_matrix_short * m2);
GSL_FUN int gsl_matrix_short_tricpy(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix_short * dest, const gsl_matrix_short * src);

GSL_FUN int gsl_matrix_short_swap_rows(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_short_swap_columns(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_short_swap_rowcol(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_short_transpose (gsl_matrix_short * m);
GSL_FUN int gsl_matrix_short_transpose_memcpy (gsl_matrix_short * dest, const gsl_matrix_short * src);
GSL_FUN int gsl_matrix_short_transpose_tricpy (CBLAS_UPLO_t Uplo_src, CBLAS_DIAG_t Diag, gsl_matrix_short * dest, const gsl_matrix_short * src);

GSL_FUN short gsl_matrix_short_max (const gsl_matrix_short * m);
GSL_FUN short gsl_matrix_short_min (const gsl_matrix_short * m);
GSL_FUN void gsl_matrix_short_minmax (const gsl_matrix_short * m, short * min_out, short * max_out);

GSL_FUN void gsl_matrix_short_max_index (const gsl_matrix_short * m, size_t * imax, size_t *jmax);
GSL_FUN void gsl_matrix_short_min_index (const gsl_matrix_short * m, size_t * imin, size_t *jmin);
GSL_FUN void gsl_matrix_short_minmax_index (const gsl_matrix_short * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_FUN int gsl_matrix_short_equal (const gsl_matrix_short * a, const gsl_matrix_short * b);

GSL_FUN int gsl_matrix_short_isnull (const gsl_matrix_short * m);
GSL_FUN int gsl_matrix_short_ispos (const gsl_matrix_short * m);
GSL_FUN int gsl_matrix_short_isneg (const gsl_matrix_short * m);
GSL_FUN int gsl_matrix_short_isnonneg (const gsl_matrix_short * m);

GSL_FUN int gsl_matrix_short_add (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_FUN int gsl_matrix_short_sub (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_FUN int gsl_matrix_short_mul_elements (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_FUN int gsl_matrix_short_div_elements (gsl_matrix_short * a, const gsl_matrix_short * b);
GSL_FUN int gsl_matrix_short_scale (gsl_matrix_short * a, const double x);
GSL_FUN int gsl_matrix_short_scale_rows (gsl_matrix_short * a, const gsl_vector_short * x);
GSL_FUN int gsl_matrix_short_scale_columns (gsl_matrix_short * a, const gsl_vector_short * x);
GSL_FUN int gsl_matrix_short_add_constant (gsl_matrix_short * a, const double x);
GSL_FUN int gsl_matrix_short_add_diagonal (gsl_matrix_short * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_short_get_row(gsl_vector_short * v, const gsl_matrix_short * m, const size_t i);
GSL_FUN int gsl_matrix_short_get_col(gsl_vector_short * v, const gsl_matrix_short * m, const size_t j);
GSL_FUN int gsl_matrix_short_set_row(gsl_matrix_short * m, const size_t i, const gsl_vector_short * v);
GSL_FUN int gsl_matrix_short_set_col(gsl_matrix_short * m, const size_t j, const gsl_vector_short * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL short   gsl_matrix_short_get(const gsl_matrix_short * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void    gsl_matrix_short_set(gsl_matrix_short * m, const size_t i, const size_t j, const short x);
GSL_FUN INLINE_DECL short * gsl_matrix_short_ptr(gsl_matrix_short * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const short * gsl_matrix_short_const_ptr(const gsl_matrix_short * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
short
gsl_matrix_short_get(const gsl_matrix_short * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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] ;
}

INLINE_FUN
void
gsl_matrix_short_set(gsl_matrix_short * m, const size_t i, const size_t j, const short x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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 ;
}

INLINE_FUN
short *
gsl_matrix_short_ptr(gsl_matrix_short * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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 (short *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const short *
gsl_matrix_short_const_ptr(const gsl_matrix_short * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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 short *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_SHORT_H__ */


#pragma once
#include <array>
#include <gsl/span>
#include <sam.h>

namespace mcu {

#define EEPROM [[gnu::section(".eeprom")]]

class FlashEEPROM
{
public:
	FlashEEPROM() = delete;

	template <typename T>
	static void write(const T* flashAddress, const T& src) noexcept
	{
		write(flashAddress, gsl::span(&src, 1));
	}
	template <typename T>
	static T read(const T* flashAddress) noexcept
	{
		T result;
		read(flashAddress, gsl::span(&result, 1));
		return result;
	}

	template <typename T>
	static void write(const T* flashAddress, gsl::span<const T> src) noexcept
	{
		write(reinterpret_cast<const std::byte*>(flashAddress), gsl::as_bytes(src));
	}
	template <typename T>
	static void read(const T* flashAddress, gsl::span<T> dst) noexcept
	{
		read(reinterpret_cast<const std::byte*>(flashAddress), gsl::as_writable_bytes(dst));
	}

	static void write(const std::byte* flashAddress, gsl::span<const std::byte> src) noexcept;
	static void read(const std::byte* flashAddress, gsl::span<std::byte> dst) noexcept;

	static void commit() noexcept;
	static bool needsCommit() noexcept { return cacheDirty; }

private:
	using Page = std::array<unsigned, FLASH_PAGE_SIZE / sizeof(unsigned)>;
	using Row = std::array<Page, 4>;

	static constexpr size_t RowSize = sizeof(Row);
	static constexpr uintptr_t RowOffsetMask = sizeof(Row) - 1;

	union Cache {
		std::array<std::byte, sizeof(Row)> bytes;
		Row pages;
	};
	static Cache cache;
	static Row* cacheTag;
	static bool cacheDirty;

	static void cacheRow(Row* row) noexcept;
	static void copyPage(const Page& src, Page& dst) noexcept;
};

} // namespace mcu


// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root
// for more information.

#ifndef NOVELRT_GRAPHICS_H
#define NOVELRT_GRAPHICS_H

// Graphics dependencies
#include "NovelRT/EngineConfig.h"
#include "NovelRT/Maths/Maths.h"
#include "NovelRT/ResourceManagement/ResourceManagement.h"
#include "NovelRT/SceneGraph/SceneGraph.h"
#include "NovelRT/Threading/Threading.h"
#include "NovelRT/Utilities/Event.h"
#include "NovelRT/Utilities/Lazy.h"
#include "NovelRT/Utilities/Misc.h"
#include "RGBAColour.h"
#include <chrono>
#include <cstdint>
#include <filesystem>
#include <gsl/span>
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <typeindex>
#include <utility>
#include <vector>

/**
 * @brief The experimental Graphics plugin API. Comes with built-in support for the ECS.
 */
namespace NovelRT::Graphics
{
    enum class ShaderProgramKind : uint32_t;
    enum class GraphicsResourceAccess : uint32_t;
    enum class GraphicsSurfaceKind : uint32_t;
    enum class GraphicsTextureAddressMode : uint32_t;
    enum class GraphicsPipelineBlendFactor : uint32_t;
    enum class GraphicsPipelineInputElementKind : uint32_t;
    enum class GraphicsPipelineResourceKind : uint32_t;
    enum class ShaderProgramVisibility : uint32_t;
    enum class GraphicsTextureKind : uint32_t;
    enum class GraphicsMemoryRegionAllocationFlags : uint32_t;
    enum class GraphicsMemoryRegionAllocationFlags : uint32_t;
    enum class GraphicsBufferKind : uint32_t;
    enum class TexelFormat : uint32_t;
    struct GraphicsMemoryAllocatorSettings;
    class GraphicsDeviceObject;
    class IGraphicsSurface;
    class GraphicsAdapter;
    class GraphicsDevice;
    class GraphicsResource;
    class GraphicsBuffer;
    class GraphicsTexture;
    class ShaderProgram;
    class GraphicsPipeline;
    class GraphicsPipelineSignature;
    class GraphicsPipelineInput;
    class GraphicsPipelineResource;
    class GraphicsPipelineInputElement;
    class GraphicsContext;
    class GraphicsFence;
    class GraphicsPrimitive;
    class GraphicsProvider;
    class GraphicsMemoryAllocator;
    class IGraphicsAdapterSelector;
    class GraphicsMemoryBlockCollection;
    class GraphicsMemoryBlock;
    class GraphicsMemoryBudget;
    class GraphicsSurfaceContext;
    class GraphicsResourceManager;
}

// Graphics types
// clang-format off

#include "ShaderProgramKind.h"
#include "GraphicsAdapter.h"
#include "GraphicsDeviceObject.h"
#include "GraphicsContext.h"
#include "GraphicsFence.h"
#include "GraphicsMemoryAllocatorSettings.h"
#include "GraphicsMemoryRegion.h"
#include "IGraphicsMemoryRegionCollection.h"
#include "GraphicsMemoryRegionAllocationFlags.h"
#include "TexelFormat.h"
#include "GraphicsMemoryAllocator.h"
#include "GraphicsMemoryBlockCollection.h"
#include "GraphicsMemoryBudget.h"
#include "GraphicsMemoryBlock.h"
#include "GraphicsResourceAccess.h"
#include "GraphicsSurfaceKind.h"
#include "GraphicsTextureKind.h"
#include "IGraphicsSurface.h"
#include "GraphicsSurfaceContext.h"
#include "GraphicsDevice.h"
#include "GraphicsResource.h"
#include "GraphicsBufferKind.h"
#include "GraphicsBuffer.h"
#include "GraphicsTextureAddressMode.h"
#include "GraphicsTexture.h"
#include "IGraphicsAdapterSelector.h"
#include "ShaderProgram.h"
#include "GraphicsPipelineBlendFactor.h"
#include "GraphicsPipeline.h"
#include "GraphicsPipelineSignature.h"
#include "GraphicsPrimitive.h"
#include "GraphicsProvider.h"
#include "GraphicsPipelineInput.h"
#include "GraphicsPipelineInputElement.h"
#include "GraphicsPipelineInputElementKind.h"
#include "GraphicsPipelineResource.h"
#include "GraphicsPipelineResourceKind.h"
#include "ShaderProgramVisibility.h"
#include "GraphicsResourceManager.h"

// clang-format on

#endif // !NOVELRT_GRAPHICS_H


#ifndef GNUPLOT_H
#define GNUPLOT_H

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

#include "constants.h"

void writePlotData(gsl_vector *f, const std::string& filename);

#endif // GNUPLOT_H


#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>
#include <gsl/gsl_odeiv2.h>

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

#include <time.h>
#include <math.h>

#include <GL/glut.h>
#include <GL/gl.h>

#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

#define SLICE	360

typedef struct _line_vertex line_vertex;
struct _line_vertex
{
	float x1;
	float y1;

	float x2;
	float y2;
};

float text_x = 64.0f, text_y = 58.0f;
const float TRANS_VAL = 100.0f;

uint32_t mouse_loc;
int x, y;

bool clicked = false;
int pos_x, pos_y;
int mov_x, mov_y;
int click_x, click_y;
int special_key;

bool run = false;

line_vertex vertices[9] = {
	{ 50.0f, 50.0f, 50.0f, 30.0f },
	{ 50.0f, 50.0f, 55.0f, 50.0f },
	{ 75.0f, 50.0f, 80.0f, 50.0f },
	{ 80.0f, 50.0f, 80.0f, 30.0f },
	{ 55.0f, 55.0f, 55.0f, 45.0f },
	{ 55.0f, 55.0f, 75.0f, 55.0f },
	{ 75.0f, 55.0f, 75.0f, 45.0f },
	{ 55.0f, 45.0f, 75.0f, 45.0f }
};

line_vertex pick_vertices[9] = {
	{ 45.0f, 25.0f, 45.0f, 65.0f },
	{ 45.0f, 25.0f, 85.0f, 25.0f },
	{ 45.0f, 65.0f, 85.0f, 65.0f },
	{ 85.0f, 25.0f, 85.0f, 65.0f }
};

line_vertex power_vertices[5] = {
	{ 40.0f, 50.0f, 40.0f, 60.0f },
	{ 50.0f, 50.0f, 30.0f, 50.0f },
	{ 45.0f, 45.0f, 35.0f, 45.0f },
	{ 40.0f, 45.0f, 40.0f, 35.0f }
};

line_vertex ground_vertices[5] = {
	{ 40.0f, 50.0f, 40.0f, 60.0f },
	{ 50.0f, 50.0f, 30.0f, 50.0f },
	{ 47.5f, 47.5f, 32.5f, 47.5f },
	{ 45.0f, 45.0f, 35.0f, 45.0f }
};

int *jac;

int rhs (double t, const double y[], double f[], void *params_ptr);
//int jacobian (double t, const double y[], double *dfdy, double dfdt[], void *params_ptr);

void calc_differential_eq(void);
void draw_differential_eq_plot(void);
void draw_lattice(void);
void draw_lattice2(void);
void draw_volt_plot(void);

void reshape(int w, int h);

typedef struct _xy_plot xy_plot;
struct _xy_plot
{
	double x;
	double y;
};

//xy_plot plot_res[1001];
xy_plot plot_res[100000001];
//double volt_res[100000001];

float resistance;
float inductance;
float voltage;

int sub_window;
int sub_window2;

void sim_reshape(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

	// glOrtho(표기하고자 하는 최소 x값, 최대 x값, 화면상 나타는 최소 y, 최대 y, z는 관계없음)
	// 즉 내가 보고자 하는 x, y, z의 범위를 지정할 수 있음
	glOrtho(-0.000005, 0.000005, -0.7, 0.7, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void simulate(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3f(1, 0, 0);

    glBegin(GL_LINE_LOOP);
    	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);

    glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 100.0, 0.0);
        glVertex3f(0.0, -100.0, 0.0);
    glEnd();

    draw_differential_eq_plot();
    draw_lattice();

    glutSwapBuffers();
}

void sim_reshape2(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

	glOrtho(-0.000005, 0.000005, -2000000, 500000, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void simulate2(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3f(1, 0, 0);

    glBegin(GL_LINE_LOOP);
    	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);

    glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 500000.0, 0.0);
        glVertex3f(0.0, -2000000.0, 0.0);
    glEnd();

    draw_volt_plot();
    draw_lattice2();

    glutSwapBuffers();
}

void keyboard_handler(unsigned char key, int x, int y)
{
	int pid;
	int i, status;

	// fork -> execve
	// OpenGL -> GPU
#if 0
	char *argv[5] = {"print_simulation"};
	char *envp[] = {0};
	char volt[64] = {0};
	char coil[64] = {0};
	char resist[64] = {0};
#endif

	switch(key)
	{
		case 's':
			printf("Simulation Preparation\n");
			printf("저항값 입력: ");
			scanf("%f", &resistance);
			printf("인덕턴스값 입력: ");
			scanf("%f", &inductance);
			printf("입력 전원값 입력: ");
			scanf("%f", &voltage);

			printf("R = %f, L = %f, V = %f\n", resistance, inductance, voltage);

			calc_differential_eq();
			run = true;
			break;
		case 'r':
			printf("Run Simulation\n");

#if 0
			pid = fork();

			if (pid > 0)
			{
				printf("wait for simulation finished\n");
				wait(&status);
			}
			else if (pid == 0)
			{
				execve("./print_simulation", argv, envp);
			}
			else
			{
				perror("fork() ");
				exit(-1);
			}
#endif

			sub_window = glutCreateWindow("Current i(t)");

			glutDisplayFunc(simulate);
			glutReshapeFunc(sim_reshape);

			sub_window2 = glutCreateWindow("Voltage V(t)");

			glutDisplayFunc(simulate2);
			glutReshapeFunc(sim_reshape2);
			break;
		case 't':
			printf("Termination\n");
			glutDestroyWindow(sub_window);
			glutDestroyWindow(sub_window2);
			break;
		case 27:
			exit(1);
			break;
	}
}

void on_mouse(int button, int state, int x, int y)
{
	special_key = glutGetModifiers();

	if (button == GLUT_LEFT_BUTTON & state == GLUT_DOWN)
	{
		if (clicked == false)
		{
			printf("Click - Down\n");
			click_x = x;
			click_y = y;
			clicked = true;

			printf("click_x = %d, click_y = %d\n", click_x, click_y);
		}
	}

	if (button == GLUT_LEFT_BUTTON & state == GLUT_UP)
	{
		printf("Click - Up\n");
		clicked = false;
	}

	int window_width = glutGet(GLUT_WINDOW_WIDTH);
	int window_height = glutGet(GLUT_WINDOW_HEIGHT);

	GLbyte color[4];
	GLfloat depth;
	GLuint index;

	glReadPixels(x, window_height - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
	glReadPixels(x, window_height - y - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
	glReadPixels(x, window_height - y - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &index);

	printf("Clicked on pixel %d, %d, color %02hhx%02hhx%02hhx%02hhx, depth %f, stencil index %u\n",
			x, y, color[0], color[1], color[2], color[3], depth, index);

}

void drag_mouse(int x, int y)
{
	pos_x = x;
	pos_y = y;

	special_key = glutGetModifiers();

	if (clicked == true)
	{
		printf("clicked!\n");

		mov_x = (click_x - pos_x) / 3;
		mov_y = (click_y - pos_y) / 2.5;

		click_x = pos_x;
		click_y = pos_y;

		printf("mov_x = %d, mov_y = %d\n", mov_x, mov_y);

		int i;

		for (i = 0; i < 8; i++)
		{
			vertices[i].x1 -= mov_x;
			vertices[i].x2 -= mov_x;
			vertices[i].y1 += mov_y;
			vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			pick_vertices[i].x1 -= mov_x;
			pick_vertices[i].x2 -= mov_x;
			pick_vertices[i].y1 += mov_y;
			pick_vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			power_vertices[i].x1 -= mov_x;
			power_vertices[i].x2 -= mov_x;
			power_vertices[i].y1 += mov_y;
			power_vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			ground_vertices[i].x1 -= mov_x;
			ground_vertices[i].x2 -= mov_x;
			ground_vertices[i].y1 += mov_y;
			ground_vertices[i].y2 += mov_y;
		}

		text_x -= mov_x;
		text_y += mov_y;

		glutPostRedisplay();
	}
}

void drawString (char *s)
{
	unsigned int i;

	for (i = 0; i < strlen (s); i++)
		glutBitmapCharacter (GLUT_BITMAP_HELVETICA_10, s[i]);
}

void drawStringBig (char *s)
{
	unsigned int i;

	for (i = 0; i < strlen (s); i++)
		glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, s[i]);
}

void draw_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(pick_vertices[i].x1, pick_vertices[i].y1);
			glVertex2f(pick_vertices[i].x2, pick_vertices[i].y2);
		}
	glEnd();
}

void draw_inductance_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 + 50.0f, pick_vertices[i].y1);
			glVertex2f(pick_vertices[i].x2 + 50.0f, pick_vertices[i].y2);
		}
	glEnd();
}

void draw_resistance(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(vertices[i].x1, vertices[i].y1);
			glVertex2f(vertices[i].x2, vertices[i].y2);
		}
	glEnd();

	sprintf (label, "R");
	glRasterPos2f (text_x, text_y);
	drawStringBig (label);
}

void draw_inductance(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(vertices[i].x1 + 50.0f, vertices[i].y1);
			glVertex2f(vertices[i].x2 + 50.0f, vertices[i].y2);
		}
	glEnd();

	sprintf (label, "L");
	glRasterPos2f (text_x + 50.0f, text_y);
	drawStringBig (label);
}

void draw_power_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 - 75.0f, pick_vertices[i].y1 + 20.0f);
			glVertex2f(pick_vertices[i].x2 - 75.0f, pick_vertices[i].y2 + 20.0f);
		}
	glEnd();
}

void draw_power(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(power_vertices[i].x1 - 50.0f, power_vertices[i].y1 + 15.0f);
			glVertex2f(power_vertices[i].x2 - 50.0f, power_vertices[i].y2 + 15.0f);
		}
	glEnd();

	sprintf (label, "V");
	glRasterPos2f (text_x - 70.0f, text_y + 20.0f);
	drawStringBig (label);
}

void draw_ground_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 - 75.0f, pick_vertices[i].y1 - 20.0f);
			glVertex2f(pick_vertices[i].x2 - 75.0f, pick_vertices[i].y2 - 20.0f);
		}
	glEnd();
}

void draw_ground(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(ground_vertices[i].x1 - 50.0f, ground_vertices[i].y1 - 30.0f);
			glVertex2f(ground_vertices[i].x2 - 50.0f, ground_vertices[i].y2 - 30.0f);
		}
	glEnd();

	sprintf (label, "GND");
	glRasterPos2f (text_x - 70.0f, text_y - 30.0f);
	drawStringBig (label);
}

void display(void)
{
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glColor3f(1, 0, 0);

	glBegin(GL_LINE_LOOP);
	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
        glEnd();

        glColor3f(0.0, 1.0, 0.0);

        glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 100.0, 0.0);
        glVertex3f(0.0, -100.0, 0.0);
    glEnd();

	draw_resistance();
	draw_outline();

	draw_inductance();
	draw_inductance_outline();

	draw_power();
	draw_power_outline();

	draw_ground();
	draw_ground_outline();

#if 0
	if (run)
	{
		draw_differential_eq_plot();
	}

	draw_lattice();
#endif

	glutSwapBuffers();
}

void reshape(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

#if 0
    if(w <= h)
        glOrtho(-n_range, n_range, -n_range * h / w, n_range * h / w, -n_range, n_range);
    else
        glOrtho(-n_range * w / h, n_range * w / h, -n_range, n_range, -n_range, n_range);
#endif

	//glOrtho(-10, 10, -0.5, 0.5, -1, 1);
	glOrtho(-100, 100, -100, 100, 1.0, -1.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int calc_vertices_num(void)
{
	double tmin = 0.0, tmax = 10.0, delta_t = 0.01;
	return (tmax - tmin) / delta_t;
}

void calc_differential_eq(void)
{
    int dim = 1;

	gsl_odeiv2_system sys = {rhs, NULL, dim, NULL};
	gsl_odeiv2_driver * d = gsl_odeiv2_driver_alloc_y_new (&sys, gsl_odeiv2_step_rkf45, 1e-6, 1e-6, 0.0);

	int cnt = 0;

	double i;
    double x0 = 0.0,  xf = 10.0;
    double x = x0;
    //double y[1] = { 0.5 };
    double y[1] = { 0.0 };
	double delta = 0.0000001;

	for (i = 0; i <= xf; i += delta)
    {
        double xi = x0 + i * (xf-x0) / xf;
        int status = gsl_odeiv2_driver_apply (d, &x, xi, y);

        if (status != GSL_SUCCESS)
        {
            printf ("error, return value=%d\n", status);
            break;
        }

		plot_res[cnt].x = x;
		plot_res[cnt].y = y[0];

		//volt_res[cnt] = y[0] * (resistance / inductance);

		if (cnt < 1001)
		{
       		printf ("%.8e %.8e\n", plot_res[cnt].x, plot_res[cnt].y);
		}

		cnt++;
    }

	printf("Finish Calc\n");

    gsl_odeiv2_driver_free (d);

#if 0
    const gsl_odeiv_step_type *type_ptr = gsl_odeiv_step_rkf45;

    gsl_odeiv_step *step_ptr = gsl_odeiv_step_alloc (type_ptr, dimension);
    gsl_odeiv_control *control_ptr = gsl_odeiv_control_y_new (eps_abs, eps_rel);
    gsl_odeiv_evolve *evolve_ptr = gsl_odeiv_evolve_alloc (dimension);

    gsl_odeiv_system my_system;	/* structure with the rhs function, etc. */

    double mu = 10;		/* parameter for the diffeq */
    double y[2];			/* current solution vector */

    double t, t_next;		/* current and next independent variable */
    double tmin, tmax, delta_t;	/* range of t and step size for output */

    double h = 1e-6;		/* starting step size for ode solver */

	int cnt = 0;
	int vertex_num = calc_vertices_num();

    my_system.function = rhs;	/* the right-hand-side functions dy[i]/dt */
    my_system.jacobian = NULL;
    my_system.dimension = dimension;	/* number of diffeq's */
    my_system.params = NULL;

    tmin = 0.;			/* starting t value */
    tmax = 10.;			/* final t value */
    delta_t = 0.01;

    y[1] = 0.5;			/* initial x value */

    t = tmin;             /* initialize t */

    for (t_next = tmin + delta_t; t_next <= tmax; t_next += delta_t)
    {
        while (t < t_next)	/* evolve from t to t_next */
        {
            gsl_odeiv_evolve_apply (evolve_ptr, control_ptr, step_ptr,
                                    &my_system, &t, t_next, &h, y);
        }

		plot_res[cnt].x = t;
		plot_res[cnt].y = y[0];
        printf ("%.5e %.5e %.5e\n", t, plot_res[cnt].x, plot_res[cnt].y); /* print at t=t_next */

		cnt++;
    }

    gsl_odeiv_evolve_free (evolve_ptr);
    gsl_odeiv_control_free (control_ptr);
    gsl_odeiv_step_free (step_ptr);
#endif
}

void draw_lattice(void)
{
	int i;
	float lattice = 0.000001;
	char buf[64] = {0};

	glColor3f(1, 1, 0);
	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(i * 0.000001, -0.01);
			glVertex2f(i * 0.000001, 0.01);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%f", lattice * i);
	    glRasterPos2f (lattice * i - 0.0000005, -0.05);
	    drawStringBig (buf);
	}

	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(-0.0000002, 0.1 * i);
			glVertex2f(0.0000002, 0.1 * i);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%.1f", 0.1 * i);
	    glRasterPos2f (-0.0000004, 0.1 * i - 0.05);
	    drawStringBig (buf);
	}
}

void draw_differential_eq_plot(void)
{
	static char label[100];

	float x = 0, x2 = 0, y2, cx, cy;
	float tmp;
	int cache = 0;
	int i;

	glColor3f(0, 1, 1);
	glBegin(GL_LINES);
	//for(; ; t += step)
	for (i = 0; i < 100000000; i++)
	{
		x2 = plot_res[i].x;
		y2 = plot_res[i].y;

		if(cache)
		{
			glVertex2f(cx, cy);	// 이전값
			glVertex2f(x2, y2);	// 현재값
		}

		cache = 1;
		cx = x2;
		cy = y2;
		//printf("t = %f, y2 = %f\n", x2, y2);
	}
	glEnd();

	sprintf (label, "i(t)");
	// -0.000005
    glRasterPos2f (-0.0000045, 0.0000045);
    drawStringBig (label);
}

void draw_lattice2(void)
{
	int i;
	float lattice = 0.000001;
	char buf[64] = {0};

	glColor3f(1, 0, 0);
	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(i * 0.000001, -20000);
			glVertex2f(i * 0.000001, 20000);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%f", lattice * i);
	    glRasterPos2f (lattice * i - 0.0000005, 50000);
	    drawStringBig (buf);
	}

	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(-0.0000002, -200000 * i);
			glVertex2f(0.0000002, -200000 * i);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%d", -200000 * i);
	    glRasterPos2f (-0.0000015, -200000 * i);
	    drawStringBig (buf);
	}

}

void draw_volt_plot(void)
{
	static char label[100];

	float x = 0, x2 = 0, y2, cx, cy;
	float tmp;
	int cache = 0;
	int i;

	glColor3f(1, 1, 0);
	glBegin(GL_LINES);
	//for(; ; t += step)
	for (i = 0; i < 100000000; i++)
	{
		x2 = plot_res[i].x;
		y2 = plot_res[i].y * (-resistance / inductance);

		if(cache)
		{
			glVertex2f(cx, cy);	// 이전값
			glVertex2f(x2, y2);	// 현재값
		}

		cache = 1;
		cx = x2;
		cy = y2;
		//printf("t = %f, y2 = %f\n", x2, y2);
	}
	glEnd();

	sprintf (label, "V(t)");
	// -0.000005
    glRasterPos2f (-0.0000045, 0.000004);
    drawStringBig (label);
}

int main (int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("Digital Signal Processing");

	printf("저항 1000, 인덕터: 0.0003, 전압: 5V\n");

	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMouseFunc(on_mouse);
	glutMotionFunc(drag_mouse);
	// 새롭게 추가된 요것은 키보드 입력을 처리하는 콜백 등록 함수
	glutKeyboardFunc(keyboard_handler);
	glutMainLoop();

    return 0;
}

/*************************** rhs ****************************/
/*
   x' = v                  ==>  dy[0]/dt = f[0] = y[1]
   v' = -x + \mu v (1-x^2) ==>  dy[1]/dt = f[1] = -y[0] + mu*y[1]*(1-y[0]*y[0])

   x''(t) + x'(t) + x(t) = 0
   x''(t) = -x'(t) - x(t)

   x' = v            ===> dy[0]/dt = f[0] = y[1]
   v' = -v - x       ===> dy[1]/dt = f[1] = -y[1] - y[0]
*/

// https://www.wolframalpha.com/input/?i=y%27%27+%2B+y%27+%2B+y+%3D+0%2C+y%280%29+%3D+1%2C+y%27%280%29+%3D+0, y(0) = 1
// https://www.wolframalpha.com/input/?i=%28sqrt%283%29+*+sin%28sqrt%283%29+%2F+2%29+%2B+3+*+cos%28sqrt%283%29+%2F+2%29%29+%2F+%283+*+sqrt%28e%29%29, y(1)
// (sqrt(3) * sin(sqrt(3) / 2) + 3 * cos(sqrt(3) / 2)) / (3 * sqrt(e)), y(1) = 0.6597001...
int
rhs (double t, const double y[], double f[], void *params_ptr)
{
#if 0
	f[0] = y[1];
	f[1] = -y[1] - 9 * y[0];
#endif

	f[0] =  -(resistance / inductance) * y[0] + voltage / inductance;

    return GSL_SUCCESS;		/* GSL_SUCCESS defined in gsl/errno.h as 0 */
}

#if 0

주석: 현재 아래 상황은 방전 상황입니다.
      또한 현재 코드는 충전 상황입니다.
	  두 가지 버전으로 작성이 필요함.

V = iR + L * di /dt
초기조건 V_L(t) | (t=0) ===> V_L(0) = 0V

V * (1 / L) = i(t) * (R / L) + i`(t)

P(x) = R / L

mu = C * exp^(int P(x)dx)

mu = C * exp^(R/L)t

d (mu * i(t)) / dt = (R/L) exp^(R/L)t * i(t) + exp^(R/L)t * i`(t)

int d(mu * i(t)) / dt = mu * i(t) = c

exp^(R/L)t * i(t) = C

i(t) = C * exp^(-R/L)t

i(t) | (t=0) = 0.5A ===> C = 0.5

i(t) = 0.5 * exp^(-R/L)t
V(t) = L di / dt
     = L * 0.5 * (-R/L) * exp^(-R/L)t
	-(resistance / inductance)
#endif


#pragma once

#include <imageview/ImageRowView.h>
#include <imageview/IsPixelFormat.h>
#include <imageview/internal/ImageViewIterator.h>
#include <imageview/internal/ImageViewStorage.h>
#include <imageview/internal/PixelRef.h>

#include <gsl/assert>
#include <gsl/span>

#include <cstddef>
#include <type_traits>

namespace imageview {

// Non-owning view into a bitmap image with the specified pixel format.
//
// \param PixelFormat - specifies how colors are stored in the bitmap.
//        ContinuousImageView only supports images with fixed color depth (bits
//        per pixel). PixelFormat should satisfy IsPixelFormat trait, i.e. be
//        like
//          class MyPixelFormat {
//           public:
//            using color_type = MyColor;
//            static constexpr int kBytesPerPixel = N;
//            color_type read(gsl::span<const std::byte, kBytesPerPixel> pixel_data) const;
//            void write(const color_type& color, gsl::span<std::byte, kBytesPerPixel> pixel_data) const;
//          };
// \param Mutable - if true, ContinuousImageView provides write access to the
//        bitmap (naturally, this requires that ContinuousImageView is
//        constructed from a non-const pointer to the data). Otherwise, only
//        read-only access is provided.
template <class PixelFormat, bool Mutable = false>
class ContinuousImageView {
 public:
  static_assert(IsPixelFormat<PixelFormat>::value, "Not a PixelFormat.");

  using byte_type = std::conditional_t<Mutable, std::byte, const std::byte>;
  using value_type = typename PixelFormat::color_type;
  // If `Mutable == false`, then `reference` is an alias to `value_type`.
  // Otherwise, it is a proxy class, which mimics `value_type&`:
  // * is implicitly convertible to `value_type`.
  // * you can assign a value_type to it, changing the color of the referenced pixel.
  using reference = std::conditional_t<Mutable, detail::PixelRef<PixelFormat>, value_type>;
  // Constant LegacyInputIterator whose value_type is @value_type.
  // It is not, however, a LegacyForwardIterator, because
  //   std::iterator_traits<iterator>::reference
  // is not const value_type&.
  // Nevertheless, this iterator supports all arithmetic operations of LegacyRandomAccessIterator, e.g.,
  //   `it += n`, ` --it`, `it[n]`.
  using const_iterator = detail::ImageViewIterator<PixelFormat, false>;
  // LegacyInputIterator whose value_type is @value_type. If Mutable == true, then it also
  // models LegacyOutputIterator. It is not, however, a LegacyForwardIterator, because
  //   std::iterator_traits<iterator>::reference
  // is neither value_type& nor const value_type&.
  // Nevertheless, this iterator supports all arithmetic operations of LegacyRandomAccessIterator, e.g.,
  //   `it += n`, ` --it`, `it[n]`.
  using iterator = detail::ImageViewIterator<PixelFormat, Mutable>;

  // Construct an empty view.
  // This constructor is only available if PixelFormat is default-constructible.
  template <class Enable = std::enable_if_t<std::is_default_constructible_v<PixelFormat>>>
  constexpr ContinuousImageView() noexcept(noexcept(std::is_nothrow_default_constructible_v<PixelFormat>)) {}

  // Construct a view into an image.
  // This constructor is only available if PixelFormat is default-constructible
  // \param height - height of the image.
  // \param width - width of the image.
  // \param data - pointer to the uncompressed RGB24 bitmap data. The size of
  //        the array should be exactly
  //          height * width * PixelFormat::kBytesPerPixel.
  //        Pixels are assumed to be stored contiguously, with each pixel
  //        occupying exactly PixelFormat::kBytesPerPixel.
  template <class Enable = std::enable_if_t<std::is_default_constructible_v<PixelFormat>>>
  constexpr ContinuousImageView(unsigned int height, unsigned int width, gsl::span<byte_type> data) noexcept(
      std::is_nothrow_default_constructible_v<PixelFormat>);

  // Construct a view into an image.
  // \param height - height of the image.
  // \param width - width of the image.
  // \param data - pointer to the uncompressed RGB24 bitmap data. The size of
  //        the array should be exactly
  //          height * width * PixelFormat::kBytesPerPixel.
  //        Pixels are assumed to be stored contiguously, with each pixel
  //        occupying exactly PixelFormat::kBytesPerPixel.
  // \param pixel_format - PixelFormat instance to use.
  constexpr ContinuousImageView(unsigned int height, unsigned int width, gsl::span<byte_type> data,
                                const PixelFormat& pixel_format);

  // Construct a view into an image.
  // \param height - height of the image.
  // \param width - width of the image.
  // \param data - pointer to the uncompressed RGB24 bitmap data. The size of
  //        the array should be exactly
  //          height * width * PixelFormat::kBytesPerPixel.
  //        Pixels are assumed to be stored contiguously, with each pixel
  //        occupying exactly PixelFormat::kBytesPerPixel.
  // \param pixel_format - PixelFormat instance to use.
  constexpr ContinuousImageView(unsigned int height, unsigned int width, gsl::span<byte_type> data,
                                PixelFormat&& pixel_format);

  // Construct a read-only view from a mutable view.
  template <class Enable = std::enable_if_t<!Mutable>>
  constexpr ContinuousImageView(ContinuousImageView<PixelFormat, !Mutable> image);

  // Returns the height of the image.
  constexpr unsigned int height() const noexcept;

  // Returns the width of the image.
  constexpr unsigned int width() const noexcept;

  // Returns the total number of pixels.
  constexpr std::size_t area() const noexcept;

  // Returns true if the image has zero area, false otherwise.
  constexpr bool empty() const noexcept;

  // Returns the pixel format used by this image.
  constexpr const PixelFormat& pixelFormat() const noexcept;

  // Returns the pointer to the bitmap data.
  constexpr gsl::span<byte_type> data() const noexcept;

  // Returns the total size of the bitmap in bytes.
  constexpr std::size_t size_bytes() const noexcept;

  // Returns an iterator to the first pixel.
  constexpr iterator begin() const;

  // Returns a const iterator to the first pixel.
  constexpr const_iterator cbegin() const;

  // Returns an iterator past the last pixel.
  constexpr iterator end() const;

  // Returns a const iterator past the last pixel.
  constexpr const_iterator cend() const;

  // Access the specified pixel.
  // \param y - Y coordinate of the pixel. Should be within [0; height()).
  // \param x - X coordinate of the pixel. Should be within [0; width()).
  // \return the color of the specified pixel.
  constexpr reference operator()(unsigned int y, unsigned int x) const;

  // Returns a non-owning view into the specified row of the image.
  // \param y - 0-based index of the row. Should be within [0; height()).
  // \return a non-owning view into the row @y.
  constexpr ImageRowView<PixelFormat, Mutable> row(unsigned int y) const;

 private:
  constexpr gsl::span<byte_type, PixelFormat::kBytesPerPixel> getPixelData(unsigned int y, unsigned int x) const;

  detail::ImageViewStorage<PixelFormat, Mutable> storage_;
  unsigned int height_ = 0;
  unsigned int width_ = 0;
};

// Convert a ContinuousImageView into an ImageRowView.
// \param image - input image.
// \return an ImageRowView referring to the same data as image; the number of
//         elements in the returned view equals image.area().
template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable> flatten(ContinuousImageView<PixelFormat, Mutable> image) {
  return ImageRowView<PixelFormat, Mutable>(image.data(), image.area(), image.pixelFormat());
}

template <class PixelFormat, bool Mutable>
template <class Enable>
constexpr ContinuousImageView<PixelFormat, Mutable>::ContinuousImageView(
    unsigned int height, unsigned int width,
    gsl::span<byte_type> data) noexcept(std::is_nothrow_default_constructible_v<PixelFormat>)
    : storage_(data.data()), height_(height), width_(width) {
  Expects(data.size() == height * width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
constexpr ContinuousImageView<PixelFormat, Mutable>::ContinuousImageView(unsigned int height, unsigned int width,
                                                                         gsl::span<byte_type> data,
                                                                         const PixelFormat& pixel_format)
    : storage_(data.data(), pixel_format), height_(height), width_(width) {
  Expects(data.size() == height * width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
constexpr ContinuousImageView<PixelFormat, Mutable>::ContinuousImageView(unsigned int height, unsigned int width,
                                                                         gsl::span<byte_type> data,
                                                                         PixelFormat&& pixel_format)
    : storage_(data.data(), std::move(pixel_format)), height_(height), width_(width) {
  Expects(data.size() == height * width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
template <class Enable>
constexpr ContinuousImageView<PixelFormat, Mutable>::ContinuousImageView(
    ContinuousImageView<PixelFormat, !Mutable> image)
    : ContinuousImageView(image.height(), image.width(), image.data(), image.pixelFormat()) {}

template <class PixelFormat, bool Mutable>
constexpr unsigned int ContinuousImageView<PixelFormat, Mutable>::height() const noexcept {
  return height_;
}

template <class PixelFormat, bool Mutable>
constexpr unsigned int ContinuousImageView<PixelFormat, Mutable>::width() const noexcept {
  return width_;
}

template <class PixelFormat, bool Mutable>
constexpr std::size_t ContinuousImageView<PixelFormat, Mutable>::area() const noexcept {
  return static_cast<std::size_t>(height_) * width_;
}

template <class PixelFormat, bool Mutable>
constexpr bool ContinuousImageView<PixelFormat, Mutable>::empty() const noexcept {
  return height_ == 0 || width_ == 0;
}

template <class PixelFormat, bool Mutable>
constexpr const PixelFormat& ContinuousImageView<PixelFormat, Mutable>::pixelFormat() const noexcept {
  return storage_.pixelFormat();
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::data() const noexcept -> gsl::span<byte_type> {
  return gsl::span<byte_type>(storage_.data(), size_bytes());
}

template <class PixelFormat, bool Mutable>
constexpr std::size_t ContinuousImageView<PixelFormat, Mutable>::size_bytes() const noexcept {
  return area() * PixelFormat::kBytesPerPixel;
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::begin() const -> iterator {
  return iterator(storage_.data(), pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::cbegin() const -> const_iterator {
  return const_iterator(storage_.data(), pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::end() const -> iterator {
  return iterator(storage_.data() + size_bytes(), pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::cend() const -> const_iterator {
  return const_iterator(storage_.data() + size_bytes(), pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::getPixelData(unsigned int y, unsigned int x) const
    -> gsl::span<byte_type, PixelFormat::kBytesPerPixel> {
  Expects(y < height_);
  Expects(x < width_);
  const std::size_t offset = (y * width_ + x) * PixelFormat::kBytesPerPixel;
  return gsl::span<byte_type, PixelFormat::kBytesPerPixel>(storage_.data() + offset, PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
constexpr auto ContinuousImageView<PixelFormat, Mutable>::operator()(unsigned int y, unsigned int x) const
    -> reference {
  const gsl::span<byte_type, PixelFormat::kBytesPerPixel> pixel_data = getPixelData(y, x);
  if constexpr (Mutable) {
    return detail::PixelRef<PixelFormat>(pixel_data, storage_.pixelFormat());
  } else {
    return storage_.pixelFormat().read(pixel_data);
  }
}

template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable> ContinuousImageView<PixelFormat, Mutable>::row(unsigned int y) const {
  Expects(y < height_);
  const std::size_t bytes_per_row = static_cast<std::size_t>(width_) * PixelFormat::kBytesPerPixel;
  const gsl::span<byte_type> row_data(storage_.data() + y * bytes_per_row, bytes_per_row);
  return ImageRowView<PixelFormat, Mutable>(row_data, width_, pixelFormat());
}

}  // namespace imageview


// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2013.
//
// This software is released under a three-clause BSD license:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of any author or any participating institution
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS.
// --------------------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
// INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// --------------------------------------------------------------------------
// $Maintainer: Erhan Kenar $
// $Authors: $
// --------------------------------------------------------------------------

#ifndef OPENMS_DATASTRUCTURES_MATRIX_H
#define OPENMS_DATASTRUCTURES_MATRIX_H

#include <OpenMS/CONCEPT/Macros.h>

#include <cmath> // pow()
#include <iomanip>
#include <vector>

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

namespace OpenMS
{

  /**
    @brief A two-dimensional matrix.  Similar to std::vector, but uses a binary
    operator(,) for element access.

    Think of it as a random access container.  You can also generate gray
    scale images.  This data structure is not designed to be used for linear algebra,
    but rather a simple two-dimensional array.

    The following member functions of the base class std::vector<ValueType>
    can also be used:

begin

end

rbegin

rend

front

back

assign

empty

size

capacity

max_size

         @ingroup Datastructures
  */
  template <typename Value>
  class Matrix :
    protected std::vector<Value>
  {
protected:
    typedef std::vector<Value> Base;

public:

    ///@name STL compliance type definitions
    //@{
    typedef Base container_type;

    typedef typename Base::difference_type difference_type;
    typedef typename Base::size_type size_type;

    typedef typename Base::const_iterator const_iterator;
    typedef typename Base::const_reverse_iterator const_reverse_iterator;
    typedef typename Base::iterator iterator;
    typedef typename Base::reverse_iterator reverse_iterator;

    typedef typename Base::const_reference const_reference;
    typedef typename Base::pointer pointer;
    typedef typename Base::reference reference;
    typedef typename Base::value_type value_type;

    typedef typename Base::allocator_type allocator_type;
    //@}

    ///@name OpenMS compliance type definitions
    //@{
    typedef Base ContainerType;
    typedef difference_type DifferenceType;
    typedef size_type SizeType;

    typedef const_iterator ConstIterator;
    typedef const_reverse_iterator ConstReverseIterator;
    typedef iterator Iterator;
    typedef reverse_iterator ReverseIterator;

    typedef const_reference ConstReference;
    typedef pointer Pointer;
    typedef reference Reference;
    typedef value_type ValueType;

    typedef allocator_type AllocatorType;
    //@}

    ///@name Constructors, assignment, and destructor
    //@{
    Matrix() :
      Base(),
      rows_(0),
      cols_(0)
    {}

    Matrix(const SizeType rows, const SizeType cols, ValueType value = ValueType()) :
      Base(rows * cols, value),
      rows_(rows),
      cols_(cols)
    {}

    Matrix(const Matrix& source) :
      Base(source),
      rows_(source.rows_),
      cols_(source.cols_)
    {}

    Matrix& operator=(const Matrix& rhs)
    {
      Base::operator=(rhs);
      rows_ = rhs.rows_;
      cols_ = rhs.cols_;
      return *this;
    }

    ~Matrix() {}
    //@}

    ///@name Accessors
    //@{
    const_reference operator()(size_type const i, size_type const j) const
    {
      return getValue(i, j);
    }

    reference operator()(size_type const i, size_type const j)
    {
      return getValue(i, j);
    }

    const_reference getValue(size_type const i, size_type const j) const
    {
      return Base::operator[](index(i, j));
    }

    reference getValue(size_type const i, size_type const j)
    {
      return Base::operator[](index(i, j));
    }

    void setValue(size_type const i, size_type const j, value_type value)
    {
      Base::operator[](index(i, j)) = value;
    }

    /// Return the i-th row of the matrix as a vector.
    container_type row(size_type const i) const
    {
#ifdef OPENMS_DEBUG
      if (i >= rows_) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, i, rows_);
#endif
      container_type values(cols_);
      for (size_type j = 0; j < cols_; j++)
      {
        values[j] = Base::operator[](index(i, j));
      }
      return values;
    }

    /// Return the i-th column of the matrix as a vector.
    container_type col(size_type const i) const
    {
#ifdef OPENMS_DEBUG
      if (i >= cols_) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, i, cols_);
#endif
      container_type values(rows_);
      for (size_type j = 0; j < rows_; j++)
      {
        values[j] = Base::operator[](index(j, i));
      }
      return values;
    }

    //@}

    /**
      @name Pure access declarations

      These make begin(), end() etc. from container_type accessible.
    */
    //@{
public:

    using Base::begin;
    using Base::end;
    using Base::rbegin;
    using Base::rend;

    using Base::front;
    using Base::back;
    using Base::assign;

    using Base::empty;
    using Base::size;

    using Base::capacity;
    using Base::max_size;

    //@}

    void clear()
    {
      Base::clear();
      rows_ = 0;
      cols_ = 0;
    }

    void resize(size_type i, size_type j, value_type value = value_type())
    {
      rows_ = i;
      cols_ = j;
      Base::resize(rows_ * cols_, value);
    }

    void resize(std::pair<Size, Size> const& size_pair, value_type value = value_type())
    {
      rows_ = size_pair.first;
      cols_ = size_pair.second;
      Base::resize(rows_ * cols_, value);
    }

    /// Number of rows
    SizeType rows() const
    {
      return rows_;
    }

    /// Number of columns
    SizeType cols() const
    {
      return cols_;
    }

    std::pair<Size, Size> sizePair() const
    {
      return std::pair<Size, Size>(rows_, cols_);
    }

    /**
      @brief Calculate the index into the underlying vector from row and column.
      Note that Matrix uses the (row,column) lexicographic ordering for indexing.
    */
    SizeType const index(SizeType row, SizeType col) const
    {
#ifdef OPENMS_DEBUG
      if (row >= rows_) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, row, rows_);
      if (col >= cols_) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, col, cols_);
#endif
      return row * cols_ + col;
    }

    /**
      @brief Calculate the row and column from an index into the underlying vector.
      Note that Matrix uses the (row,column) lexicographic ordering for indexing.
    */
    std::pair<Size, Size> const indexPair(Size index) const
    {
#ifdef OPENMS_DEBUG
      if (index >= size()) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, index, size() - 1);
#endif
      return std::pair<SizeType, SizeType>(index / cols_, index % cols_);
    }

    /**
      @brief Calculate the column from an index into the underlying vector.
      Note that Matrix uses the (row,column) lexicographic ordering for indexing.
    */
    SizeType colIndex(SizeType index) const
    {
#ifdef OPENMS_DEBUG
      if (index >= size()) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, index, size() - 1);
#endif
      return index % cols_;
    }

    /**
      @brief Calculate the row from an index into the underlying vector.
      Note that Matrix uses the (row,column) lexicographic ordering for indexing.
    */
    SizeType rowIndex(SizeType index) const
    {
#ifdef OPENMS_DEBUG
      if (index >= size()) throw Exception::IndexOverflow(__FILE__, __LINE__, __PRETTY_FUNCTION__, index, size() - 1);
#endif

      return index / cols_;
    }

    /**
      @brief Equality comparator.

      If matrices have different row or column numbers, throws a precondition exception.
    */
    bool operator==(Matrix const& rhs) const
    {
      OPENMS_PRECONDITION(cols_ == rhs.cols_,
                          "Matrices have different row sizes.");
      OPENMS_PRECONDITION(rows_ == rhs.rows_,
                          "Matrices have different column sizes.");
      return static_cast<typename Matrix<Value>::Base const&>(*this) == static_cast<typename Matrix<Value>::Base const&>(rhs);
    }

    /**
      @brief Less-than comparator.  Comparison is done lexicographically: first by row, then by column.

      If matrices have different row or column numbers, throws a precondition exception.
    */
    bool operator<(Matrix const& rhs) const
    {
      OPENMS_PRECONDITION(cols_ == rhs.cols_,
                          "Matrices have different row sizes.");
      OPENMS_PRECONDITION(rows_ == rhs.rows_,
                          "Matrices have different column sizes.");
      return static_cast<typename Matrix<Value>::Base const&>(*this) < static_cast<typename Matrix<Value>::Base const&>(rhs);
    }

    /// set matrix to 2D arrays values
    template <int ROWS, int COLS>
    void setMatrix(const ValueType matrix[ROWS][COLS])
    {
      resize(ROWS, COLS);
      for (SizeType i = 0; i < this->rows_; ++i)
      {
        for (SizeType j = 0; j < this->cols_; ++j)
        {
          setValue(i, j, matrix[i][j]);
        }
      }
    }

    /**
      @brief create gsl_matrix*

      allocate and return an equivalent GSL matrix
      @note Works only for Matrix<double> and Matrix<float>
      @note Clean up the gsl_matrix using gsl_matrix_free (gsl_matrix * m)
    */
    gsl_matrix* toGslMatrix()
    {
      gsl_matrix* m_ptr = gsl_matrix_alloc(rows_, cols_);

      for (size_type i = 0; i < this->rows_; ++i)
      {
        for (size_type j = 0; j < this->cols_; ++j)
        {
          gsl_matrix_set(m_ptr, i, j, (double) (*this)(i, j));
        }
      }

      return m_ptr;
    }

protected:

    ///@name Data members
    //@{
    /// Number of rows (height of a column)
    SizeType rows_;
    /// Number of columns (width of a row)
    SizeType cols_;
    //@}

  }; // class Matrix

  // template<> OPENMS_DLLAPI gsl_matrix* Matrix<double>::toGslMatrix();
  // template<> OPENMS_DLLAPI gsl_matrix* Matrix<float>::toGslMatrix();

  /**
    @brief Print the contents to a stream.

    @relatesalso Matrix
  */
  template <typename Value>
  std::ostream& operator<<(std::ostream& os, const Matrix<Value>& matrix)
  {
    typedef typename Matrix<Value>::size_type size_type;
    for (size_type i = 0; i < matrix.rows(); ++i)
    {
      for (size_type j = 0; j < matrix.cols(); ++j)
      {
        os << std::setprecision(6) << std::setw(6) << matrix(i, j) << ' ';
      }
      os << std::endl;
    }
    return os;
  }

} // namespace OpenMS

#endif // OPENMS_DATASTRUCTURES_MATRIX_H


#ifndef __GSL_PERMUTE_MATRIX_H__
#define __GSL_PERMUTE_MATRIX_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

#include <gsl/gsl_permute_matrix_complex_long_double.h>
#include <gsl/gsl_permute_matrix_complex_double.h>
#include <gsl/gsl_permute_matrix_complex_float.h>

#include <gsl/gsl_permute_matrix_long_double.h>
#include <gsl/gsl_permute_matrix_double.h>
#include <gsl/gsl_permute_matrix_float.h>

#include <gsl/gsl_permute_matrix_ulong.h>
#include <gsl/gsl_permute_matrix_long.h>

#include <gsl/gsl_permute_matrix_uint.h>
#include <gsl/gsl_permute_matrix_int.h>

#include <gsl/gsl_permute_matrix_ushort.h>
#include <gsl/gsl_permute_matrix_short.h>

#include <gsl/gsl_permute_matrix_uchar.h>
#include <gsl/gsl_permute_matrix_char.h>

#endif /* __GSL_PERMUTE_MATRIX_H__ */


#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_math.h>

#include "allvars.h"
#include "proto.h"

#ifdef MPISENDRECV_CHECKSUM

#undef MPI_Sendrecv

int MPI_Check_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype,
		       int dest, int sendtag, void *recvbufreal, int recvcount,
		       MPI_Datatype recvtype, int source, int recvtag, MPI_Comm comm, MPI_Status * status)
{
  int checksumtag = 1000, errtag = 2000;
  int i, iter = 0, err_flag, err_flag_imported, size_sendtype, size_recvtype;
  long long sendCheckSum, recvCheckSum, importedCheckSum;
  unsigned char *p, *buf, *recvbuf;

  if(dest != source)
    endrun(3);

  MPI_Type_size(sendtype, &size_sendtype);
  MPI_Type_size(recvtype, &size_recvtype);

  if(dest == ThisTask)
    {
      memcpy(recvbufreal, sendbuf, recvcount * size_recvtype);
      return 0;
    }

  if(!(buf = mymalloc(recvcount * size_recvtype + 1024)))
    endrun(6);

  for(i = 0, p = buf; i < recvcount * size_recvtype + 1024; i++)
    *p++ = 255;

  recvbuf = buf + 512;

  MPI_Sendrecv(sendbuf, sendcount, sendtype, dest, sendtag,
	       recvbuf, recvcount, recvtype, source, recvtag, comm, status);

  for(i = 0, p = buf; i < 512; i++, p++)
    {
      if(*p != 255)
	{
	  printf
	    ("MPI-ERROR: Task=%d/%s: Recv occured before recv buffer. message-size=%d from %d, i=%d c=%d\n",
	     ThisTask, getenv("HOST"), recvcount, dest, i, *p);
	  fflush(stdout);
	  endrun(6);
	}
    }

  for(i = 0, p = recvbuf + recvcount * size_recvtype; i < 512; i++, p++)
    {
      if(*p != 255)
	{
	  printf
	    ("MPI-ERROR: Task=%d/%s: Recv occured after recv buffer. message-size=%d from %d, i=%d c=%d\n",
	     ThisTask, getenv("HOST"), recvcount, dest, i, *p);
	  fflush(stdout);
	  endrun(6);
	}
    }

  for(i = 0, p = sendbuf, sendCheckSum = 0; i < sendcount * size_sendtype; i++, p++)
    sendCheckSum += *p;

  importedCheckSum = 0;

  if(dest > ThisTask)
    {
      if(sendcount > 0)
	MPI_Ssend(&sendCheckSum, sizeof(sendCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD);
      if(recvcount > 0)
	MPI_Recv(&importedCheckSum, sizeof(importedCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD,
		 status);
    }
  else
    {
      if(recvcount > 0)
	MPI_Recv(&importedCheckSum, sizeof(importedCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD,
		 status);
      if(sendcount > 0)
	MPI_Ssend(&sendCheckSum, sizeof(sendCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD);
    }

  checksumtag++;

  for(i = 0, p = recvbuf, recvCheckSum = 0; i < recvcount * size_recvtype; i++, p++)
    recvCheckSum += *p;

  err_flag = err_flag_imported = 0;

  if(recvCheckSum != importedCheckSum)
    {
      printf
	("MPI-ERROR: Receive error on task=%d/%s from task=%d, message size=%d, sendcount=%d checksums= %d %d  %d %d. Try to fix it...\n",
	 ThisTask, getenv("HOST"), source, recvcount, sendcount, (int) (recvCheckSum >> 32),
	 (int) recvCheckSum, (int) (importedCheckSum >> 32), (int) importedCheckSum);
      fflush(stdout);

      err_flag = 1;
    }

  if(dest > ThisTask)
    {
      MPI_Ssend(&err_flag, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD);
      MPI_Recv(&err_flag_imported, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD, status);
    }
  else
    {
      MPI_Recv(&err_flag_imported, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD, status);
      MPI_Ssend(&err_flag, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD);
    }
  errtag++;

  if(err_flag > 0 || err_flag_imported > 0)
    {
      printf("Task=%d is on %s, wants to send %d and has checksum=%d %d of send data\n",
	     ThisTask, getenv("HOST"), sendcount, (int) (sendCheckSum >> 32), (int) sendCheckSum);
      fflush(stdout);

      do
	{
	  sendtag++;
	  recvtag++;

	  for(i = 0, p = recvbuf; i < recvcount * size_recvtype; i++, p++)
	    *p = 0;

	  if((iter & 1) == 0)
	    {
	      if(dest > ThisTask)
		{
		  if(sendcount > 0)
		    MPI_Ssend(sendbuf, sendcount, sendtype, dest, sendtag, MPI_COMM_WORLD);
		  if(recvcount > 0)
		    MPI_Recv(recvbuf, recvcount, recvtype, dest, recvtag, MPI_COMM_WORLD, status);
		}
	      else
		{
		  if(recvcount > 0)
		    MPI_Recv(recvbuf, recvcount, recvtype, dest, recvtag, MPI_COMM_WORLD, status);
		  if(sendcount > 0)
		    MPI_Ssend(sendbuf, sendcount, sendtype, dest, sendtag, MPI_COMM_WORLD);
		}
	    }
	  else
	    {
	      if(iter > 5)
		{
		  printf("we're trying to send each byte now on task=%d (iter=%d)\n", ThisTask, iter);
		  fflush(stdout);
		  if(dest > ThisTask)
		    {
		      for(i = 0, p = sendbuf; i < sendcount * size_sendtype; i++, p++)
			MPI_Ssend(p, 1, MPI_BYTE, dest, i, MPI_COMM_WORLD);
		      for(i = 0, p = recvbuf; i < recvcount * size_recvtype; i++, p++)
			MPI_Recv(p, 1, MPI_BYTE, dest, i, MPI_COMM_WORLD, status);
		    }
		  else
		    {
		      for(i = 0, p = recvbuf; i < recvcount * size_recvtype; i++, p++)
			MPI_Recv(p, 1, MPI_BYTE, dest, i, MPI_COMM_WORLD, status);
		      for(i = 0, p = sendbuf; i < sendcount * size_sendtype; i++, p++)
			MPI_Ssend(p, 1, MPI_BYTE, dest, i, MPI_COMM_WORLD);
		    }
		}
	      else
		{
		  MPI_Sendrecv(sendbuf, sendcount, sendtype, dest, sendtag,
			       recvbuf, recvcount, recvtype, source, recvtag, comm, status);
		}
	    }

	  importedCheckSum = 0;

	  for(i = 0, p = sendbuf, sendCheckSum = 0; i < sendcount * size_sendtype; i++, p++)
	    sendCheckSum += *p;

	  printf("Task=%d gas send_checksum=%d %d\n", ThisTask, (int) (sendCheckSum >> 32),
		 (int) sendCheckSum);
	  fflush(stdout);

	  if(dest > ThisTask)
	    {
	      if(sendcount > 0)
		MPI_Ssend(&sendCheckSum, sizeof(sendCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD);
	      if(recvcount > 0)
		MPI_Recv(&importedCheckSum, sizeof(importedCheckSum), MPI_BYTE, dest, checksumtag,
			 MPI_COMM_WORLD, status);
	    }
	  else
	    {
	      if(recvcount > 0)
		MPI_Recv(&importedCheckSum, sizeof(importedCheckSum), MPI_BYTE, dest, checksumtag,
			 MPI_COMM_WORLD, status);
	      if(sendcount > 0)
		MPI_Ssend(&sendCheckSum, sizeof(sendCheckSum), MPI_BYTE, dest, checksumtag, MPI_COMM_WORLD);
	    }

	  for(i = 0, p = recvbuf, recvCheckSum = 0; i < recvcount; i++, p++)
	    recvCheckSum += *p;

	  err_flag = err_flag_imported = 0;

	  if(recvCheckSum != importedCheckSum)
	    {
	      printf
		("MPI-ERROR: Again (iter=%d) a receive error on task=%d/%s from task=%d, message size=%d, checksums= %d %d  %d %d. Try to fix it...\n",
		 iter, ThisTask, getenv("HOST"), source, recvcount, (int) (recvCheckSum >> 32),
		 (int) recvCheckSum, (int) (importedCheckSum >> 32), (int) importedCheckSum);
	      fflush(stdout);
	      err_flag = 1;
	    }

	  if(dest > ThisTask)
	    {
	      MPI_Ssend(&err_flag, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD);
	      MPI_Recv(&err_flag_imported, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD, status);
	    }
	  else
	    {
	      MPI_Recv(&err_flag_imported, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD, status);
	      MPI_Ssend(&err_flag, 1, MPI_INT, dest, errtag, MPI_COMM_WORLD);
	    }

	  if(err_flag == 0 && err_flag_imported == 0)
	    break;

	  errtag++;
	  checksumtag++;
	  iter++;
	}
      while(iter < 10);

      if(iter >= 10)
	{
	  char buf[1000];
	  int length;
	  FILE *fd;

	  sprintf(buf, "send_data_%d.dat", ThisTask);
	  fd = fopen(buf, "w");
	  length = sendcount * size_sendtype;
	  fwrite(&length, 1, sizeof(int), fd);
	  fwrite(sendbuf, sendcount, size_sendtype, fd);
	  fclose(fd);

	  sprintf(buf, "recv_data_%d.dat", ThisTask);
	  fd = fopen(buf, "w");
	  length = recvcount * size_recvtype;
	  fwrite(&length, 1, sizeof(int), fd);
	  fwrite(recvbuf, recvcount, size_recvtype, fd);
	  fclose(fd);

	  printf("MPI-ERROR: Even 10 trials proved to be insufficient on task=%d/%s. Stopping\n", ThisTask,
		 getenv("HOST"));
	  fflush(stdout);
	  endrun(10);
	}
    }

  memcpy(recvbufreal, recvbuf, recvcount * size_recvtype);

  myfree(buf);

  return 0;
}

#endif


#include "quac_p.h"
#include "quac.h"
#include "operators_p.h"
#include "operators.h"
#include <petsc.h>

int petsc_initialized = 0;
int nid;
int np;

/*
 * QuaC_initialize initializes petsc, gets each core's nid, and lets the
 * rest of the program know that it has been initialized.
 * Inputs:
 *       int argc, char **args - command line input, for PETSc
 */
void QuaC_initialize(int argc,char **args){

  /* Initialize Petsc */
  PetscInitialize(&argc,&args,(char*)0,NULL);
#if !defined(PETSC_USE_COMPLEX)
  SETERRQ(PETSC_COMM_WORLD,1,"This example requires complex numbers");
#endif
  /* Get core's id */
  MPI_Comm_rank(PETSC_COMM_WORLD,&nid);
  /* Get number of processors */
  MPI_Comm_size(PETSC_COMM_WORLD,&np);

  petsc_initialized = 1;
  PetscLogStageRegister("Pre-solve",&pre_solve_stage);
  PetscLogStageRegister("Solve",&solve_stage);
  PetscLogStageRegister("Post-solve",&post_solve_stage);
  /* Register events */
  PetscClassIdRegister("QuaC Class",&quac_class_id);
  PetscLogEventRegister("add_lin",quac_class_id,&add_lin_event);
  PetscLogEventRegister("add_to_ham",quac_class_id,&add_to_ham_event);
  PetscLogEventRegister("add_lin_recovery",quac_class_id,&add_lin_recovery_event);
  PetscLogEventRegister("_qc_event",quac_class_id,&_qc_event_function_event);
  PetscLogEventRegister("_qc_postevent",quac_class_id,&_qc_postevent_function_event);
  PetscLogEventRegister("_apply_gate",quac_class_id,&_apply_gate_event);

  PetscLogStagePush(pre_solve_stage);

}

/*
 * QuaC_clear clears the internal state of many of QuaC's
 * variables so that multiple systems can be run in one file.
 */

void QuaC_clear(){
  int i;
  /* Destroy Matrix */
  MatDestroy(&full_A);
  MatDestroy(&ham_A);
  MatDestroy(&full_stiff_A);
  MatDestroy(&ham_stiff_A);

  for (i=0;i<_num_time_dep;i++){
    MatDestroy(&_time_dep_list[i].mat);
  }
  //stab_added       = 0;
  _print_dense_ham = 0;
  _num_time_dep = 0;
  op_initialized = 0;
}

/*
 * QuaC_finalize finalizes petsc and destroys full_A.
 * The user is responsible for freeing all of the objects
 * using destroy_*
 */

void QuaC_finalize(){
  int i;
  /* Destroy Matrix */
  MatDestroy(&full_A);
  MatDestroy(&ham_A);
  MatDestroy(&full_stiff_A);
  MatDestroy(&ham_stiff_A);

  for (i=0;i<_num_time_dep;i++){
    MatDestroy(&_time_dep_list[i].mat);
  }
  /* Finalize Petsc */
  PetscLogStagePop();
  PetscFinalize();
  return;
}

/*
 * destroy_op frees the memory from op.
 * Inputs:
 *       operator *op - pointer to operator to be freed
 */

void destroy_op(operator *op){

  free((*op)->dag);
  free((*op)->n);
  free(*op);
}

/*
 * destroy_vec frees the memory from a vec.
 * Inputs:
 *       vec_op *op - pointer to vec_op to be freed
 */

void destroy_vec(vec_op *op){
  int num_levels,i;
  num_levels = (*op)[0]->my_levels;

  /* Free each up in the array */
  for (i=0;i<num_levels;i++){
    free((*op)[i]);
  }
  free(*op);
}