#include <gsl/gsl_multiset.h>
#include <gsl/gsl_errno.h>
#include <stdio.h>

int mgsl_multiset_fwrite(const char *filename, const gsl_multiset *p)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_multiset_fwrite(fp, p) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_multiset_fread(const char *filename, gsl_multiset *p)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_multiset_fread(fp, p) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_multiset_fprintf(const char *filename, const gsl_multiset *p, const char *format)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_multiset_fprintf(fp, p, format) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_multiset_fscanf(const char *filename, gsl_multiset *p)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_multiset_fscanf(fp, p) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}


///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef BITVEC_H
#define BITVEC_H

#include <gsl/gsl_assert> // for Expects
#include <gsl/gsl_util>   // for narrow_cast, narrow

using gsl::narrow_cast;

#include <cstddef>  // for ptrdiff_t, size_t, nullptr_t
#include <iterator> // for reverse_iterator, distance, random_access_...
#include <memory>

#ifdef _MSC_VER
#pragma warning(push)

// turn off some warnings that are noisy about our Expects statements
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code

// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool.
#pragma warning(                                                               \
    disable : 26495) // uninitalized member when constructor calls constructor
#pragma warning(                                                               \
    disable : 26446) // parser bug does not allow attributes on some templates

#endif // _MSC_VER

namespace gb {

template <std::ptrdiff_t Extent> class bitvec {
  public:
	// constants and types
	using element_type = unsafe_bit_t;
	using value_type = std::remove_cv_t<element_type>;
	using index_type = std::ptrdiff_t;
	using pointer = shared_ptr_unsynchronized<element_type>;

	using iterator = details::span_iterator<bitvec<Extent>, false>;
	using const_iterator = details::span_iterator<bitvec<Extent>, true>;
	using reverse_iterator = std::reverse_iterator<iterator>;
	using const_reverse_iterator = std::reverse_iterator<const_iterator>;

	using size_type = index_type;

	// [bitvec.cons], bitvec constructors, copy, assignment, and destructor
	template <bool Dependent = false,
	          // "Dependent" is needed to make "std::enable_if_t<Dependent ||
	          // Extent <= 0>" SFINAE, since "std::enable_if_t<Extent <= 0>" is
	          // ill-formed when Extent is greater than 0.
	          class = std::enable_if_t<(Dependent || Extent <= 0)>>
	bitvec() noexcept : storage_(nullptr, details::extent_type<0>()) {};

	bitvec(pointer ptr, index_type count) : storage_(ptr, count) {}

	bitvec(const bitvec &other) noexcept = default;

	template <std::ptrdiff_t OtherExtent,
	          class = std::enable_if_t<OtherExtent == Extent ||
	                                   Extent == dynamic_extent>>
	bitvec(const bitvec<OtherExtent> &other)
	    : storage_(other.data(),
	               details::extent_type<OtherExtent>(other.size())) {}

	~bitvec() noexcept = default;
	bitvec &operator=(const bitvec &other) noexcept = default;
	inline void operator=(bool value) {
		write(*this, value);
	};

	template <std::ptrdiff_t OtherExtent>
	void operator&=(const bitvec<OtherExtent> &other) noexcept {
		static_assert(Extent == OtherExtent || Extent == dynamic_extent || OtherExtent == dynamic_extent,
			"Mismatching operand sizes");
		_and(*this, *this, other);
	};
	template <std::ptrdiff_t OtherExtent>
	void operator|=(const bitvec<OtherExtent> &other) noexcept {
		static_assert(Extent == OtherExtent || Extent == dynamic_extent || OtherExtent == dynamic_extent,
			"Mismatching operand sizes");
		_or(*this, *this, other);
	};
	template <std::ptrdiff_t OtherExtent>
	void operator^=(const bitvec<OtherExtent> &other) noexcept {
		static_assert(Extent == OtherExtent || Extent == dynamic_extent || OtherExtent == dynamic_extent,
			"Mismatching operand sizes");
		_xor(*this, *this, other);
	};

	// [bitvec.sub], bitvec subviews
	template <std::ptrdiff_t Count> bitvec<Count> first() const {
		Expects(Count >= 0 && Count <= size());
		return {data(), Count};
	}

	template <std::ptrdiff_t Count = 1>
	GSL_SUPPRESS(bounds .1) // NO-FORMAT: attribute
	bitvec<Count> last() const {
		Expects(Count >= 0 && size() - Count >= 0);
		return {data_plus(size() - Count), Count};
	}

	template <std::ptrdiff_t Offset, std::ptrdiff_t Count = dynamic_extent>
	GSL_SUPPRESS(bounds .1) // NO-FORMAT: attribute
	auto subspan() const ->
	    typename details::calculate_subspan_type<Extent, Offset, Count>::type {
		Expects((Offset >= 0 && size() - Offset >= 0) &&
		        (Count == dynamic_extent ||
		         (Count >= 0 && Offset + Count <= size())));

		return {data_plus(Offset),
		        Count == dynamic_extent ? size() - Offset : Count};
	}

	template <std::ptrdiff_t Count>
	bitvec<Count> subspan(std::ptrdiff_t offset) const {
		Expects((offset >= 0 && size() - offset >= 0) &&
		        (Count == dynamic_extent ||
		         (Count >= 0 && offset + Count <= size())));

		return {data_plus(offset),
		        Count == dynamic_extent ? size() - offset : Count};
	}

	bitvec<dynamic_extent> first(index_type count) const {
		Expects(count >= 0 && count <= size());
		return {data(), count};
	}

	bitvec<dynamic_extent> last(index_type count) const {
		return make_subspan(size() - count, dynamic_extent,
		                    subspan_selector<Extent>{});
	}

	bitvec<dynamic_extent> subspan(index_type offset,
	                             index_type count = dynamic_extent) const {
		return make_subspan(offset, count, subspan_selector<Extent>{});
	}

	// [bitvec.obs], bitvec observers
	index_type size() const noexcept { return storage_.size(); }
	index_type size_bytes() const noexcept {
		return size() * narrow_cast<index_type>(sizeof(element_type));
	}
	bool empty() const noexcept { return size() == 0; }

	bitvec<1> operator[](index_type idx) const { return {data_plus(idx), 1}; }

	pointer at(index_type idx) const {
		Expects(CheckRange(idx, idx < storage_.size()));
		return data()[idx];
	}
	pointer operator()(index_type idx) const { return this->operator[](idx); }
	pointer data() const noexcept { return storage_.data(); }
	element_type *cptr() const noexcept { return storage_.data().get(); }

	// [bitvec.iter], bitvec iterator support
	iterator begin() const noexcept { return {this, 0}; }
	iterator end() const noexcept { return {this, size()}; }

	const_iterator cbegin() const noexcept { return {this, 0}; }
	const_iterator cend() const noexcept { return {this, size()}; }

	reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
	reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }

	const_reverse_iterator crbegin() const noexcept {
		return const_reverse_iterator{cend()};
	}
	const_reverse_iterator crend() const noexcept {
		return const_reverse_iterator{cbegin()};
	}

#ifdef _MSC_VER
	// Tell MSVC how to unwrap spans in range-based-for
	pointer _Unchecked_begin() const noexcept { return data(); }
	pointer _Unchecked_end() const noexcept {
		GSL_SUPPRESS(bounds .1) // NO-FORMAT: attribute
		return data() + size();
	}
#endif // _MSC_VER

  private:
	static constexpr bool CheckRange(index_type idx, index_type size) {
		// Optimization:
		//
		// idx >= 0 && idx < size
		// =>
		// static_cast<size_t>(idx) < static_cast<size_t>(size)
		//
		// because size >=0 by bitvec construction, and negative idx will
		// wrap around to a value always greater than size when casted.

		// check if we have enough space to wrap around
		if (sizeof(index_type) <= sizeof(size_t)) {
			return narrow_cast<size_t>(idx) < narrow_cast<size_t>(size);
		} else {
			return idx >= 0 && idx < size;
		}
	}

	// Needed to remove unnecessary null check in subspans
	struct KnownNotNull {
		pointer p;
	};

	// this implementation detail class lets us take advantage of the
	// empty base class optimization to pay for only storage of a single
	// pointer in the case of fixed-size spans
	template <class ExtentType> class storage_type : public ExtentType {
	  public:
		// KnownNotNull parameter is needed to remove unnecessary null check
		// in subspans and constructors from arrays
		template <class OtherExtentType>
		storage_type(KnownNotNull data, OtherExtentType ext)
		    : ExtentType(ext), data_(data.p) {
			Expects(ExtentType::size() >= 0);
		}

		template <class OtherExtentType>
		storage_type(pointer data, OtherExtentType ext)
		    : ExtentType(ext), data_(data) {
			Expects(ExtentType::size() >= 0);
			Expects(data || ExtentType::size() == 0);
		}

		pointer data() const noexcept { return data_; }

	  private:
		pointer data_;
	};

	storage_type<details::extent_type<Extent>> storage_;

	// The rest is needed to remove unnecessary null check
	// in subspans and constructors from arrays
	bitvec(KnownNotNull ptr, index_type count) : storage_(ptr, count) {}

	template <std::ptrdiff_t CallerExtent> class subspan_selector {};

	template <std::ptrdiff_t CallerExtent>
	bitvec<dynamic_extent> make_subspan(index_type offset, index_type count,
	                                  subspan_selector<CallerExtent>) const {
		const bitvec<dynamic_extent> tmp(*this);
		return tmp.subspan(offset, count);
	}

	GSL_SUPPRESS(bounds .1) // NO-FORMAT: attribute
	bitvec<dynamic_extent> make_subspan(index_type offset, index_type count,
	                                  subspan_selector<dynamic_extent>) const {
		Expects(offset >= 0 && size() - offset >= 0);

		pointer p = data_plus(offset);

		if (count == dynamic_extent) {
			return {KnownNotNull{p}, size() - offset};
		}

		Expects(count >= 0 && size() - offset >= count);
		return {KnownNotNull{p}, count};
	}

  private:
	// Implements `data() + offset` taking care of ownership.
	pointer data_plus(index_type offset) const {
		Expects(offset >= 0 && size() - offset >= 0);

		// Uses same ref counter as data()
		return pointer(data(), data().get() + offset);
	}
};

} // namespace gb

#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER

#endif // BITVEC_H


#ifndef LOGSPLINEPDF
#define LOGSPLINEPDF

#include <gsl/gsl_rng.h>

#include "splinetable.h"

#ifdef __cplusplus
extern "C" {
#endif

void logsplinepdf_n_sample(double *result, int results, int burnin,
    double *coords, int dim, struct splinetable *table, int derivatives,
    double (* proposal)(void*), double (* proposal_pdf)(double, double, void*),
    void *proposal_info, const gsl_rng *rng);

void splinepdf_n_sample(double *result, int results, int burnin,
    double *coords, int dim, struct splinetable *table, int derivatives,
    double (* proposal)(void*), double (* proposal_pdf)(double, double, void*),
    void *proposal_info, const gsl_rng *rng);

#ifdef __cplusplus
}
#endif

#endif


#pragma once

#include <gsl/gsl>
#include <string>
#include <memory>
#include <vector>
#include <string_view>

#include "aas/aas_math.h"

namespace aas {

	struct MeshVertex {
		static constexpr size_t sMaxBoneAttachments = 6;
		DX::XMFLOAT4 pos;
		DX::XMFLOAT4 normal;
		DX::XMFLOAT2 uv;
		uint16_t padding;
		uint16_t attachmentCount;
		uint16_t attachmentBone[sMaxBoneAttachments];
		float attachmentWeight[sMaxBoneAttachments];

		bool IsAttachedTo(int bone_idx) const {
			for (int i = 0; i < attachmentCount; i++) {
				if (attachmentBone[i] == bone_idx) {
					return true;
				}
			}
			return false;
		}
	};

	struct MeshFace {
		static constexpr size_t sVertexCount = 3;
		int16_t material_idx;
		int16_t vertices[sVertexCount];
	};

	struct MeshBone {
		int16_t flags;
		int16_t parent_id;
		char name[48];
		Matrix3x4 full_world_inverse;
	};

	class Mesh {
	public:
		Mesh(std::string filename, std::vector<uint8_t> data);

		const std::vector<std::string_view> &GetMaterials() const {
			return materials_;
		}

		std::string_view GetMaterial(int materialIdx) const {
			return materials_[materialIdx];
		}

		gsl::span<MeshBone> GetBones() const {
			return bones_;
		}

		const MeshBone &GetBone(int boneIdx) const {
			Expects(boneIdx >= 0 && boneIdx < bones_.size());
			return bones_[boneIdx];
		}

		gsl::span<MeshVertex> GetVertices() const {
			return vertices_;
		}

		const MeshVertex &GetVertex(int vertexIdx) const {
			return vertices_[vertexIdx];
		}

		const size_t GetVertexCount() const {
			return vertices_.size();
		}

		MeshVertex &GetVertex(int vertexIdx) {
			return vertices_[vertexIdx];
		}

		gsl::span<MeshFace> GetFaces() const {
			return faces_;
		}

		const MeshFace &GetFace(int faceIdx) const {
			return faces_[faceIdx];
		}

		void RenormalizeClothVertices(int clothBoneId);

		const std::string &GetFilename() const {
			return filename_;
		}

	private:
		std::string filename_;
		std::vector<uint8_t> data_;
		std::vector<std::string_view> materials_;

		// These are views into the data buffer
		gsl::span<MeshBone> bones_;
		gsl::span<MeshVertex> vertices_;
		gsl::span<MeshFace> faces_;
	};

	std::unique_ptr<Mesh> LoadMeshFile(std::string_view filename);

}


#include <bindings.cmacros.h>
#include <gsl/gsl_multiroots.h>

BC_INLINE3(GSL_MULTIROOT_FN_EVAL,gsl_multiroot_function*,gsl_vector*,gsl_vector*,int)
BC_INLINE3(GSL_MULTIROOT_FN_EVAL_F,gsl_multiroot_function_fdf*,gsl_vector*,gsl_vector*,int)
BC_INLINE3(GSL_MULTIROOT_FN_EVAL_DF,gsl_multiroot_function_fdf*,gsl_vector*,gsl_matrix*,int)
BC_INLINE4(GSL_MULTIROOT_FN_EVAL_F_DF,gsl_multiroot_function_fdf*,gsl_vector*,gsl_vector*,gsl_matrix*,int)


#pragma once

#include <gsl.h>

#include "types/MT3_MathChar.h"
#include "types/MT3_Radical.h"
#include "types/MT3_Accent.h"
#include "types/MT3_VCenter.h"
#include "types/MT3_Overline.h"
#include "types/MT3_Underline.h"
#include "types/MT3_GenFraction.h"
#include "types/MT3_LeftRight.h"
#include "types/MT3_Script.h"
#include "types/MT3_BigOp.h"
#include "types/MT3_SubBox.h"
#include "types/MT3_MList.h"
#include "types/MT3_Kind.h"
#include "types/MT3_MPen.h"
#include "types/MT3_MSpace.h"
#include "types/MT3_Style.h"
#include "types/MT3_Choice.h"


#include <gsl/gsl_chebyshev.h>
const struct _GSLMethods
chebyshev_f   = { (void_m_t) gsl_cheb_free,
		  /* gsl_multimin_fminimizer_restart */  (void_m_t) NULL,
		  NULL, NULL};

static int
PyGSL_cheb_init(PyGSL_Solver *self, PyObject *args, PyObject *kw)
{
     ;
}


#ifndef UTILS_STRINGS_ONE_AWAY_H_INCLUDED
#define UTILS_STRINGS_ONE_AWAY_H_INCLUDED

#include <string> // std::string
#include <gsl/gsl>

bool isOneAway(const std::string &, const std::string &);

#endif


#include <bindings.cmacros.h>
#include <gsl/gsl_odeiv2.h>

BC_INLINE4(GSL_ODEIV_FN_EVAL,gsl_odeiv2_system*,double,double*,double*,int)
BC_INLINE5(GSL_ODEIV_JA_EVAL,gsl_odeiv2_system*,double,double*,double*,double*,int)


/* vector/gsl_vector_float.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_VECTOR_FLOAT_H__
#define __GSL_VECTOR_FLOAT_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_block_float.h>

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

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  float *data;
  gsl_block_float *block;
  int owner;
}
gsl_vector_float;

typedef struct
{
  gsl_vector_float vector;
} _gsl_vector_float_view;

typedef _gsl_vector_float_view gsl_vector_float_view;

typedef struct
{
  gsl_vector_float vector;
} _gsl_vector_float_const_view;

typedef const _gsl_vector_float_const_view gsl_vector_float_const_view;

/* Allocation */

GSL_FUN gsl_vector_float *gsl_vector_float_alloc (const size_t n);
GSL_FUN gsl_vector_float *gsl_vector_float_calloc (const size_t n);

GSL_FUN gsl_vector_float *gsl_vector_float_alloc_from_block (gsl_block_float * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_FUN gsl_vector_float *gsl_vector_float_alloc_from_vector (gsl_vector_float * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_FUN void gsl_vector_float_free (gsl_vector_float * v);

/* Views */

GSL_FUN _gsl_vector_float_view
gsl_vector_float_view_array (float *v, size_t n);

GSL_FUN _gsl_vector_float_view
gsl_vector_float_view_array_with_stride (float *base,
                                         size_t stride,
                                         size_t n);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_float_const_view_array (const float *v, size_t n);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_float_const_view_array_with_stride (const float *base,
                                               size_t stride,
                                               size_t n);

GSL_FUN _gsl_vector_float_view
gsl_vector_float_subvector (gsl_vector_float *v,
                            size_t i,
                            size_t n);

GSL_FUN _gsl_vector_float_view
gsl_vector_float_subvector_with_stride (gsl_vector_float *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_float_const_subvector (const gsl_vector_float *v,
                                  size_t i,
                                  size_t n);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_float_const_subvector_with_stride (const gsl_vector_float *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_FUN void gsl_vector_float_set_zero (gsl_vector_float * v);
GSL_FUN void gsl_vector_float_set_all (gsl_vector_float * v, float x);
GSL_FUN int gsl_vector_float_set_basis (gsl_vector_float * v, size_t i);

GSL_FUN int gsl_vector_float_fread (FILE * stream, gsl_vector_float * v);
GSL_FUN int gsl_vector_float_fwrite (FILE * stream, const gsl_vector_float * v);
GSL_FUN int gsl_vector_float_fscanf (FILE * stream, gsl_vector_float * v);
GSL_FUN int gsl_vector_float_fprintf (FILE * stream, const gsl_vector_float * v,
                              const char *format);

GSL_FUN int gsl_vector_float_memcpy (gsl_vector_float * dest, const gsl_vector_float * src);

GSL_FUN int gsl_vector_float_reverse (gsl_vector_float * v);

GSL_FUN int gsl_vector_float_swap (gsl_vector_float * v, gsl_vector_float * w);
GSL_FUN int gsl_vector_float_swap_elements (gsl_vector_float * v, const size_t i, const size_t j);

GSL_FUN float gsl_vector_float_max (const gsl_vector_float * v);
GSL_FUN float gsl_vector_float_min (const gsl_vector_float * v);
GSL_FUN void gsl_vector_float_minmax (const gsl_vector_float * v, float * min_out, float * max_out);

GSL_FUN size_t gsl_vector_float_max_index (const gsl_vector_float * v);
GSL_FUN size_t gsl_vector_float_min_index (const gsl_vector_float * v);
GSL_FUN void gsl_vector_float_minmax_index (const gsl_vector_float * v, size_t * imin, size_t * imax);

GSL_FUN int gsl_vector_float_add (gsl_vector_float * a, const gsl_vector_float * b);
GSL_FUN int gsl_vector_float_sub (gsl_vector_float * a, const gsl_vector_float * b);
GSL_FUN int gsl_vector_float_mul (gsl_vector_float * a, const gsl_vector_float * b);
GSL_FUN int gsl_vector_float_div (gsl_vector_float * a, const gsl_vector_float * b);
GSL_FUN int gsl_vector_float_scale (gsl_vector_float * a, const float x);
GSL_FUN int gsl_vector_float_add_constant (gsl_vector_float * a, const double x);
GSL_FUN int gsl_vector_float_axpby (const float alpha, const gsl_vector_float * x, const float beta, gsl_vector_float * y);
GSL_FUN float gsl_vector_float_sum (const gsl_vector_float * a);

GSL_FUN int gsl_vector_float_equal (const gsl_vector_float * u,
                            const gsl_vector_float * v);

GSL_FUN int gsl_vector_float_isnull (const gsl_vector_float * v);
GSL_FUN int gsl_vector_float_ispos (const gsl_vector_float * v);
GSL_FUN int gsl_vector_float_isneg (const gsl_vector_float * v);
GSL_FUN int gsl_vector_float_isnonneg (const gsl_vector_float * v);

GSL_FUN INLINE_DECL float gsl_vector_float_get (const gsl_vector_float * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_float_set (gsl_vector_float * v, const size_t i, float x);
GSL_FUN INLINE_DECL float * gsl_vector_float_ptr (gsl_vector_float * v, const size_t i);
GSL_FUN INLINE_DECL const float * gsl_vector_float_const_ptr (const gsl_vector_float * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
float
gsl_vector_float_get (const gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

INLINE_FUN
void
gsl_vector_float_set (gsl_vector_float * v, const size_t i, float x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

INLINE_FUN
float *
gsl_vector_float_ptr (gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (float *) (v->data + i * v->stride);
}

INLINE_FUN
const float *
gsl_vector_float_const_ptr (const gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const float *) (v->data + i * v->stride);
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_FLOAT_H__ */


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

#include <mpi.h>
#include <petsc.h>
#include <petscvec.h>
#include <petscsnes.h>
#include <Python.h>
#include <StGermain/libStGermain/src/StGermain.h>
#include <StgDomain/libStgDomain/src/StgDomain.h>
#include <StgFEM/libStgFEM/src/StgFEM.h>
#include "StgFEM/Discretisation/src/Discretisation.h"

#include "types.h"

#include "SystemLinearEquations.h"
#include "SLE_Solver.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "StiffnessMatrix.h"
#include "SolutionVector.h"
#include "ForceVector.h"
#include "FiniteElementContext.h"

/* Textual name of this class */
const Type SystemLinearEquations_Type = "SystemLinearEquations";

/** Constructor */
SystemLinearEquations* SystemLinearEquations_New(
   Name                    name,
   FiniteElementContext*   context,
   SLE_Solver*             solver,
   void*                   nlSolver,
   Bool                    isNonLinear,
   double                  nonLinearTolerance,
   Iteration_Index         nonLinearMinIterations,
   Iteration_Index         nonLinearMaxIterations,
   Bool                    killNonConvergent,
   EntryPoint_Register*    entryPoint_Register,
   MPI_Comm                comm )
{
   SystemLinearEquations* self = _SystemLinearEquations_DefaultNew( name );

   self->isConstructed = True;
   _SystemLinearEquations_Init(
      self,
      solver,
      nlSolver,
      context,
      False, /* TODO: A hack put in place for setting the convergence stream to 'off' if the SLE class is created from within the code, not via an xml */
      isNonLinear,
      nonLinearTolerance,
      nonLinearMaxIterations,
      killNonConvergent,
      nonLinearMinIterations,
      "",
      "",
      entryPoint_Register,
      comm );

   return self;
}

/* Creation implementation / Virtual constructor */
SystemLinearEquations* _SystemLinearEquations_New(  SYSTEMLINEAREQUATIONS_DEFARGS  )
{
   SystemLinearEquations* self;

   /* Allocate memory */
   assert( _sizeOfSelf >= sizeof(SystemLinearEquations) );
   /* The following terms are parameters that have been passed into this function but are being set before being passed onto the parent */
   /* This means that any values of these parameters that are passed into this function are not passed onto the parent function
      and so should be set to ZERO in any children of this class. */
   nameAllocationType = NON_GLOBAL;

   self = (SystemLinearEquations*) _Stg_Component_New(  STG_COMPONENT_PASSARGS  );

   /* Virtual info */
   self->_LM_Setup = _LM_Setup;
   self->_matrixSetup = _matrixSetup;
   self->_vectorSetup = _vectorSetup;
   self->_updateSolutionOntoNodes = _updateSolutionOntoNodes;
   self->_mgSelectStiffMats = _mgSelectStiffMats;

   self->_sleFormFunction = NULL;

   /* this guy defaults to true so that we run within the execute phase as default */
   self->runatExecutePhase = True;

   self->solver_callback = NULL;

   return self;
}

void _SystemLinearEquations_Init(
   void*                   sle,
   SLE_Solver*             solver,
   void*                   nlSolver,
   FiniteElementContext*   context,
   Bool                    makeConvergenceFile,
   Bool                    isNonLinear,
   double                  nonLinearTolerance,
   Iteration_Index         nonLinearMaxIterations,
   Bool                    killNonConvergent,
   Iteration_Index         nonLinearMinIterations,
   Name                    nonLinearSolutionType,
   Name                    optionsPrefix,
   EntryPoint_Register*    entryPoint_Register,
   MPI_Comm                comm )
{
   SystemLinearEquations*    self = (SystemLinearEquations*)sle;
   char*                     filename;
   char*                     optionsName;

   self->extensionManager = ExtensionManager_New_OfExistingObject( self->name, self );

   self->debug = Stream_RegisterChild( StgFEM_SLE_SystemSetup_Debug, self->type );
   self->info =  Journal_MyStream( Info_Type, self );
    /* Note: currently we're sending self->info to the master proc only so there's not too much
      identical timing info printed. May want to fine-tune later so that some info does get
       printed on all procs. */
   Stream_SetPrintingRank( self->info, 0 );

   self->makeConvergenceFile = makeConvergenceFile;
   if ( context && self->makeConvergenceFile ) {
      self->convergenceStream = Journal_Register( InfoStream_Type, (Name)"Convergence Info"  );
      Stg_asprintf( &filename, "Convergence.dat" );
      Stream_RedirectFile_WithPrependedPath( self->convergenceStream, context->outputPath, filename );
      Stream_SetPrintingRank( self->convergenceStream, 0 );
      Memory_Free( filename );
      Journal_Printf( self->convergenceStream , "Timestep\tIteration\tResidual\tTolerance\n" );
   }

   self->comm = comm;
   self->solver = solver;
   self->nlSolver = (SNES)nlSolver;
   self->stiffnessMatrices = Stg_ObjectList_New();
   self->forceVectors = Stg_ObjectList_New();
   self->solutionVectors = Stg_ObjectList_New();
   self->context = context;

   /* Init NonLinear Stuff */
   self->nonLinearSolutionType = nonLinearSolutionType; /* This will never got propogated through to _Initialise->SetToNonLinear if we keep it in the loop */
   if ( isNonLinear )
      SystemLinearEquations_SetToNonLinear( self, True );
   self->nonLinearTolerance        = nonLinearTolerance;
   self->nonLinearMaxIterations    = nonLinearMaxIterations;
   self->killNonConvergent         = killNonConvergent;
   self->nonLinearMinIterations    = nonLinearMinIterations;
   self->curResidual               = 0.0;
   self->curSolveTime              = 0.0;
                                /* _  /0 */
   optionsName = Memory_Alloc_Array_Unnamed( char, strlen(optionsPrefix) + 1 + 1 );
   sprintf( optionsName, "%s_", optionsPrefix );
   self->optionsPrefix = optionsName;

   /* BEGIN LUKE'S FRICTIONAL BCS BIT */
   Stg_asprintf( &self->nlSetupEPName, "%s-nlSetupEP", self->name );
   self->nlSetupEP = EntryPoint_New( self->nlSetupEPName, EntryPoint_2VoidPtr_CastType );
   Stg_asprintf( &self->nlEPName, "%s-nlEP", self->name );
   self->nlEP = EntryPoint_New( self->nlEPName, EntryPoint_2VoidPtr_CastType );
   Stg_asprintf( &self->nlEPName, "%s-postNlEP", self->name );
   self->postNlEP = EntryPoint_New( self->postNlEPName, EntryPoint_2VoidPtr_CastType );
   Stg_asprintf( &self->nlConvergedEPName, "%s-nlConvergedEP", self->name );
   self->nlConvergedEP = EntryPoint_New( self->nlConvergedEPName, EntryPoint_2VoidPtr_CastType );
   /* END LUKE'S FRICTIONAL BCS BIT */
   self->nlFormJacobian = False;
   self->nlCurIterate = PETSC_NULL;

   /* Initialise MG stuff. */
   self->mgEnabled = False;
   self->mgUpdate = True;
   self->nMGHandles = 0;
   self->mgHandles = NULL;

   /* Create Execute Entry Point */
   Stg_asprintf( &self->executeEPName, "%s-execute", self->name );
   self->executeEP = EntryPoint_New( self->executeEPName, EntryPoint_2VoidPtr_CastType );

   /* Add default hooks to Execute E.P. */
   EntryPoint_Append( self->executeEP, "BC_Setup", SystemLinearEquations_BC_Setup, self->type);
   EntryPoint_Append( self->executeEP, "LM_Setup", SystemLinearEquations_LM_Setup, self->type);
   EntryPoint_Append( self->executeEP, "IntegrationSetup", SystemLinearEquations_IntegrationSetup, self->type );
   EntryPoint_Append( self->executeEP, "ZeroAllVectors", SystemLinearEquations_ZeroAllVectors, self->type);
   EntryPoint_Append( self->executeEP, "MatrixSetup", SystemLinearEquations_MatrixSetup, self->type);
   EntryPoint_Append( self->executeEP, "VectorSetup", SystemLinearEquations_VectorSetup, self->type);
   EntryPoint_Append( self->executeEP, "ExecuteSolver", SystemLinearEquations_ExecuteSolver, self->type);
   EntryPoint_Append( self->executeEP, "UpdateSolutionOntoNodes",SystemLinearEquations_UpdateSolutionOntoNodes,self->type);

   /* Create Integration Setup EP */
   Stg_asprintf( &self->integrationSetupEPName, "%s-integrationSetup", self->name );
   self->integrationSetupEP = EntryPoint_New( self->integrationSetupEPName, EntryPoint_Class_VoidPtr_CastType );

   if ( entryPoint_Register )
      EntryPoint_Register_Add( entryPoint_Register, self->executeEP );
   self->entryPoint_Register = entryPoint_Register;

   /* Add SLE to Context */
   if ( context )
      FiniteElementContext_AddSLE( context, self );
}

void _SystemLinearEquations_Delete( void* sle ) {
   SystemLinearEquations* self = (SystemLinearEquations*)sle;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );

        Stg_Class_Delete(self->integrationSetupEP);

   /* delete parent */
   _Stg_Component_Delete( self );

   Stream_UnIndentBranch( StgFEM_Debug );
}

void _SystemLinearEquations_Print( void* sle, Stream* stream ) {
   SystemLinearEquations*      self = (SystemLinearEquations*)sle;

   /* General info */
   Journal_Printf( stream, "SystemLinearEquations (ptr): %p\n", self );
   _Stg_Component_Print( self, stream );

   /* Virtual info */
   Stg_Class_Print( self->stiffnessMatrices, stream );
   Stg_Class_Print( self->forceVectors, stream );
   Stg_Class_Print( self->solutionVectors, stream );

   /* other info */
   Journal_PrintPointer( stream, self->extensionManager );
   Journal_Printf( stream, "\tcomm: %u\n", self->comm );
   Journal_Printf( stream, "\tsolver (ptr): %p\n", self->solver );
   Stg_Class_Print( self->solver, stream );
}

void* _SystemLinearEquations_Copy( void* sle, void* dest, Bool deep, Name nameExt, PtrMap* ptrMap ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;
   SystemLinearEquations*   newSLE;
   PtrMap* map = ptrMap;
   Bool    ownMap = False;

   if( !map ) {
      map = PtrMap_New( 10 );
      ownMap = True;
   }

   newSLE = _Stg_Component_Copy( sle, dest, deep, nameExt, map );

   /* Virtual methods */
   newSLE->_LM_Setup = self->_LM_Setup;
   newSLE->_matrixSetup = self->_matrixSetup;
   newSLE->_vectorSetup = self->_vectorSetup;
   newSLE->_mgSelectStiffMats = self->_mgSelectStiffMats;

   newSLE->debug = Stream_RegisterChild( StgFEM_SLE_SystemSetup_Debug, newSLE->type );
   newSLE->comm = self->comm;

   if( deep ) {
      newSLE->solver = (SLE_Solver*)Stg_Class_Copy( self->solver, NULL, deep, nameExt, map );
      newSLE->stiffnessMatrices = (StiffnessMatrixList*)Stg_Class_Copy( self->stiffnessMatrices, NULL, deep, nameExt, map );
      newSLE->forceVectors = (ForceVectorList*)Stg_Class_Copy( self->forceVectors, NULL, deep, nameExt, map );
      newSLE->solutionVectors = (SolutionVectorList*)Stg_Class_Copy( self->solutionVectors, NULL, deep, nameExt, map );
      if( (newSLE->extensionManager = PtrMap_Find( map, self->extensionManager )) == NULL ) {
         newSLE->extensionManager = Stg_Class_Copy( self->extensionManager, NULL, deep, nameExt, map );
         PtrMap_Append( map, self->extensionManager, newSLE->extensionManager );
      }
   }
   else {
      newSLE->solver = self->solver;
      newSLE->stiffnessMatrices = self->stiffnessMatrices;
      newSLE->forceVectors = self->forceVectors;
      newSLE->solutionVectors = self->solutionVectors;
   }

   if( ownMap ) {
      Stg_Class_Delete( map );
   }

   return newSLE;
}

void* _SystemLinearEquations_DefaultNew( Name name ) {
   /* Variables set in this function */
   SizeT                                                            _sizeOfSelf = sizeof(SystemLinearEquations);
   Type                                                                    type = SystemLinearEquations_Type;
   Stg_Class_DeleteFunction*                                            _delete = _SystemLinearEquations_Delete;
   Stg_Class_PrintFunction*                                              _print = _SystemLinearEquations_Print;
   Stg_Class_CopyFunction*                                                _copy = _SystemLinearEquations_Copy;
   Stg_Component_DefaultConstructorFunction*                _defaultConstructor = _SystemLinearEquations_DefaultNew;
   Stg_Component_ConstructFunction*                                  _construct = _SystemLinearEquations_AssignFromXML;
   Stg_Component_BuildFunction*                                          _build = _SystemLinearEquations_Build;
   Stg_Component_InitialiseFunction*                                _initialise = _SystemLinearEquations_Initialise;
   Stg_Component_ExecuteFunction*                                      _execute = _SystemLinearEquations_Execute;
   Stg_Component_DestroyFunction*                                      _destroy = _SystemLinearEquations_Destroy;
   SystemLinearEquations_LM_SetupFunction*                            _LM_Setup = _SystemLinearEquations_LM_Setup;
   SystemLinearEquations_MatrixSetupFunction*                      _matrixSetup = _SystemLinearEquations_MatrixSetup;
   SystemLinearEquations_VectorSetupFunction*                      _vectorSetup = _SystemLinearEquations_VectorSetup;
   SystemLinearEquations_UpdateSolutionOntoNodesFunc*  _updateSolutionOntoNodes = _SystemLinearEquations_UpdateSolutionOntoNodes;
   SystemLinearEquations_MG_SelectStiffMatsFunc*             _mgSelectStiffMats = _SystemLinearEquations_MG_SelectStiffMats;

   /* Variables that are set to ZERO are variables that will be set either by the current _New function or another parent _New function further up the hierachy */
   AllocationType  nameAllocationType = NON_GLOBAL /* default value NON_GLOBAL */;

   return _SystemLinearEquations_New(  SYSTEMLINEAREQUATIONS_PASSARGS  );
}

void _SystemLinearEquations_AssignFromXML( void* sle, Stg_ComponentFactory* cf, void* data ){
   SystemLinearEquations*  self = (SystemLinearEquations*)sle;
   SLE_Solver*             solver = NULL;
   void*                   entryPointRegister = NULL;
   FiniteElementContext*   context = NULL;
   double                  nonLinearTolerance;
   Iteration_Index         nonLinearMaxIterations;
   Bool                    isNonLinear;
   Bool                    killNonConvergent;
   Bool                    makeConvergenceFile;
   Iteration_Index         nonLinearMinIterations;
   Name                     nonLinearSolutionType;
   SNES                    nlSolver = NULL;
   Name                     optionsPrefix;

   solver = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)SLE_Solver_Type, SLE_Solver, False, data  ) ;

   makeConvergenceFile      = Stg_ComponentFactory_GetBool( cf, self->name, (Dictionary_Entry_Key)"makeConvergenceFile", False  );
   isNonLinear               = Stg_ComponentFactory_GetBool( cf, self->name, (Dictionary_Entry_Key)"isNonLinear", False  );
   nonLinearTolerance      = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"nonLinearTolerance", 0.01  );
   nonLinearMaxIterations   = Stg_ComponentFactory_GetUnsignedInt( cf, self->name, (Dictionary_Entry_Key)"nonLinearMaxIterations", 500  );
   killNonConvergent         = Stg_ComponentFactory_GetBool( cf, self->name, (Dictionary_Entry_Key)"killNonConvergent", True  );
   nonLinearMinIterations    = Stg_ComponentFactory_GetUnsignedInt( cf, self->name, (Dictionary_Entry_Key)"nonLinearMinIterations", 1  );
   nonLinearSolutionType   = Stg_ComponentFactory_GetString( cf, self->name, (Dictionary_Entry_Key)"nonLinearSolutionType", "default"  );
   optionsPrefix            = Stg_ComponentFactory_GetString( cf, self->name, (Dictionary_Entry_Key)"optionsPrefix", ""  );

   /* Read some value for Picard */
   self->picard_form_function_type = Stg_ComponentFactory_GetString( cf, self->name, (Dictionary_Entry_Key)"picard_FormFunctionType", "PicardFormFunction_KSPResidual"  );

   self->alpha            = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"picard_alpha", 1.0  );
   self->rtol            = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"picard_rtol", 1.0e-8  );
   self->abstol         = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"picard_atol", 1.0e-50  );
   self->stol            = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"picard_stol", 1.0e-8  );
   self->picard_monitor   = Stg_ComponentFactory_GetBool( cf, self->name, (Dictionary_Entry_Key)"picard_ActivateMonitor", False  );

   context = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"Context", FiniteElementContext, False, data );
   if( !context  )
      context = Stg_ComponentFactory_ConstructByName( cf, (Name)"context", FiniteElementContext, False, data );

    if( context ){
        entryPointRegister = context->entryPoint_Register;
        assert( entryPointRegister );
    }

   if( isNonLinear  ) {
      SNESCreate( MPI_COMM_WORLD, &nlSolver );
      self->linearSolveInitGuess = Stg_ComponentFactory_GetBool( cf, self->name, (Dictionary_Entry_Key)"linearSolveInitialGuess", False  );
   }

   _SystemLinearEquations_Init(
      self,
      solver,
      nlSolver,
      context,
      makeConvergenceFile,
      isNonLinear,
      nonLinearTolerance,
      nonLinearMaxIterations,
      killNonConvergent,
      nonLinearMinIterations,
      nonLinearSolutionType,
      optionsPrefix,
      entryPointRegister,
      MPI_COMM_WORLD );

   VecCreate( self->comm, &self->X );
   VecCreate( self->comm, &self->F );
   MatCreate( self->comm, &self->A );
   MatCreate( self->comm, &self->J );
}

/* Build */
void _SystemLinearEquations_Build( void* sle, void* _context ) {
   SystemLinearEquations*      self = (SystemLinearEquations*)sle;
   Index            index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );

   /* build the matrices */
   for ( index = 0; index < self->stiffnessMatrices->count; index++ ) {
      /* Update rowSize and colSize if boundary conditions have been applied */
      Stg_Component_Build( self->stiffnessMatrices->data[index], _context, False );
   }

   /* and the vectors */
   for ( index = 0; index < self->forceVectors->count; index++ ) {
      /* Build the force vectors - includes updateing matrix size based on Dofs */
      Stg_Component_Build( self->forceVectors->data[index], _context, False );
   }

   /* and the solutions */
   for ( index = 0; index < self->solutionVectors->count; index++ ) {
      /* Build the force vectors - includes updateing matrix size based on Dofs */
      Stg_Component_Build( self->solutionVectors->data[index], _context, False );
   }

   /* lastly, the solver - if required */
   if( self->solver )
      Stg_Component_Build( self->solver, self, True );

   Stream_UnIndentBranch( StgFEM_Debug );
}

void _SystemLinearEquations_Initialise( void* sle, void* _context ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;
   Index                  index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );

   /* initialise the matrices */
   for ( index = 0; index < self->stiffnessMatrices->count; index++ ) {
      /* Update rowSize and colSize if boundary conditions have been applied */
      Stg_Component_Initialise( self->stiffnessMatrices->data[index], _context, False );
   }

   /* and the vectors */
   for ( index = 0; index < self->forceVectors->count; index++ ) {
      /* Initialise the force vectors - includes updateing matrix size based on Dofs */
      Stg_Component_Initialise( self->forceVectors->data[index], _context, False );
   }

   /* and the solutions */
   for ( index = 0; index < self->solutionVectors->count; index++ ) {
      /* Initialise the force vectors - includes updateing matrix size based on Dofs */
      Stg_Component_Initialise( self->solutionVectors->data[index], _context, False );
   }

   /* Check to see if any of the components need to make the SLE non-linear */
   SystemLinearEquations_CheckIfNonLinear( self );

   /* Setup Location Matrix */
   SystemLinearEquations_LM_Setup( self, _context );

   /* lastly, the solver, if required */
   if( self->solver )
      Stg_Component_Initialise( self->solver, self, False );
   Stream_UnIndentBranch( StgFEM_Debug );
}

void _SystemLinearEquations_Execute( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );

   if(self->runatExecutePhase) _SystemLinearEquations_RunEP( sle, _context );

   Stream_UnIndentBranch( StgFEM_Debug );
}

void _SystemLinearEquations_RunEP( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   _EntryPoint_Run_2VoidPtr( self->executeEP, sle, _context );
}

void SystemLinearEquations_ExecuteSolver( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;
   double wallTime;
   /* Actually run the solver to get the new values into the SolutionVectors */

   Journal_Printf(self->info,"Linear solver (%s) \n",self->executeEPName);

   wallTime = MPI_Wtime();
   if( self->solver )
      Stg_Component_Execute( self->solver, self, True );

   self->curSolveTime = MPI_Wtime() - wallTime;
   Journal_Printf(self->info,"Linear solver (%s), solution time %6.6e (secs)\n",self->executeEPName, self->curSolveTime);

}

void _SystemLinearEquations_Destroy( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   /* BEGIN LUKE'S FRICTIONAL BCS BIT */
   Memory_Free( self->nlSetupEPName );
   Memory_Free( self->nlEPName );
   Memory_Free( self->postNlEPName );
   Memory_Free( self->nlConvergedEPName );
   /* END LUKE'S FRICTIONAL BCS BIT */
   Memory_Free( self->executeEPName );

   Stg_Class_Delete( self->extensionManager );

   Stg_Class_Delete( self->stiffnessMatrices );
   Stg_Class_Delete( self->forceVectors );
   Stg_Class_Delete( self->solutionVectors );

   Memory_Free( self->optionsPrefix );

   Stg_VecDestroy(&self->X );
   Stg_VecDestroy(&self->F );
   Stg_MatDestroy(&self->A );
   Stg_MatDestroy(&self->J );

   /* Free the the MG handles. */
   FreeArray( self->mgHandles );
}

void SystemLinearEquations_BC_Setup( void* sle, void* _context ) {
   SystemLinearEquations*            self = (SystemLinearEquations*)sle;
   Index                  index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   for ( index = 0; index < self->solutionVectors->count; index++ ) {
      SolutionVector_ApplyBCsToVariables( self->solutionVectors->data[index], _context );
   }
}

void SystemLinearEquations_LM_Setup( void* sle, void* _context ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   self->_LM_Setup( self, _context );
}

void SystemLinearEquations_IntegrationSetup( void* sle, void* _context ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   _EntryPoint_Run_Class_VoidPtr( self->integrationSetupEP, _context );
}

void _SystemLinearEquations_LM_Setup( void* sle, void* _context ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;
   Index                  index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );
   /* For each feVariable of each stiffness matrix, build the LM  */
   for ( index = 0; index < self->stiffnessMatrices->count; index++ ) {
      StiffnessMatrix*            sm = (StiffnessMatrix*)self->stiffnessMatrices->data[index];

      FeEquationNumber_BuildLocationMatrix( sm->rowEqNum );
      FeEquationNumber_BuildLocationMatrix( sm->colEqNum );
   }
   Stream_UnIndentBranch( StgFEM_Debug );
}

void SystemLinearEquations_MatrixSetup( void* sle, void* _context ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   self->_matrixSetup( self, _context );
}

void _SystemLinearEquations_MatrixSetup( void* sle, void* _context ) {
   SystemLinearEquations*            self = (SystemLinearEquations*)sle;
   FiniteElementContext*            context = (FiniteElementContext*)_context;
   Index                  index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );
   for ( index = 0; index < self->stiffnessMatrices->count; index++ ) {
      StiffnessMatrix_Assemble( self->stiffnessMatrices->data[index], self, context );
   }
   Stream_UnIndentBranch( StgFEM_Debug );
}

void SystemLinearEquations_VectorSetup( void* sle, void* _context ) {
   SystemLinearEquations*            self = (SystemLinearEquations*)sle;

   self->_vectorSetup( self, _context );
}

void _SystemLinearEquations_VectorSetup( void* sle, void* _context ) {
   SystemLinearEquations*            self = (SystemLinearEquations*)sle;
   Index                  index;

   Journal_DPrintf( self->debug, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );
   for ( index = 0; index < self->forceVectors->count; index++ ) {
      ForceVector_Assemble( self->forceVectors->data[index] );
   }
   Stream_UnIndentBranch( StgFEM_Debug );
}

Index _SystemLinearEquations_AddStiffnessMatrix( void* sle, StiffnessMatrix* stiffnessMatrix ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_AddStiffnessMatrix( self, stiffnessMatrix );
}

StiffnessMatrix* _SystemLinearEquations_GetStiffnessMatrix( void* sle, Name stiffnessMatrixName ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_GetStiffnessMatrix( self, stiffnessMatrixName );
}

Index _SystemLinearEquations_AddForceVector( void* sle, ForceVector* forceVector ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_AddForceVector( self, forceVector );
}

ForceVector* _SystemLinearEquations_GetForceVector( void* sle, Name forceVectorName ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_GetForceVector( self, forceVectorName );
}

Index _SystemLinearEquations_AddSolutionVector( void* sle, SolutionVector* solutionVector ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_AddSolutionVector( self, solutionVector );
}

SolutionVector* _SystemLinearEquations_GetSolutionVector( void* sle, Name solutionVectorName ) {
   SystemLinearEquations*               self = (SystemLinearEquations*)sle;

   return SystemLinearEquations_GetSolutionVector( self, solutionVectorName );
}

void SystemLinearEquations_SetCallback( void* sle, PyObject* func) {
  SystemLinearEquations *self = (SystemLinearEquations*) sle;

  // Assign the PyObject - 'None' or callable function to solver_callback
  if (func == Py_None)
    self->solver_callback = NULL;
  else {
    // check if it's callable
    if(!PyCallable_Check(func)){
      PyErr_SetString( PyExc_ValueError, "The callback function can't be called, please check if it's valid");
    }
    self->solver_callback = func;
  }
}

void SystemLinearEquations_UpdateSolutionOntoNodes( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   self->_updateSolutionOntoNodes( self, _context );

   // execute the python callback if found
   if( self->solver_callback ) {
     if(!PyObject_CallObject(self->solver_callback, NULL )) {
       // check if callback execution failed
       PyErr_SetString( PyExc_RuntimeError, "Failed to execute the callback function, please check if it's valid");
     }
   }
}

void _SystemLinearEquations_UpdateSolutionOntoNodes( void* sle, void* _context ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;
   SolutionVector_Index   solnVec_I;
   SolutionVector*      currentSolnVec;

   for ( solnVec_I=0; solnVec_I < self->solutionVectors->count; solnVec_I++ ) {
      currentSolnVec = (SolutionVector*)self->solutionVectors->data[solnVec_I];
      SolutionVector_UpdateSolutionOntoNodes( currentSolnVec );
   }
}

void SystemLinearEquations_ZeroAllVectors( void* sle, void* _context ) {
   SystemLinearEquations*      self = (SystemLinearEquations*)sle;
   Index                       index;

   for ( index = 0; index < self->forceVectors->count; index++ )
      ForceVector_Zero( self->forceVectors->data[index] );
}

/* need to do this before the SLE specific function to set up the
pre conditioners is called (beginning of solve) */
void SystemLinearEquations_NewtonInitialise( void* _context, void* data ) {
   FiniteElementContext*   context = (FiniteElementContext*)_context;
    assert(0); // the following will need to be fixed to get the sle from elsewhere
   SystemLinearEquations*   sle = (SystemLinearEquations*)context->slEquations->data[0];
   SNES                     snes;
   SNES                     oldSnes = sle->nlSolver;

   /* don't assume that a snes is being used for initial guess, check for this!!! */
   if( oldSnes && context->timeStep == 1 && !sle->linearSolveInitGuess )
      Stg_SNESDestroy(&oldSnes );

   SNESCreate( sle->comm, &snes );

   sle->nlSolver = snes;
   sle->_setFFunc( &sle->F, context );

   SNESSetJacobian( snes, sle->J, sle->P, sle->_buildJ, sle->buildJContext );
   SNESSetFunction( snes, sle->F, sle->_buildF, sle->buildFContext );

   /* configure the KSP */
   sle->_configureNLSolverFunc( snes, context );
}

/* do this after the pre conditoiners have been set up in the problem specific SLE */
void SystemLinearEquations_NewtonExecute( void* sle, void* _context ) {
   SystemLinearEquations*   self            = (SystemLinearEquations*) sle;
   SNES         snes      = self->nlSolver;

   SNESSetOptionsPrefix( snes, self->optionsPrefix );
   SNESSetFromOptions( snes );
   SNESSolve( snes, PETSC_NULL, self->X );
}

/* do this at end of solve step */
void SystemLinearEquations_NewtonFinalise( void* _context, void* data ) {
   FiniteElementContext*   context      = (FiniteElementContext*)_context;
    assert(0); // the following will need to be fixed to get the sle from elsewhere
   SystemLinearEquations*   sle             = (SystemLinearEquations*)context->slEquations->data[0];
   SNES         snes      = sle->nlSolver;

   sle->_updateOldFields( &sle->X, context );

   Stg_SNESDestroy(&snes );
}

void SystemLinearEquations_NewtonMFFDExecute( void* sle, void* _context ) {
   SystemLinearEquations*   self            = (SystemLinearEquations*) sle;
   Vec             F;

   VecDuplicate( SystemLinearEquations_GetSolutionVectorAt( self, 0 )->vector, &F );

   /* creates the nonlinear solver */
   if( self->nlSolver != PETSC_NULL )
      Stg_SNESDestroy(&self->nlSolver );
   SNESCreate( self->comm, &self->nlSolver );
   SNESSetFunction( self->nlSolver, F, self->_buildF, _context );

   // set J (jacobian)

   // set F (residual vector)

   // call non linear solver func (SNES wrapper)
}

void SystemLinearEquations_NonLinearExecute( void* sle, void* _context ) {
   SystemLinearEquations*   self            = (SystemLinearEquations*) sle;
   Vec                     previousVector;
   Vec                     currentVector;
   double                  residual;
   double                  tolerance       = self->nonLinearTolerance;
   Iteration_Index         maxIterations   = self->nonLinearMaxIterations;
   Bool                    converged;
   Stream*                 errorStream     = Journal_Register( Error_Type, (Name)self->type  );
   double                  wallTime;
   Iteration_Index         minIterations   = self->nonLinearMinIterations;
   SLE_Solver*             solver;

   PetscScalar      currVecNorm, prevVecNorm;

   Journal_Printf( self->info, "In %s\n", __func__ );
   Stream_IndentBranch( StgFEM_Debug );

   wallTime = MPI_Wtime();

   /* First Solve */
   /* setting the nonlinear stuff */
   //START OF NONLINEAR ITERATION!!!!!
   /* first get current timestep */
        solver = self->solver;
//    assert(0); // THE FOLLOWING WILL NEED TO BE FIXED.. NOT SURE WHY THE SOLVER NEEDS THE CURRENT TIMESTEP
//   solver->currenttimestep = self->context->timeStep;
   /* if current timestep is not the same as previous timestep, then reset all variables back to zero and update previous timestep */
//   if(solver->currenttimestep != solver->previoustimestep){
      //update prev timestep
      solver->previoustimestep = solver->currenttimestep;
      solver->nonlinearitsinitialtime = 0;
      solver->nonlinearitsendtime = 0;
      solver->totalnonlinearitstime = 0;
      solver->totalnumnonlinearits = 0;
      solver->avgtimenonlinearits = 0;
      solver->inneritsinitialtime = 0;
      solver->outeritsinitialtime = 0;
      solver->inneritsendtime = 0;
      solver->outeritsendtime = 0;
      solver->totalinneritstime = 0;
      solver->totalouteritstime = 0;
      solver->totalnuminnerits = 0;
      solver->totalnumouterits = 0;
      solver->avgnuminnerits = 0;
      solver->avgnumouterits = 0;
      solver->avgtimeinnerits = 0;
      solver->avgtimeouterits = 0;
//   }

   self->nonLinearIteration_I = 0;
   Journal_Printf(self->info,"\nNon linear solver - iteration %d\n", self->nonLinearIteration_I);

        /* More of Luke's stuff. I need an entry point for a non-linear setup operation. */
        _EntryPoint_Run_2VoidPtr( self->nlSetupEP, sle, _context );

   /*Don't know if we should include this but the timing of the outer and inner iterations starts here so it makes sense to count this one? */
   solver->nonlinearitsinitialtime = MPI_Wtime();

   self->linearExecute( self, _context );
   self->hasExecuted = True;

   solver->nonlinearitsendtime = MPI_Wtime();
   solver->totalnonlinearitstime = solver->totalnonlinearitstime + (-solver->nonlinearitsinitialtime + solver->nonlinearitsendtime);
   /* reset initial time and end time for inner its back to 0 - probs don't need to do this but just in case */
   solver->nonlinearitsinitialtime = 0;
   solver->nonlinearitsendtime = 0;
   /*
   ** Include an entry point to do some kind of post-non-linear-iteration operation. */
   _EntryPoint_Run_2VoidPtr( self->postNlEP, sle, _context );

   /* TODO - Give option which solution vector to test */
   currentVector   = SystemLinearEquations_GetSolutionVectorAt( self, 0 )->vector;
   VecDuplicate( currentVector, &previousVector );

   for ( self->nonLinearIteration_I = 1 ; self->nonLinearIteration_I < maxIterations ; self->nonLinearIteration_I++ ) {
      /* get initial wall time for nonlinear loop */
      solver->nonlinearitsinitialtime = MPI_Wtime();

      /*
      ** BEGIN LUKE'S FRICTIONAL BCS BIT
      **
      ** Adding an interface for allowing other components to add some form of non-linearity to the system.
      ** This is with a focus on frictional BCs, where we want to examine the stress field and modify
      ** traction BCs to enforce friction rules. - Luke 18/07/2007
      */

      _EntryPoint_Run_2VoidPtr( self->nlEP, sle, _context );

      /*
      ** END LUKE'S FRICTIONAL BCS BIT
      */

      //Vector_CopyEntries( currentVector, previousVector );
      VecCopy( currentVector, previousVector );

      Journal_Printf(self->info,"Non linear solver - iteration %d\n", self->nonLinearIteration_I);

      self->linearExecute( self, _context );
//      PetscPrintf( PETSC_COMM_WORLD, "|Xn+1| = %12.12e \n", Vector_L2Norm(SystemLinearEquations_GetSolutionVectorAt(self,1)->vector) );

      /* Calculate Residual */
      VecAXPY( previousVector, -1.0, currentVector );
      VecNorm( previousVector, NORM_2, &prevVecNorm );
      VecNorm( currentVector, NORM_2, &currVecNorm );
      residual = ((double)prevVecNorm) / ((double)currVecNorm);

      self->curResidual = residual;

                /*
                ** Include an entry point to do some kind of post-non-linear-iteration operation. */
      _EntryPoint_Run_2VoidPtr( self->postNlEP, sle, _context );

      Journal_Printf( self->info, "In func %s: Iteration %u of %u - Residual %.5g - Tolerance = %.5g\n",
            __func__, self->nonLinearIteration_I, maxIterations, residual, tolerance );
      if ( self->context && self->makeConvergenceFile ) {
         Journal_Printf( self->convergenceStream, "%d\t\t%d\t\t%.5g\t\t%.5g\n",
                      self->context->timeStep, self->nonLinearIteration_I, residual, tolerance );
      }

      /* Check if residual is below tolerance */
      converged = (residual < tolerance);

      Journal_Printf(self->info,"Non linear solver - Residual %.8e; Tolerance %.4e%s%s - %6.6e (secs)\n\n", residual, tolerance,
         (converged) ? " - Converged" : " - Not converged",
         (self->nonLinearIteration_I < maxIterations) ? "" : " - Reached iteration limit",
         MPI_Wtime() - wallTime );
         //END OF NONLINEAR ITERATION LOOP!!!

         /* add the outer loop iterations to the total outer iterations */
         solver->totalnumnonlinearits += 1;
         /*get wall time for end of outer loop*/
         solver->nonlinearitsendtime = MPI_Wtime();
         /* add time to total time inner its: */
         solver->totalnonlinearitstime = solver->totalnonlinearitstime + (-solver->nonlinearitsinitialtime + solver->nonlinearitsendtime);
         //printf("totalnumnonlinearits before converging is %d totalnonlinearitstime is %g, totalouteritstime is %g and totalinneritstime is %g\n",solver->totalnumnonlinearits,solver->totalnonlinearitstime,solver->totalouteritstime,solver->totalinneritstime);

         /* reset initial time and end time for inner its back to 0 - probs don't need to do this but just in case */
         solver->nonlinearitsinitialtime = 0;
         solver->nonlinearitsendtime = 0;
      if ( (converged) && (self->nonLinearIteration_I>=minIterations) ) {
         int result, ierr;

         /* Adding in another entry point so we can insert out own custom
            convergeance checks. For example, with frictional boundary
            conditions we need to ensure envery node was gone from the
            original searching state to a fixed slipping or sticking state. */
         _EntryPoint_Run_2VoidPtr( self->nlConvergedEP, _context, &converged );
         ierr = MPI_Allreduce( &converged, &result, 1, MPI_INT, MPI_LOR, MPI_COMM_WORLD );
         if( result )
            break;
      }
   }

   /* Print Info */
   if ( converged ) {
      Journal_Printf( self->info, "In func %s: Converged after %u iterations.\n",
            __func__, self->nonLinearIteration_I );
   }
   else {
      Journal_Printf( errorStream, "In func %s: Failed to converge after %u iterations.\n",
            __func__, self->nonLinearIteration_I);
      if ( self->killNonConvergent ) {
         abort();
      }
   }

   Stream_UnIndentBranch( StgFEM_Debug );

   Stg_VecDestroy(&previousVector );

   /*Set all the printout variables */
        if( solver->totalnumnonlinearits ) {
           solver->avgtimenonlinearits = (solver->totalnonlinearitstime - solver->totalouteritstime)/solver->totalnumnonlinearits;
           solver->avgnumouterits = solver->totalnumouterits/solver->totalnumnonlinearits;
        }
        if( solver->totalnumouterits ) {
           solver->avgnuminnerits = solver->totalnuminnerits/solver->totalnumouterits;
           solver->avgtimeouterits = (solver->totalouteritstime - solver->totalinneritstime)/solver->totalnumouterits;
        }
        if( solver->totalnuminnerits )
           solver->avgtimeinnerits = solver->totalinneritstime/solver->totalnuminnerits;
   //printf("totalnumnonlinearits = %d, avgnumouterits %d, avgnuminnerits %d\n",solver->totalnumnonlinearits, solver->avgnumouterits, solver->avgnuminnerits);

}

void SystemLinearEquations_AddNonLinearSetupEP( void* sle, const char* name, EntryPoint_2VoidPtr_Cast func ) {
   SystemLinearEquations* self = (SystemLinearEquations*)sle;

   SystemLinearEquations_SetToNonLinear( self, True );
   EntryPoint_Append( self->nlSetupEP, (char*)name, func, self->type );
}

void SystemLinearEquations_AddPostNonLinearEP( void* sle, const char* name, EntryPoint_2VoidPtr_Cast func ) {
   SystemLinearEquations* self = (SystemLinearEquations*)sle;

   EntryPoint_Append( self->postNlEP, (char*)name, func, self->type );
}

void SystemLinearEquations_AddNonLinearConvergedEP( void* sle,
                      const char* name,
                      EntryPoint_2VoidPtr_Cast func )
{
   SystemLinearEquations* self = (SystemLinearEquations*)sle;

   SystemLinearEquations_SetToNonLinear( self, True );
   EntryPoint_Append( self->nlConvergedEP, (char*)name, func, self->type );
}

/*
Computes
  F1 := A(x) x -b  = -r,
  where r = b - A(x) x
*/
//void SystemLinearEquations_SNESPicardFormalResidual( void *someSLE, Vector *stg_X, Vector *stg_F, void *_context )
void SystemLinearEquations_SNESPicardFormalResidual( void *someSLE, Vec X, Vec F, void *_context )
{
   SystemLinearEquations *sle = (SystemLinearEquations*)someSLE;
       SLE_Solver            *solver = (SLE_Solver*)sle->solver;
   abort();

   solver->_formResidual( (void*)sle,  (void*)solver, F );
}

/*
Computes
  F2 := x - A(x)^{-1} b
*/
#if 0
void SystemLinearEquations_SNESPicardKSPResidual( void *someSLE, Vector *stg_X, Vector *stg_F, void *_context )
{
        SystemLinearEquations *sle = (SystemLinearEquations*)someSLE;
        SLE_Solver            *solver = (SLE_Solver*)sle->solver;
        Vec                   F,X,Xcopy;
        PetscReal norm;

        F = StgVectorGetPetscVec( stg_F );
        X = StgVectorGetPetscVec( stg_X );

        VecDuplicate( X, &Xcopy );
        VecCopy( X, Xcopy );

        VecCopy( X, F );                        /* F <- X */
//      VecNorm( X, NORM_2, &norm );
//      PetscPrintf(PETSC_COMM_WORLD,"  |X|_pre = %5.5e \n", norm );
        sle->linearExecute( sle, _context );    /* X = A^{-1} b */
        X = StgVectorGetPetscVec( stg_X );
//      VecNorm( X, NORM_2, &norm );
//      PetscPrintf(PETSC_COMM_WORLD,"  |X|_post = %5.5e \n", norm );

        VecAXPY( F, -1.0, X );                  /* F <- X - F */
//      VecNorm( F, NORM_2, &norm );
//      PetscPrintf(PETSC_COMM_WORLD,"  |F|_post = %5.5e \n", norm );

        VecCopy( Xcopy, X );
        Stg_VecDestroy(&Xcopy );
}
#endif

/*
stg_X must not get modified by this function !!
*/
void SystemLinearEquations_SNESPicardKSPResidual( void *someSLE, Vec X, Vec F, void *_context )
{
        SystemLinearEquations *sle = (SystemLinearEquations*)someSLE;
        SLE_Solver            *solver = (SLE_Solver*)sle->solver;
   Vec         Xstar;
   PetscReal norm,norms;

   solver->_getSolution( sle, solver, &Xstar );

   /* Map most current solution into stg object, vec->mesh  */
   VecCopy( X, Xstar );  /* X* <- X */
   /* Map onto nodes */
   SystemLinearEquations_UpdateSolutionOntoNodes( someSLE, _context );

   VecNorm( X, NORM_2, &norm );
   VecNorm( Xstar, NORM_2, &norms );
//   PetscPrintf(PETSC_COMM_WORLD,"  |X| = %12.12e : |x*| = %12.12e <pre>\n", norm, norms );

   sle->linearExecute( sle, _context );    /* X* = A^{-1} b */

   VecNorm( X, NORM_2, &norm );
   VecNorm( Xstar, NORM_2, &norms );
//   PetscPrintf(PETSC_COMM_WORLD,"  |X| = %12.12e : |x*| = %12.12e <post> \n", norm, norms );

   VecWAXPY( F, -1.0, Xstar, X ); /* F = -X* + X  */
}

PetscErrorCode SLEComputeFunction( void *someSLE, Vec X, Vec F, void *_context )
{
        SystemLinearEquations *sle = (SystemLinearEquations*)someSLE;

   if (sle->_sleFormFunction!=NULL) {
      sle->_sleFormFunction( sle, X, F, _context );
   }
   else {
      Stg_SETERRQ( PETSC_ERR_SUP, "SLEComputeFunction in not valid" );
   }
}

void SLE_SNESMonitor( void *sle, PetscInt iter, PetscReal fnorm )
{
  PetscPrintf( PETSC_COMM_WORLD, "  %.4d SLE_NS Function norm %12.12e    --------------------------------------------------------------------------------\n", iter, fnorm );
}

void SLE_SNESMonitor2( void *sle, PetscInt iter, PetscReal fnorm0, PetscReal fnorm, PetscReal dX, PetscReal X1 )
{
  if(iter==0) {
    PetscPrintf( PETSC_COMM_WORLD, "  SLE_NS  it       |F|              |F|/|F0|          |X1-X0|        |X1-X0|/|X1| \n" );
  }
  PetscPrintf( PETSC_COMM_WORLD,   "  SLE_NS  %1.4d     %2.4e      %2.4e       %2.4e     %2.4e \n", iter, fnorm, fnorm/fnorm0, dX, dX/X1 );
}

void _monitor_progress( PetscReal initial, PetscReal target, PetscReal current, PetscReal *p )
{
  PetscReal p0;

  p0 = log10(initial) - log10(target);
  *p = 100.0 * ( 1.0 - (log10(current)-log10(target)) / p0 );
}

void SLE_SNESMonitorProgress( void *sle,
  PetscInt iter,
  PetscReal fnorm0, PetscReal fnorm, PetscReal dX, PetscReal X1,
  PetscReal fatol, PetscReal frtol, PetscReal stol )
{
  PetscReal f_abs_s, f_abs_e, v1, p1;
  PetscReal f_rel_s, f_rel_e, v2, p2;
  PetscReal x_del_s, x_del_e, v3, p3;

  if(iter==0) {
    PetscPrintf( PETSC_COMM_WORLD, "  SLE_NS  it       |F|                 |F|/|F0|             |X1-X0|/|X1| \n" );
  }

  f_abs_s = fnorm0;
  f_abs_e = fatol;
  v1 = fnorm;
  _monitor_progress( f_abs_s, f_abs_e, v1, &p1 );

  f_rel_s = fnorm0;
  f_rel_e = fnorm0 * frtol;
  v2 = fnorm;
  _monitor_progress( f_rel_s, f_rel_e, v2, &p2 );

  x_del_s = 1.0;
  x_del_e = stol;
  v3 = dX/X1;
  _monitor_progress( x_del_s, x_del_e, v3, &p3 );

  PetscPrintf( PETSC_COMM_WORLD,   "  SLE_NS  %1.4d     %2.4e [%2.0f%%]     %2.4e [%2.0f%%]      %2.4e [%2.0f%%] \n", iter, fnorm,p1, fnorm/fnorm0,p2,  dX/X1,p3 );
}

void SLE_SNESConverged(
   PetscReal snes_abstol, PetscReal snes_rtol, PetscReal snes_ttol, PetscReal snes_stol,
   PetscInt it,PetscReal xnorm,PetscReal pnorm,PetscReal fnorm,SNESConvergedReason *reason )
{
  /* PetscErrorCode ierr; */

  *reason = SNES_CONVERGED_ITERATING;

  if (!it) {
    /* set parameter for default relative tolerance convergence test */
    snes_ttol = fnorm*snes_rtol;
  }
  if (fnorm != fnorm) {
//    ierr = PetscInfo(snes,"Failed to converged, function norm is NaN\n");CHKERRQ(ierr);
    *reason = SNES_DIVERGED_FNORM_NAN;
  } else if (fnorm < snes_abstol) {
//    ierr = PetscInfo2(snes,"Converged due to function norm %G < %G\n",fnorm,snes_abstol);CHKERRQ(ierr);
    *reason = SNES_CONVERGED_FNORM_ABS;
  }
//  } else if (snes->nfuncs >= snes->max_funcs) {
//    ierr = PetscInfo2(snes,"Exceeded maximum number of function evaluations: %D > %D\n",snes->nfuncs,snes->max_funcs);CHKERRQ(ierr);
//    *reason = SNES_DIVERGED_FUNCTION_COUNT;
//  }

  if (it && !*reason) {
    if (fnorm <= snes_ttol) {
//      ierr = PetscInfo2(snes,"Converged due to function norm %G < %G (relative tolerance)\n",fnorm,snes_ttol);CHKERRQ(ierr);
      *reason = SNES_CONVERGED_FNORM_RELATIVE;
    } else if (pnorm < snes_stol*xnorm) {
//      ierr = PetscInfo3(snes,"Converged due to small update length: %G < %G * %G\n",pnorm,snes_stol,xnorm);CHKERRQ(ierr);
#if( (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR <= 2) || PETSC_VERSION_MAJOR<3 )
      *reason = SNES_CONVERGED_PNORM_RELATIVE;
#else
      *reason = SNES_CONVERGED_SNORM_RELATIVE;
#endif

    }
  }
}

#if defined(PETSC_HAVE_ISINF) && defined(PETSC_HAVE_ISNAN)
#define PetscIsInfOrNanScalar(a) (isinf(PetscAbsScalar(a)) || isnan(PetscAbsScalar(a)))
#define PetscIsInfOrNanReal(a) (isinf(a) || isnan(a))
#elif defined(PETSC_HAVE__FINITE) && defined(PETSC_HAVE__ISNAN)
#if defined(PETSC_HAVE_FLOAT_H)
#include "float.h"  /* windows defines _finite() in float.h */
#endif
#define PetscIsInfOrNanScalar(a) (!_finite(PetscAbsScalar(a)) || _isnan(PetscAbsScalar(a)))
#define PetscIsInfOrNanReal(a) (!_finite(a) || _isnan(a))
#else
#define PetscIsInfOrNanScalar(a) ((a - a) != 0.0)
#define PetscIsInfOrNanReal(a) ((a - a) != 0.0)
#endif

/*
This will be replaced by SNESPicard in petsc 2.4.0.
*/

PetscErrorCode SystemLinearEquations_PicardExecute( void *sle, void *_context )
{
  SystemLinearEquations *self = (SystemLinearEquations*)sle;
  SLE_Solver            *solver = (SLE_Solver*)self->solver;

  Vec            X, Y, F, Xstar,delta_X;
  PetscReal      alpha = 1.0;
  PetscReal      fnorm,norm_X,pnorm,fnorm0;
  PetscInt       i;
  PetscErrorCode ierr;
  SNESConvergedReason snes_reason;

  PetscReal snes_norm;
  PetscInt snes_iter;

  PetscReal snes_ttol, snes_rtol, snes_abstol, snes_stol;
  PetscInt  snes_maxits;

  PetscTruth monitor_flg;

  /* setup temporary some vectors */
  solver->_getSolution( self, solver, &Xstar );

  VecDuplicate( Xstar, &X );
  VecDuplicate( X, &F );
  VecDuplicate( F, &Y );
  VecDuplicate( F, &delta_X );

  /* Get some values from dictionary */
  snes_maxits =  (PetscInt)self->nonLinearMaxIterations;
  snes_ttol   = 0.0;
  snes_rtol   = (PetscReal)self->rtol;
  snes_abstol = (PetscReal)self->abstol;
  snes_stol   = (PetscReal)self->stol;
  monitor_flg = PETSC_FALSE;
  if (self->picard_monitor==True) { monitor_flg = PETSC_TRUE; }
  alpha       = (PetscReal)self->alpha;

  snes_reason = SNES_CONVERGED_ITERATING;

  /* Map X <- X* */
  VecCopy( Xstar, X );  // Vector_CopyEntries( currentVector, previousVector );

  /* Get an initial guess if |X| ~ 0, by solving the linear problem  */
  VecNorm( X, NORM_2, &norm_X );
  if (norm_X <1.0e-20) {
    if (monitor_flg==PETSC_TRUE)
      PetscPrintf( PETSC_COMM_WORLD, "SLE_Picard: Computing an initial guess for X from the linear problem\n");

    self->linearExecute( sle, _context );    /* X* = A^{-1} b */
    self->hasExecuted = True;

    /* Map X <- X* */
    VecCopy( Xstar, X );
    VecNorm( X, NORM_2, &norm_X );
  }

  snes_iter = 0;
  snes_norm = 0;

  SLEComputeFunction( sle, X, F, _context );
  ierr = VecNorm(F, NORM_2, &fnorm); CHKERRQ(ierr); /* fnorm <- ||F||  */
  fnorm0 = fnorm;

  if( PetscIsInfOrNanReal(fnorm) )
     Stg_SETERRQ(PETSC_ERR_FP,"Infinite or not-a-number generated in norm");

  snes_norm = fnorm;
  if(monitor_flg==PETSC_TRUE) {
  /*  SLE_SNESMonitor(sle,0,fnorm); */
  /*  SLE_SNESMonitor2(sle,0,fnorm0,fnorm, norm_X, norm_X ); */
    SLE_SNESMonitorProgress( sle, snes_iter,  fnorm0, fnorm, norm_X, norm_X, snes_abstol, snes_rtol, snes_stol );
  }

  /* set parameter for default relative tolerance convergence test */
  snes_ttol = fnorm*snes_rtol;
  /* test convergence */
  SLE_SNESConverged( snes_abstol,snes_rtol,snes_ttol,snes_stol , 0,norm_X,0.0,fnorm,&snes_reason);

  if (snes_reason)
     return NULL;

  for(i = 0; i < snes_maxits; i++) {
    /* Update guess Y = X^n - F(X^n) */
    ierr = VecWAXPY(Y, -1.0, F, X);
    CHKERRQ(ierr);

    VecCopy( X, delta_X );  /* delta_X <- X */

    /* X^{n+1} = (1 - \alpha) X^n + alpha Y */
    ierr = VecAXPBY(X, alpha, 1 - alpha, Y);
    CHKERRQ(ierr);

    VecNorm( X, NORM_2, &norm_X );
    VecAYPX( delta_X, -1.0, X );   /* delta_X <- Xn+1 - delta_X */
    VecNorm( delta_X, NORM_2, &pnorm );

    /* Compute F(X^{new}) */
    SLEComputeFunction( sle, X, F, _context );
    ierr = VecNorm(F, NORM_2, &fnorm);
    CHKERRQ(ierr);

    if( PetscIsInfOrNanReal(fnorm) )
       Stg_SETERRQ(PETSC_ERR_FP,"Infinite or not-a-number generated norm");

    /* Monitor convergence */
    snes_iter = i+1;
    snes_norm = fnorm;
    if (monitor_flg==PETSC_TRUE) {
    /*  SLE_SNESMonitor(sle,snes_iter,snes_norm); */
    /*  SLE_SNESMonitor2(sle,snes_iter,fnorm0,fnorm, pnorm, norm_X ); */

      SLE_SNESMonitorProgress( sle, snes_iter,  fnorm0, fnorm, pnorm, norm_X, snes_abstol, snes_rtol, snes_stol );
    }

    /* Test for convergence */
    SLE_SNESConverged( snes_abstol,snes_rtol,snes_ttol,snes_stol , snes_iter,norm_X,pnorm,fnorm,&snes_reason);
    if (snes_reason) break;
  }

  if (i == snes_maxits) {
    if (!snes_reason) snes_reason = SNES_DIVERGED_MAX_IT;
  }

  /* If monitoring, report reason converged */

  if (monitor_flg==PETSC_TRUE)
    PetscPrintf( PETSC_COMM_WORLD, "Nonlinear solve converged due to %s \n", SNESConvergedReasons[snes_reason] );

  Stg_VecDestroy(&X );
  Stg_VecDestroy(&F );
  Stg_VecDestroy(&Y );
  Stg_VecDestroy(&delta_X );

  return NULL;

}

/* ////////////// */

void SystemLinearEquations_AddNonLinearEP( void* sle, const char* name, EntryPoint_2VoidPtr_Cast func ) {
   SystemLinearEquations* self = (SystemLinearEquations*)sle;

   SystemLinearEquations_SetToNonLinear( self, True );
   EntryPoint_Append( self->nlEP, (char*)name, func, self->type );
}

void SystemLinearEquations_SetNonLinearTolerance( void* sle, double tol ){
   SystemLinearEquations*   self = (SystemLinearEquations*) sle;
   self->nonLinearTolerance = tol;
   // SystemLinearEquations_SetToNonLinear( self, True );
}

void SystemLinearEquations_SetToNonLinear( void* sle, Bool isNonLinear ) {
   SystemLinearEquations*   self = (SystemLinearEquations*) sle;
   Hook*         nonLinearInitHook   = NULL;
   Hook*         nonLinearFinaliseHook   = NULL;
   FiniteElementContext*   context         = NULL;

    if (isNonLinear) {
        if ( self->isNonLinear )
            return;

        self->isNonLinear = True;

        self->linearExecute = self->_execute;
        self->_execute = SystemLinearEquations_NonLinearExecute;

        if( self->nonLinearSolutionType ) {
            if( !strcmp( self->nonLinearSolutionType, "default" ) ) {
                self->_execute = SystemLinearEquations_NonLinearExecute;
            }

            if( !strcmp( self->nonLinearSolutionType, "MatrixFreeNewton" ) )
                self->_execute = SystemLinearEquations_NewtonMFFDExecute;

            if( !strcmp( self->nonLinearSolutionType, "Newton" ) ) {
                assert(0); // the following will need to be fixed cos no context
                context = self->context;

                nonLinearInitHook = Hook_New( "NewtonInitialise",
                            SystemLinearEquations_NewtonInitialise, self->name );
                _EntryPoint_PrependHook_AlwaysFirst( Context_GetEntryPoint( context, AbstractContext_EP_Solve ),
                                    nonLinearInitHook );
                nonLinearFinaliseHook = Hook_New( "NewtonFinalise",
                            SystemLinearEquations_NewtonFinalise, self->name );
                _EntryPoint_AppendHook_AlwaysLast( Context_GetEntryPoint( context, AbstractContext_EP_Solve ),
                            nonLinearFinaliseHook );
                self->_execute = SystemLinearEquations_NewtonExecute;
            }

            if (!strcmp( self->nonLinearSolutionType, "Picard") ) {
                /* set function pointer for execute */
                self->_execute = SystemLinearEquations_PicardExecute;

                /* set form function */
                if (!strcmp(self->picard_form_function_type,"PicardFormFunction_KSPResidual") ) {
                    self->_sleFormFunction = SystemLinearEquations_SNESPicardKSPResidual;
                }
                else if (!strcmp(self->picard_form_function_type,"PicardFormFunction_FormalResidual") ) {
                    self->_sleFormFunction = SystemLinearEquations_SNESPicardFormalResidual;
                }
                else {
                     Stream *errorStream = Journal_Register( Error_Type, (Name)self->type  );

                            Journal_Printf( errorStream, "Unknown the Picard FormFunction type %s is unrecognised. .\n", self->picard_form_function_type );
                    Journal_Printf( errorStream, "Supported types include <PicardFormFunction_FormalResidual, PicardFormFunction_KSPResidual> \n" );
                                abort();

                }
            }
        }
    } else
    {
        self->isNonLinear   = False;
        self->linearExecute = _SystemLinearEquations_Execute;
        self->_execute      = _SystemLinearEquations_Execute;
    }
}

void SystemLinearEquations_CheckIfNonLinear( void* sle ) {
   SystemLinearEquations*   self            = (SystemLinearEquations*) sle;
   Index                   index;

   for ( index = 0; index < self->stiffnessMatrices->count; index++ ) {
      StiffnessMatrix* stiffnessMatrix = SystemLinearEquations_GetStiffnessMatrixAt( self, index );

      if ( stiffnessMatrix->isNonLinear )
         SystemLinearEquations_SetToNonLinear( self, True );

      /* TODO CHECK FOR FORCE VECTORS */
   }
}

/*
** All the MG functions and their general implementations.
*/

void SystemLinearEquations_MG_Enable( void* _sle ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)_sle;

   if( !self->isBuilt ) {
      Journal_Printf(self->info, "Warning: SLE has not been built, can't enable multi-grid.\n" );
      return;
   }

   self->mgEnabled = True;
}

void SystemLinearEquations_MG_SelectStiffMats( void* _sle, unsigned* nSMs, StiffnessMatrix*** sms ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)_sle;

   assert( self->_mgSelectStiffMats );
   self->_mgSelectStiffMats( self, nSMs, sms );
}

void _SystemLinearEquations_MG_SelectStiffMats( void* _sle, unsigned* nSMs, StiffnessMatrix*** sms ) {
   SystemLinearEquations*   self = (SystemLinearEquations*)_sle;

   /*
   ** As we have nothing else to go on, attempt to apply MG to all stiffness matrices in the list.
   */

   {
      unsigned   sm_i;

      *nSMs = 0;
      for( sm_i = 0; sm_i < self->stiffnessMatrices->count; sm_i++ ) {
         StiffnessMatrix*   sm = ((StiffnessMatrix**)self->stiffnessMatrices->data)[sm_i];

         /* Add this one to the list. */
         *sms = Memory_Realloc_Array( *sms, StiffnessMatrix*, (*nSMs) + 1 );
         (*sms)[*nSMs] = sm;
         (*nSMs)++;
      }
   }
}

void SystemLinearEquations_SetCustomRunPoint( void* sle, void* _context, const Name entryPointName ){
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   /** set this flag to ensure we now do not run during the execute phase */
   self->runatExecutePhase = False;
   assert(0); // the following will need to be fixed or deprecated
   EP_AppendClassHook( Context_GetEntryPoint( _context, entryPointName ), _SystemLinearEquations_RunEP, sle );

}

SystemLinearEquations_RunEPFunction* SystemLinearEquations_GetRunEPFunction(){
   return _SystemLinearEquations_RunEP;
}

void SystemLinearEquations_SetRunDuringExecutePhase( void* sle, Bool setRunDuringExectutePhase ){
   SystemLinearEquations*   self = (SystemLinearEquations*)sle;

   self->runatExecutePhase = setRunDuringExectutePhase;
}


#ifndef SOURCE_PRINTTOFILE_H_
#define SOURCE_PRINTTOFILE_H_

// *** release/print_to_file.h ***
// Author: Kevin Wolz, date: 08/2018
//
// Utilities to write the results obtained by robustness_main into files.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <sys/stat.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include "utils.h"
#include "md5hash.h"

// Appends the robustness (and, if analytical output, pdf) value to a file.
void WriteData(std::ofstream& datafile, double& robustness, double& pdf);

// Appends the initial conditions of a run (e.g. dimensions of data, covariance
// matrix etc.) to a file.
void WriteMetadata(std::ofstream& metafile, const unsigned& datdim,
                   gsl_matrix* covar_matrix, const unsigned& pardim,
                   gsl_vector* prior_param_values, gsl_matrix* prior_matrix,
                   gsl_matrix* g_matrix, const unsigned& num_samplings);

// Appends the metadata about a run (e.g. data hash ID, filepaths, timestamp,
// etc) to a file.
void WriteLogs(std::ofstream& logfile, std::string& meta_filepath,
               const int& op_mode, std::string& data_filepath,
               const int& success);

#endif // SOURCE_PRINTTOFILE_H_


/* $Id$      */
/*--------------------------------------------------------------------*/
/*;  Copyright (C) 2008-2016                                          */
/*;  Associated Universities, Inc. Washington DC, USA.                */
/*;                                                                   */
/*;  This program is free software; you can redistribute it and/or    */
/*;  modify it under the terms of the GNU General Public License as   */
/*;  published by the Free Software Foundation; either version 2 of   */
/*;  the License, or (at your option) any later version.              */
/*;                                                                   */
/*;  This program is distributed in the hope that it will be useful,  */
/*;  but WITHOUT ANY WARRANTY; without even the implied warranty of   */
/*;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    */
/*;  GNU General Public License for more details.                     */
/*;                                                                   */
/*;  You should have received a copy of the GNU General Public        */
/*;  License along with this program; if not, write to the Free       */
/*;  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge,     */
/*;  MA 02139, USA.                                                   */
/*;                                                                   */
/*;Correspondence about this software should be addressed as follows: */
/*;         Internet email: bcotton@nrao.edu.                         */
/*;         Postal address: William Cotton                            */
/*;                         National Radio Astronomy Observatory      */
/*;                         520 Edgemont Road                         */
/*;                         Charlottesville, VA 22903-2475 USA        */
/*--------------------------------------------------------------------*/
#ifndef OBITSPECTRUMFIT_H
#define OBITSPECTRUMFIT_H

#include "Obit.h"
#include "ObitErr.h"
#include "ObitImage.h"
#include "ObitBeamShape.h"
#include "ObitThread.h"
#include "ObitInfoList.h"
#ifdef HAVE_GSL
#include <gsl/gsl_multifit_nlin.h>
#endif /* HAVE_GSL */

/*-------- Obit:  Merx mollis mortibus nuper ------------------*/
/**
 * \file ObitSpectrumFit.h
 *
 * ObitSpectrumFit Class for fitting spectra to image pixels
 *
 * This class does least squares fitting of log(s) as a polynomial in log($\nu$).
 * Either an image cube or a set of single plane images at arbitrary
 * frequencies may be fitted.
 * The result is an image cube of Log(S) with multiples of powers of log($\nu$)
 * as the planes.
 * The function ObitSpectrumFitEval will evaluate this fit and return an image
 * with the flux densities at the desired frequencies.
 *
 * \section ObitSpectrumFitaccess Creators and Destructors
 * An ObitSpectrumFit will usually be created using ObitSpectrumFitCreate which allows
 * specifying a name for the object as well as other information.
 *
 * A copy of a pointer to an ObitSpectrumFit should always be made using the
 * #ObitSpectrumFitRef function which updates the reference count in the object.
 * Then whenever freeing an ObitSpectrumFit or changing a pointer, the function
 * #ObitSpectrumFitUnref will decrement the reference count and destroy the object
 * when the reference count hits 0.
 * There is no explicit destructor.
 */

/*--------------Class definitions-------------------------------------*/
/** ObitSpectrumFit Class structure. */
typedef struct {
#include "ObitSpectrumFitDef.h"   /* this class definition */
} ObitSpectrumFit;

/*----------------- Macroes ---------------------------*/
/**
 * Macro to unreference (and possibly destroy) an ObitSpectrumFit
 * returns a ObitSpectrumFit*.
 * in = object to unreference
 */
#define ObitSpectrumFitUnref(in) ObitUnref (in)

/**
 * Macro to reference (update reference count) an ObitSpectrumFit.
 * returns a ObitSpectrumFit*.
 * in = object to reference
 */
#define ObitSpectrumFitRef(in) ObitRef (in)

/**
 * Macro to determine if an object is the member of this or a
 * derived class.
 * Returns TRUE if a member, else FALSE
 * in = object to reference
 */
#define ObitSpectrumFitIsA(in) ObitIsA (in, ObitSpectrumFitGetClass())

/*---------------Public functions---------------------------*/
/** Public: Class initializer. */
void ObitSpectrumFitClassInit (void);

/** Public: Default Constructor. */
ObitSpectrumFit* newObitSpectrumFit (gchar* name);

/** Public: Create/initialize ObitSpectrumFit structures */
ObitSpectrumFit* ObitSpectrumFitCreate (gchar* name, olong nterm);
/** Typedef for definition of class pointer structure */
typedef ObitSpectrumFit* (*ObitSpectrumFitCreateFP) (gchar* name,
						     olong nterm);

/** Public: ClassInfo pointer */
gconstpointer ObitSpectrumFitGetClass (void);

/** Public: Copy (deep) constructor. */
ObitSpectrumFit* ObitSpectrumFitCopy  (ObitSpectrumFit *in,
				       ObitSpectrumFit *out, ObitErr *err);

/** Public: Copy structure. */
void ObitSpectrumFitClone (ObitSpectrumFit *in, ObitSpectrumFit *out,
			   ObitErr *err);

/** Public: Fit spectrum to an image cube */
void ObitSpectrumFitCube (ObitSpectrumFit* in, ObitImage *inImage,
			  ObitImage *outImage, ObitErr *err);
/** Typedef for definition of class pointer structure */
typedef void(*ObitSpectrumFitCubeFP) (ObitSpectrumFit* in, ObitImage *inImage,
				      ObitImage *outImage, ObitErr *err);

/** Public: Fit spectrum to an array of images */
void ObitSpectrumFitImArr (ObitSpectrumFit* in, olong nimage, ObitImage **imArr,
			   ObitImage *outImage, ObitErr *err);
/** Typedef for definition of class pointer structure */
typedef void(*ObitSpectrumFitImArrFP) (ObitSpectrumFit* in, olong nimage, ObitImage **imArr,
			   ObitImage *outImage, ObitErr *err);

/* Do actual fitting */
void ObitSpectrumFitter (ObitSpectrumFit* in, ObitErr *err);
typedef void(*ObitSpectrumFitterFP) (ObitSpectrumFit* in, ObitErr *err);

/** Public: Evaluate spectrum */
void ObitSpectrumFitEval (ObitSpectrumFit* in, ObitImage *inImage,
			  odouble outFreq, ObitImage *outImage, ObitErr *err);
/** Typedef for definition of class pointer structure */
typedef void(*ObitSpectrumFitEvalFP) (ObitSpectrumFit* in, ObitImage *inImage,
				      odouble outFreq, ObitImage *outImage,
				      ObitErr *err);
/** Private: Write output image */
void ObitSpectrumWriteOutput (ObitSpectrumFit* in, ObitImage *outImage,
			      ObitErr *err);
typedef void (*ObitSpectrumWriteOutputFP) (ObitSpectrumFit* in, ObitImage *outImage,
					   ObitErr *err);
/** Public: Fit single spectrum */
ofloat* ObitSpectrumFitSingle (olong nfreq, olong nterm, odouble refFreq, odouble *freq,
			       ofloat *flux, ofloat *sigma, gboolean doBrokePow,
			       ObitErr *err);
/** Typedef for definition of class pointer structure */
typedef ofloat*(*ObitSpectrumFitSingleFP) (olong nfreq, olong nterm, odouble refFreq,
					   odouble *freq, ofloat *flux, ofloat *sigma,
					   gboolean doBrokePow, ObitErr *err);

/** Public: Make fitting arg structure */
gpointer ObitSpectrumFitMakeArg (olong nfreq, olong nterm,
				 odouble refFreq, odouble *freq,
				 gboolean doBrokePow,
				 ofloat **out, ObitErr *err);

/** Public: Fit single spectrum using arg */
void ObitSpectrumFitSingleArg (gpointer arg, ofloat *flux, ofloat  *sigma,
			       ofloat *out);

/** Public: Kill fitting arg structure */
void ObitSpectrumFitKillArg (gpointer arg);
/*----------- ClassInfo Structure -----------------------------------*/
/**
 * ClassInfo Structure.
 * Contains class name, a pointer to any parent class
 * (NULL if none) and function pointers.
 */
typedef struct  {
#include "ObitSpectrumFitClassDef.h"
} ObitSpectrumFitClassInfo;

#endif /* OBITFSPECTRUMFIT_H */


#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_histogram2d.h>
#include <gsl/gsl_errno.h>

int mgsl_histogram_fwrite(const char *filename, const gsl_histogram *h)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_histogram_fwrite(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram_fread(const char *filename, gsl_histogram *h)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_histogram_fread(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram_fprintf(const char *filename, const gsl_histogram *h, const char *range_format, const char *bin_format)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_histogram_fprintf(fp, h, range_format, bin_format) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram_fscanf(const char *filename, gsl_histogram *h)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_histogram_fscanf(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram2d_fwrite(const char *filename, const gsl_histogram2d *h)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_histogram2d_fwrite(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram2d_fread(const char *filename, gsl_histogram2d *h)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_histogram2d_fread(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram2d_fprintf(const char *filename, const gsl_histogram2d *h, const char *range_format, const char *bin_format)
{
  FILE *fp;
  if((fp = fopen(filename, "w")) == NULL) return GSL_EFAILED;
  if(gsl_histogram2d_fprintf(fp, h, range_format, bin_format) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}

int mgsl_histogram2d_fscanf(const char *filename, gsl_histogram2d *h)
{
  FILE *fp;
  if((fp = fopen(filename, "r")) == NULL) return GSL_EFAILED;
  if(gsl_histogram2d_fscanf(fp, h) != GSL_SUCCESS) return GSL_EFAILED;
  fclose(fp);
  return GSL_SUCCESS;
}


#include <bindings.cmacros.h>
#include <gsl/gsl_math.h>

BC_INLINE2(GSL_FN_EVAL,gsl_function*,double,double)
BC_INLINE2(GSL_FN_FDF_EVAL_F,gsl_function_fdf*,double,double)
BC_INLINE2(GSL_FN_FDF_EVAL_DF,gsl_function_fdf*,double,double)
BC_INLINE3(GSL_FN_VEC_EVAL,gsl_function_vec*,double,double*,double)
BC_INLINE4VOID(GSL_FN_FDF_EVAL_F_DF,gsl_function_fdf*,double,double*,double*)