//  This matrix class is a C++ wrapper for the GNU Scientific Library
//  Copyright (C)  ULP-IPB Strasbourg

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#ifndef _vector_double_h
#define _vector_double_h

#ifdef __HP_aCC
#include <iostream.h>
#else
#include <iostream>
#endif

#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gslwrap/vector_double.h>

//#define NDEBUG 0

#include <assert.h>
namespace gsl
{

#ifndef __HP_aCC
	using std::ostream;
//using std::string;
//using std::runtime_error;
#endif

class vector_view;

class vector
{
protected:
	gsl_vector *gsldata;
	void free(){if(gsldata) gsl_vector_free(gsldata);gsldata=NULL;}
	void alloc(size_t n) {gsldata=gsl_vector_alloc(n);}
	void calloc(size_t n){gsldata=gsl_vector_calloc(n);}
public:
	typedef double value_type;
	vector() : gsldata(NULL) {;}
	vector( const vector &other ):gsldata(NULL) {copy(other);}
	template<class oclass>
	vector( const oclass &other ):gsldata(NULL) {copy(other);}
	~vector(){free();}
	vector(const size_t& n,bool clear=true)
	{
		if(clear){this->calloc(n);}
		else     {this->alloc(n);}
	}
	vector(const int& n,bool clear=true)
	{
		if(clear){this->calloc(n);}
		else     {this->alloc(n);}
	}

	void resize(size_t n);

	template <class oclass>
		void copy(const oclass &other)
		{
			if ( static_cast<const void *>( this ) == static_cast<const void *>( &other ) )
				return;

			if (!other.is_set())
			{
				gsldata=NULL;
				return;
			}
			resize(other.size());
			for (size_t i=0;i<size();i++)
			{
				gsl_vector_set(gsldata, i, (double)other[i]);
			}
		}
	void copy(const vector& other);
	bool is_set() const{if (gsldata) return true; else return false;}
//	void clone(vector& other);

//	size_t size() const {if (!gsldata) {cout << "vector::size vector not initialized" << endl; exit(-1);}return gsldata->size;}
	size_t size() const {assert (gsldata); return gsldata->size;}

	/** for interfacing with gsl c */
/*  	gsl_vector       *gslobj()       {if (!gsldata){cout << "vector::gslobj ERROR, data not initialized!! " << endl; exit(-1);}return gsldata;} */
/*  	const gsl_vector *gslobj() const {if (!gsldata){cout << "vector::gslobj ERROR, data not initialized!! " << endl; exit(-1);}return gsldata;} */
	gsl_vector       *gslobj()       {assert(gsldata);return gsldata;}
	const gsl_vector *gslobj() const {assert(gsldata);return gsldata;}

	static vector_view create_vector_view( const gsl_vector_view &other );

// ********Accessing vector elements

//  Unlike FORTRAN compilers, C compilers do not usually provide support for range checking of vectors and matrices (2). However, the functions gsl_vector_get and gsl_vector_set can perform range checking for you and report an error if you attempt to access elements outside the allowed range.

//  The functions for accessing the elements of a vector or matrix are defined in `gsl_vector.h' and declared extern inline to eliminate function-call overhead. If necessary you can turn off range checking completely without modifying any source files by recompiling your program with the preprocessor definition GSL_RANGE_CHECK_OFF. Provided your compiler supports inline functions the effect of turning off range checking is to replace calls to gsl_vector_get(v,i) by v->data[i*v->stride] and and calls to gsl_vector_set(v,i,x) by v->data[i*v->stride]=x. Thus there should be no performance penalty for using the range checking functions when range checking is turned off.

//      This function returns the i-th element of a vector v. If i lies outside the allowed range of 0 to n-1 then the error handler is invoked and 0 is returned.
	double get(size_t i) const {return gsl_vector_get(gsldata,i);}

//      This function sets the value of the i-th element of a vector v to x. If i lies outside the allowed range of 0 to n-1 then the error handler is invoked.
	void  set(size_t i,double x){gsl_vector_set(gsldata,i,x);}

//      These functions return a pointer to the i-th element of a vector v. If i lies outside the allowed range of 0 to n-1 then the error handler is invoked
	double       &operator[](size_t i)       { return *gsl_vector_ptr(gsldata,i);}
	const double &operator[](size_t i) const { return *gsl_vector_ptr(gsldata,i);}

	double       &operator()(size_t i)       { return *gsl_vector_ptr(gsldata,i);}
	const double &operator()(size_t i) const { return *gsl_vector_ptr(gsldata,i);}

//  ***** Initializing vector elements

//      This function sets all the elements of the vector v to the value x.
	void set_all(double x){gsl_vector_set_all (gsldata,x);}
//      This function sets all the elements of the vector v to zero.
	void set_zero(){gsl_vector_set_zero (gsldata);}

//      This function makes a basis vector by setting all the elements of the vector v to zero except for the i-th element which is set to one.
	int set_basis (size_t i) {return gsl_vector_set_basis (gsldata,i);}

//  **** Reading and writing vectors

//  The library provides functions for reading and writing vectors to a file as binary data or formatted text.

//      This function writes the elements of the vector v to the stream stream in binary format. The return value is 0 for success and GSL_EFAILED if there was a problem writing to the file. Since the data is written in the native binary format it may not be portable between different architectures.
	int fwrite (FILE * stream) const {return gsl_vector_fwrite (stream, gsldata);}

//      This function reads into the vector v from the open stream stream in binary format. The vector v must be preallocated with the correct length since the function uses the size of v to determine how many bytes to read. The return value is 0 for success and GSL_EFAILED if there was a problem reading from the file. The data is assumed to have been written in the native binary format on the same architecture.
	int fread (FILE * stream) {return gsl_vector_fread (stream, gsldata);}

	void load( const char *filename );
	///
	void save( const char *filename ) const;

//      This function writes the elements of the vector v line-by-line to the stream stream using the format specifier format, which should be one of the %g, %e or %f formats for floating point numbers and %d for integers. The function returns 0 for success and GSL_EFAILED if there was a problem writing to the file.
	int fprintf (FILE * stream, const char * format) const {return gsl_vector_fprintf (stream, gsldata,format) ;}

//      This function reads formatted data from the stream stream into the vector v. The vector v must be preallocated with the correct length since the function uses the size of v to determine how many numbers to read. The function returns 0 for success and GSL_EFAILED if there was a problem reading from the file.
	int fscanf (FILE * stream)  {return gsl_vector_fscanf (stream, gsldata); }

//  ******* Vector views

//  In addition to creating vectors from slices of blocks it is also possible to slice vectors and create vector views. For example, a subvector of another vector can be described with a view, or two views can be made which provide access to the even and odd elements of a vector.

//  A vector view is a temporary object, stored on the stack, which can be used to operate on a subset of vector elements. Vector views can be defined for both constant and non-constant vectors, using separate types that preserve constness. A vector view has the type gsl_vector_view and a constant vector view has the type gsl_vector_const_view. In both cases the elements of the view can be accessed as a gsl_vector using the vector component of the view object. A pointer to a vector of type gsl_vector * or const gsl_vector * can be obtained by taking the address of this component with the & operator.

//      These functions return a vector view of a subvector of another vector v. The start of the new vector is offset by offset elements from the start of the original
//      vector. The new vector has n elements. Mathematically, the i-th element of the new vector v' is given by,

//      v'(i) = v->data[(offset + i)*v->stride]

//      where the index i runs from 0 to n-1.

//      The data pointer of the returned vector struct is set to null if the combined parameters (offset,n) overrun the end of the original vector.

//      The new vector is only a view of the block underlying the original vector, v. The block containing the elements of v is not owned by the new vector. When the
//      new vector goes out of scope the original vector v and its block will continue to exist. The original memory can only be deallocated by freeing the original vector.
//      Of course, the original vector should not be deallocated while the new vector is still in use.

//      The function gsl_vector_const_subvector is equivalent to gsl_vector_subvector but can be used for vectors which are declared const.
	vector_view subvector (size_t offset, size_t n);
	const vector_view subvector (size_t offset, size_t n) const;
//	vector_const_view subvector (size_t offset, size_t n) const;

//  	class view
//  	{
//  		gsl_vector_view *gsldata;
//  	public:
//  		view();
//  	};
//  	view subvector(size_t offset, size_t n)
//  	{
//  		return view(gsl_vector_subvector(gsldata,offset,n);
//  	}
//  	const view subvector(size_t offset, size_t n) const
//  	{
//  		return view(gsl_vector_const_subvector(gsldata,offset,n);
//  	}

//  Function: gsl_vector gsl_vector_subvector_with_stride (gsl_vector *v, size_t offset, size_t stride, size_t n)
//  Function: gsl_vector_const_view gsl_vector_const_subvector_with_stride (const gsl_vector * v, size_t offset, size_t stride, size_t n)
//      These functions return a vector view of a subvector of another vector v with an additional stride argument. The subvector is formed in the same way as for
//      gsl_vector_subvector but the new vector has n elements with a step-size of stride from one element to the next in the original vector. Mathematically,
//      the i-th element of the new vector v' is given by,

//      v'(i) = v->data[(offset + i*stride)*v->stride]

//      where the index i runs from 0 to n-1.

//      Note that subvector views give direct access to the underlying elements of the original vector. For example, the following code will zero the even elements of the
//      vector v of length n, while leaving the odd elements untouched,

//      gsl_vector_view v_even = gsl_vector_subvector_with_stride (v, 0, 2, n/2);
//      gsl_vector_set_zero (&v_even.vector);

//      A vector view can be passed to any subroutine which takes a vector argument just as a directly allocated vector would be, using &view.vector. For example, the
//      following code computes the norm of odd elements of v using the BLAS routine DNRM2,

//      gsl_vector_view v_odd = gsl_vector_subvector_with_stride (v, 1, 2, n/2);
//      double r = gsl_blas_dnrm2 (&v_odd.vector);

//      The function gsl_vector_const_subvector_with_stride is equivalent to gsl_vector_subvector_with_stride but can be used for
//      vectors which are declared const.

//  Function: gsl_vector_view gsl_vector_complex_real (gsl_vector_complex *v)
//  Function: gsl_vector_const_view gsl_vector_complex_const_real (const gsl_vector_complex *v)
//      These functions return a vector view of the real parts of the complex vector v.

//      The function gsl_vector_complex_const_real is equivalent to gsl_vector_complex_real but can be used for vectors which are declared
//      const.

//  Function: gsl_vector_view gsl_vector_complex_imag (gsl_vector_complex *v)
//  Function: gsl_vector_const_view gsl_vector_complex_const_imag (const gsl_vector_complex *v)
//      These functions return a vector view of the imaginary parts of the complex vector v.

//      The function gsl_vector_complex_const_imag is equivalent to gsl_vector_complex_imag but can be used for vectors which are declared
//      const.

//  Function: gsl_vector_view gsl_vector_view_array (double *base, size_t n)
//  Function: gsl_vector_const_view gsl_vector_const_view_array (const double *base, size_t n)
//      These functions return a vector view of an array. The start of the new vector is given by base and has n elements. Mathematically, the i-th element of the new
//      vector v' is given by,

//      v'(i) = base[i]

//      where the index i runs from 0 to n-1.

//      The array containing the elements of v is not owned by the new vector view. When the view goes out of scope the original array will continue to exist. The
//      original memory can only be deallocated by freeing the original pointer base. Of course, the original array should not be deallocated while the view is still in use.

//      The function gsl_vector_const_view_array is equivalent to gsl_vector_view_array but can be used for vectors which are declared const.

//  Function: gsl_vector_view gsl_vector_view_array_with_stride (double * base, size_t stride, size_t n)
//  Function: gsl_vector_const_view gsl_vector_const_view_array_with_stride (const double * base, size_t stride, size_t n)
//      These functions return a vector view of an array base with an additional stride argument. The subvector is formed in the same way as for
//      gsl_vector_view_array but the new vector has n elements with a step-size of stride from one element to the next in the original array. Mathematically,
//      the i-th element of the new vector v' is given by,

//      v'(i) = base[i*stride]

//      where the index i runs from 0 to n-1.

//      Note that the view gives direct access to the underlying elements of the original array. A vector view can be passed to any subroutine which takes a vector
//      argument just as a directly allocated vector would be, using &view.vector.

//      The function gsl_vector_const_view_array_with_stride is equivalent to gsl_vector_view_array_with_stride but can be used for
//      arrays which are declared const.

//  ************* Copying vectors

//  Common operations on vectors such as addition and multiplication are available in the BLAS part of the library (see section BLAS Support). However, it is useful to have a small number of utility functions which do not require the full BLAS code. The following functions fall into this category.

//      This function copies the elements of the vector src into the vector dest.
	vector& operator=(const vector& other){copy(other);return (*this);}

//  Function: int gsl_vector_swap (gsl_vector * v, gsl_vector * w)
//      This function exchanges the elements of the vectors v and w by copying. The two vectors must have the same length.

//  ***** Exchanging elements

//  The following function can be used to exchange, or permute, the elements of a vector.

//  Function: int gsl_vector_swap_elements (gsl_vector * v, size_t i, size_t j)
//      This function exchanges the i-th and j-th elements of the vector v in-place.
	int swap_elements (size_t i, size_t j) {return gsl_vector_swap_elements (gsldata, i,j);}

//  Function: int gsl_vector_reverse (gsl_vector * v)
//      This function reverses the order of the elements of the vector v.
	int reverse () {return  gsl_vector_reverse (gsldata) ;}

// ******* Vector operations

//  The following operations are only defined for real vectors.

//      This function adds the elements of vector b to the elements of vector a, a'_i = a_i + b_i. The two vectors must have the same length.
	int operator+=(const vector &other) {return gsl_vector_add (gsldata, other.gsldata);}

//      This function subtracts the elements of vector b from the elements of vector a, a'_i = a_i - b_i. The two vectors must have the same length.
	int operator-=(const vector &other) {return gsl_vector_sub (gsldata, other.gsldata);}

//  Function: int gsl_vector_mul (gsl_vector * a, const gsl_vector * b)
//      This function multiplies the elements of vector a by the elements of vector b, a'_i = a_i * b_i. The two vectors must have the same length.
	int operator*=(const vector &other) {return gsl_vector_mul (gsldata, other.gsldata);}

//      This function divides the elements of vector a by the elements of vector b, a'_i = a_i / b_i. The two vectors must have the same length.
	int operator/=(const vector &other) {return gsl_vector_div (gsldata, other.gsldata);}

//      This function multiplies the elements of vector a by the constant factor x, a'_i = x a_i.
	int operator*=(double x) {return gsl_vector_scale (gsldata, x);}

//  Function: int gsl_vector_add_constant (gsl_vector * a, const double x)
//      This function adds the constant value x to the elements of the vector a, a'_i = a_i + x.
	int operator+=(double x) {return gsl_vector_add_constant (gsldata,x);}

//      This function multiplies the elements of vector a by the constant factor x, a'_i = x a_i.
	int operator/=(double x) {return gsl_vector_scale (gsldata, 1/x);}

// bool operators:
	bool operator==(const vector& other) const;
	bool operator!=(const vector& other) const { return (!((*this)==other));}

// stream output:
//	friend ostream& operator<< ( ostream& os, const vector& vect );
	/** returns sum of all the vector elements. */
    double sum() const;
	// returns sqrt(v.t*v);
    double norm2() const;

// **** Finding maximum and minimum elements of vectors

//      This function returns the maximum value in the vector v.
    double max() const{return gsl_vector_max (gsldata) ;}

//  Function: double gsl_vector_min (const gsl_vector * v)
//      This function returns the minimum value in the vector v.
    double min() const{return gsl_vector_min (gsldata) ;}

//  Function: void gsl_vector_minmax (const gsl_vector * v, double * min_out, double * max_out)
//      This function returns the minimum and maximum values in the vector v, storing them in min_out and max_out.

//      This function returns the index of the maximum value in the vector v. When there are several equal maximum elements then the lowest index is returned.
	size_t max_index(){return gsl_vector_max_index (gsldata);}

//  Function: size_t gsl_vector_min_index (const gsl_vector * v)
//      This function returns the index of the minimum value in the vector v. When there are several equal minimum elements then the lowest index is returned.
	size_t min_index(){return gsl_vector_min_index (gsldata);}

//  Function: void gsl_vector_minmax_index (const gsl_vector * v, size_t * imin, size_t * imax)
//      This function returns the indices of the minimum and maximum values in the vector v, storing them in imin and imax. When there are several equal minimum
//      or maximum elements then the lowest indices are returned.

//  Vector properties

//  Function: int gsl_vector_isnull (const gsl_vector * v)
//      This function returns 1 if all the elements of the vector v are zero, and 0 otherwise. };
	bool isnull(){return gsl_vector_isnull (gsldata);}
};

// When you add create a view it will stick to its with the view until you call change_view
// ex:
// matrix_float m(5,5);
// vector_float v(5);
// // ...
// m.column(3) = v; //the 3rd column of the matrix m will equal v.
class vector_view : public vector
{
 public:
	vector_view(const vector&     other) :vector(){init(other);}
	vector_view(const vector_view& other):vector(){init(other);}
	vector_view(const gsl_vector& gsl_other) : vector() {init_with_gsl_vector(gsl_other);}

	void init(const vector& other);
	void init_with_gsl_vector(const gsl_vector& gsl_other);
	void change_view(const vector& other){init(other);}
 private:
};

ostream& operator<< ( ostream& os, const vector & vect );

// vector_type<>::type is a template interface to vector_?
// it is usefull for in templated situations for getting the correct vector type
#define tmp_type_is
#ifdef tmp_type_is
typedef vector vector_double;
template<class T>
struct vector_type  {typedef vector_double   type;};

template<class T>
struct value_type  {typedef double   type;};

#else
template<> struct vector_type<double> {typedef vector type;};
#endif
#undef tmp_type_is

}
#endif// _vector_double_h


/* -*- linux-c -*- */
/* fewbody_hier.c

   Copyright (C) 2002-2004 John M. Fregeau

   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include "fewbody.h"

/* allocate memory for a hier_t */
void fb_malloc_hier(fb_hier_t *hier)
{
	int i;

	hier->hi = (int *) malloc(hier->nstarinit * sizeof(int)) - 1;
	fb_create_indices(hier->hi, hier->nstarinit);
	hier->narr = (int *) malloc((hier->nstarinit-1)*sizeof(int)) - 2;
	/* change from malloc to calloc to get rid of harmless valgrind errors...
	   the errors occur in fb_normalize(), where uninitialized memory is normalized -
	   these are clearly harmless */
	hier->hier = (fb_obj_t *) calloc(hier->hi[hier->nstarinit] + 1, sizeof(fb_obj_t));
	for (i=0; i<hier->hi[hier->nstarinit]+1; i++) {
		hier->hier[i].ncoll = 0;
		hier->hier[i].id = (long *) malloc(hier->nstarinit * sizeof(long));
	}
	hier->obj = (fb_obj_t **) malloc(hier->nstarinit * sizeof(fb_obj_t *));
}

/* initialize to a flat hier */
void fb_init_hier(fb_hier_t *hier)
{
	int i;

	hier->nobj = hier->nstar;

	for (i=2; i<=hier->nstar; i++) {
		hier->narr[i] = 0;
	}

	for (i=0; i<hier->nstar; i++) {
		hier->obj[i] = &(hier->hier[hier->hi[1]+i]);
	}
}

/* free memory */
void fb_free_hier(fb_hier_t hier)
{
	int i;

	for (i=0; i<hier.hi[hier.nstarinit]+1; i++) {
		free(hier.hier[i].id);
	}
	free(hier.hi+1);
	free(hier.narr+2);
	free(hier.hier);
	free(hier.obj);
}

/* trickle down hier */
void fb_trickle(fb_hier_t *hier, double t)
{
	int i, j;

	for (i=hier->nstar; i>=2; i--) {
		for (j=0; j<hier->narr[i]; j++) {
			fb_downsync(&(hier->hier[hier->hi[i] + j]), t);
		}
	}
}

/* trickle up hier */
void fb_elkcirt(fb_hier_t *hier, double t)
{
	int i, j;

	for (i=2; i<=hier->nstar; i++) {
		for (j=0; j<hier->narr[i]; j++) {
			fb_upsync(&(hier->hier[hier->hi[i] + j]), t);
		}
	}
}

/* created the index array */
int fb_create_indices(int *hi, int nstar)
{
	int i, j=0;

	for (i=1; i<=nstar; i++) {
		hi[i] = j;
		j += nstar/i;
	}

	return(0);
}

/* how many members are in the immediate hierarchy */
int fb_n_hier(fb_obj_t *obj)
{
	if (obj == NULL) {
		return(0);
	} else if ((obj->obj[0] == NULL) && (obj->obj[1] == NULL)) {
		return(1);
	} else if ((obj->obj[0]->obj[0] == NULL) && (obj->obj[0]->obj[1] == NULL) && (obj->obj[1]->obj[0] == NULL) && (obj->obj[1]->obj[1] == NULL)) {
		return(2);
	} else if (((obj->obj[0]->obj[0] == NULL) && (obj->obj[0]->obj[1] == NULL)) || ((obj->obj[1]->obj[0] == NULL) && (obj->obj[1]->obj[1] == NULL))) {
		return(3);
	} else {
		return(4);
	}
}

/* print the hierarchy information */
char *fb_sprint_hier(fb_hier_t hier, char string[FB_MAX_STRING_LENGTH])
{
	int i;

	snprintf(string, FB_MAX_STRING_LENGTH, "nstar=%d nobj=%d: ", hier.nstar, hier.nobj);
	for (i=0; i<hier.nobj; i++) {
		snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), " %s", hier.obj[i]->idstring);
	}

	return(string);
}

/* print the hierarchy information in a nice human-readable format */
char *fb_sprint_hier_hr(fb_hier_t hier, char string[FB_MAX_STRING_LENGTH])
{
	int i;

	/* zero out string so strlen(string) returns 0 */
	string[0] = '\0';

	/* loop through objects */
	for (i=0; i<hier.nobj; i++) {
		/* put a dash inbetween labels */
		if (i > 0) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "-");
		}
		/* a name for each type of hierarchy */
		if (hier.obj[i]->n == 1) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "single");
		} else if (hier.obj[i]->n == 2) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "binary");
		} else if (hier.obj[i]->n == 3) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "triple");
		} else if (hier.obj[i]->n == 4) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "quadruple");
		} else if (hier.obj[i]->n == 5) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "quintuple");
		} else if (hier.obj[i]->n == 6) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "sextuple");
		} else if (hier.obj[i]->n == 7) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "septuple");
		} else if (hier.obj[i]->n == 8) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "octuple");
		} else if (hier.obj[i]->n == 9) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "nontuple");
		} else if (hier.obj[i]->n == 10) {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "decituple");
		} else {
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "%d", hier.obj[i]->n);
			snprintf(&(string[strlen(string)]), FB_MAX_STRING_LENGTH-strlen(string), "tuple");
		}
	}

	return(string);
}

/* merge the object's properties up---calculate the binary's properties from the
   stars' properties */
void fb_upsync(fb_obj_t *obj, double t)
{
	int i;
	double m0, m1, x0[3], x1[3], xrel[3], v0[3], v1[3], vrel[3], E, l0[3], l1[3], l[3];
	double ypp[3], psi, ecc_anom, L[3], A[3];

	/* a little bit of paranoia here */
	obj->ncoll = 0;
	obj->id[0] = -1;

	/* set time of upsync */
	obj->t = t;

	/* create idstring */
	snprintf(obj->idstring, FB_MAX_STRING_LENGTH, "[%s %s]", obj->obj[0]->idstring, obj->obj[1]->idstring);

	/* update number of stars in hierarchy */
	obj->n = obj->obj[0]->n + obj->obj[1]->n;

	/* update mass */
	m0 = obj->obj[0]->m;
	m1 = obj->obj[1]->m;
	obj->m = m0 + m1;

	/* update position and velocity */
	for (i=0; i<3; i++) {
		obj->x[i] = (m0*(obj->obj[0]->x[i]) + m1*(obj->obj[1]->x[i])) / (obj->m);
		obj->v[i] = (m0*(obj->obj[0]->v[i]) + m1*(obj->obj[1]->v[i])) / (obj->m);
	}

	/* update semimajor axis */
	for (i=0; i<3; i++) {
		x0[i] = obj->obj[0]->x[i] - obj->x[i];
		x1[i] = obj->obj[1]->x[i] - obj->x[i];
		xrel[i] = obj->obj[0]->x[i] - obj->obj[1]->x[i];
		v0[i] = obj->obj[0]->v[i] - obj->v[i];
		v1[i] = obj->obj[1]->v[i] - obj->v[i];
		vrel[i] = obj->obj[0]->v[i] - obj->obj[1]->v[i];
	}

	E = 0.5*(m0*fb_dot(v0, v0) + m1*fb_dot(v1, v1)) - m0*m1/fb_mod(xrel);

	if (E >= 0.0) {
		fprintf(stderr, "fb_upsync: E = %g >= 0!\n", E);
		exit(1);
	}

	obj->a = -m0 * m1 / (2.0 * E);

	/* update angular momentum, Runge-Lenz vector, and eccentricity (using the Runge-Lenz vector) */
	fb_cross(x0, v0, l0);
	fb_cross(x1, v1, l1);

	for (i=0; i<3; i++) {
		L[i] = m0 * l0[i] + m1 * l1[i];
		l[i] = L[i] * (m0+m1)/(m0*m1);
	}

	/* -A = l x v + G M \hat r */
	fb_cross(vrel, l, A);
	for (i=0; i<3; i++) {
		A[i] -= (m0+m1) * xrel[i]/fb_mod(xrel);
	}

	obj->e = fb_mod(A)/(m0+m1);

	/* must be careful with circular orbits, which have a null A vector */
	if (obj->e == 0.0) {
		for (i=0; i<3; i++) {
			A[i] = xrel[i];
		}
	}

	/* define coord system */
	for (i=0; i<3; i++) {
		obj->Lhat[i] = L[i]/fb_mod(L); /* z-direction */
		obj->Ahat[i] = A[i]/fb_mod(A); /* x-direction */
	}
	fb_cross(obj->Lhat, obj->Ahat, ypp);

	/* and set mean anomaly */
	psi = atan2(fb_dot(x0, ypp), fb_dot(x0, obj->Ahat));
	ecc_anom = acos((obj->e + cos(psi)) / (1.0 + obj->e * cos(psi)));
	if (psi < 0.0) {
		ecc_anom = 2.0 * FB_CONST_PI - ecc_anom;
	}
	obj->mean_anom = ecc_anom - obj->e * sin(ecc_anom);
}

/* randomly orient binary */
void fb_randorient(fb_obj_t *obj, gsl_rng *rng)
{
	int i;
	double theta, phi, omega, xp[3], yp[3], zp[3];

	/* random angles */
	theta = acos(2.0 * gsl_rng_uniform(rng) - 1.0);
	phi = 2.0 * FB_CONST_PI * gsl_rng_uniform(rng);
	omega = 2.0 * FB_CONST_PI * gsl_rng_uniform(rng);
	obj->mean_anom = 2.0 * FB_CONST_PI * gsl_rng_uniform(rng);

	/* set up coordinate transformations */
	zp[0] = -sin(theta) * sin(phi);
	zp[1] = sin(theta) * cos(phi);
	zp[2] = cos(theta);

	xp[0] = cos(phi);
	xp[1] = sin(phi);
	xp[2] = 0.0;

	fb_cross(zp, xp, yp);

	for (i=0; i<3; i++) {
		obj->Lhat[i] = zp[i];
		obj->Ahat[i] = xp[i] * cos(omega) + yp[i] * sin(omega);
	}
}

/* merge the object's properties down---calculate the objs' properties from the
   binary's properties */
void fb_downsync(fb_obj_t *obj, double t)
{
	int i;
	double xpp[3], ypp[3], zpp[3], er[3], epsi[3];
	double a, e, m0, m1, omega, mean_anom, ecc_anom, psi, r0, r1, L, psidot, E, r0dot, r0dot2, r1dot;

	/* we're assuming here that the objs' masses are already set */
	a = obj->a;
	e = obj->e;
	m0 = obj->obj[0]->m;
	m1 = obj->obj[1]->m;

	/* angular frequency */
	omega = sqrt(obj->m/fb_cub(a));
	/* mean anomaly, between 0 and 2PI */
	mean_anom = (obj->mean_anom + omega * (t - obj->t)) / (2.0 * FB_CONST_PI);
	mean_anom = (mean_anom - floor(mean_anom)) * 2.0 * FB_CONST_PI;
	/* eccentric anomaly, from solving the Kepler equation */
	ecc_anom = fb_kepler(e, mean_anom);
	/* true anomaly, between 0 and 2PI */
	psi = acos((cos(ecc_anom) - e) / (1.0 - e * cos(ecc_anom)));
	/* this step is necessary because acos() returns a value between 0 and PI */
	if (ecc_anom > FB_CONST_PI) {
		psi = 2.0 * FB_CONST_PI - psi;
	}

	/* define vectors, based on angular momentum and Runge-Lenz vectors */
	for (i=0; i<3; i++) {
		zpp[i] = obj->Lhat[i];
		xpp[i] = obj->Ahat[i];
	}
	fb_cross(zpp, xpp, ypp);

	for (i=0; i<3; i++) {
		er[i] = xpp[i] * cos(psi) + ypp[i] * sin(psi);
		epsi[i] = ypp[i] * cos(psi) - xpp[i] * sin(psi);
	}

	/* determine binary params */
	r0 = a * (1.0 - fb_sqr(e)) / ((1.0 + e*cos(psi)) * (1.0 + m0/m1));
	r1 = a * (1.0 - fb_sqr(e)) / ((1.0 + e*cos(psi)) * (1.0 + m1/m0));

	L = m0 * m1 * sqrt((m0+m1)*a*(1.0-fb_sqr(e))) / (m0 + m1);
	psidot = L / (m0 * fb_sqr(r0) + m1 * fb_sqr(r1));
	E = -m0 * m1 / (2.0 * a);

	/* must be careful for circular orbits */
	r0dot2 = (E + m0*m1/(r0+r1)) * 2.0 / (m0 * (1.0+m0/m1)) - fb_sqr(r0*psidot);
	if (ecc_anom > FB_CONST_PI) {
		r0dot = -(r0dot2<=0.0?0.0:sqrt(r0dot2));
	} else {
		r0dot = (r0dot2<=0.0?0.0:sqrt(r0dot2));
	}
	r1dot = m0 * r0dot / m1;

	for (i=0; i<3; i++) {
		obj->obj[0]->x[i] = r0 * er[i] + obj->x[i];
		obj->obj[0]->v[i] = r0dot * er[i] + r0 * psidot * epsi[i] + obj->v[i];
		obj->obj[1]->x[i] = -r1 * er[i] + obj->x[i];
		obj->obj[1]->v[i] = -r1dot * er[i] - r1 * psidot * epsi[i] + obj->v[i];
	}
}

/* copy one object to another, being careful about any pointers */
void fb_objcpy(fb_obj_t *obj1, fb_obj_t *obj2)
{
	int i;
	long *longptr;

	for (i=0; i<obj2->ncoll; i++) {
		obj1->id[i] = obj2->id[i];
	}

	/* prevent any dangling pointers */
	longptr = obj1->id;
	*obj1 = *obj2;
	obj1->id = longptr;
}


#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sum.h>

gsl_sum_levin_utrunc_workspace *
gsl_sum_levin_utrunc_alloc (size_t n)
{
  gsl_sum_levin_utrunc_workspace * w;

  if (n == 0)
    {
      GSL_ERROR_VAL ("length n must be positive integer", GSL_EDOM, 0);
    }

  w = (gsl_sum_levin_utrunc_workspace *) malloc(sizeof(gsl_sum_levin_utrunc_workspace));

  if (w == NULL)
    {
      GSL_ERROR_VAL ("failed to allocate struct", GSL_ENOMEM, 0);
    }

  w->q_num = (double *) malloc (n * sizeof (double));

  if (w->q_num == NULL)
    {
      free(w) ; /* error in constructor, prevent memory leak */

      GSL_ERROR_VAL ("failed to allocate space for q_num", GSL_ENOMEM, 0);
    }

  w->q_den = (double *) malloc (n * sizeof (double));

  if (w->q_den == NULL)
    {
      free (w->q_num);
      free (w) ; /* error in constructor, prevent memory leak */

      GSL_ERROR_VAL ("failed to allocate space for q_den", GSL_ENOMEM, 0);
    }

  w->dsum = (double *) malloc (n * sizeof (double));

  if (w->dsum == NULL)
    {
      free (w->q_den);
      free (w->q_num);
      free (w) ; /* error in constructor, prevent memory leak */

      GSL_ERROR_VAL ("failed to allocate space for dsum", GSL_ENOMEM, 0);
    }

  w->size = n;
  w->terms_used = 0;
  w->sum_plain = 0;

  return w;
}

void
gsl_sum_levin_utrunc_free (gsl_sum_levin_utrunc_workspace * w)
{
  RETURN_IF_NULL (w);
  free (w->dsum);
  free (w->q_den);
  free (w->q_num);
  free (w);
}


/* matrix/gsl_matrix_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_MATRIX_FLOAT_H__
#define __GSL_MATRIX_FLOAT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_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 size1;
  size_t size2;
  size_t tda;
  float * data;
  gsl_block_float * block;
  int owner;
} gsl_matrix_float;

typedef struct
{
  gsl_matrix_float matrix;
} _gsl_matrix_float_view;

typedef _gsl_matrix_float_view gsl_matrix_float_view;

typedef struct
{
  gsl_matrix_float matrix;
} _gsl_matrix_float_const_view;

typedef const _gsl_matrix_float_const_view gsl_matrix_float_const_view;

/* Allocation */

gsl_matrix_float *
gsl_matrix_float_alloc (const size_t n1, const size_t n2);

gsl_matrix_float *
gsl_matrix_float_calloc (const size_t n1, const size_t n2);

gsl_matrix_float *
gsl_matrix_float_alloc_from_block (gsl_block_float * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

gsl_matrix_float *
gsl_matrix_float_alloc_from_matrix (gsl_matrix_float * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

gsl_vector_float *
gsl_vector_float_alloc_row_from_matrix (gsl_matrix_float * m,
                                        const size_t i);

gsl_vector_float *
gsl_vector_float_alloc_col_from_matrix (gsl_matrix_float * m,
                                        const size_t j);

void gsl_matrix_float_free (gsl_matrix_float * m);

/* Views */

_gsl_matrix_float_view
gsl_matrix_float_submatrix (gsl_matrix_float * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

_gsl_vector_float_view
gsl_matrix_float_row (gsl_matrix_float * m, const size_t i);

_gsl_vector_float_view
gsl_matrix_float_column (gsl_matrix_float * m, const size_t j);

_gsl_vector_float_view
gsl_matrix_float_diagonal (gsl_matrix_float * m);

_gsl_vector_float_view
gsl_matrix_float_subdiagonal (gsl_matrix_float * m, const size_t k);

_gsl_vector_float_view
gsl_matrix_float_superdiagonal (gsl_matrix_float * m, const size_t k);

_gsl_vector_float_view
gsl_matrix_float_subrow (gsl_matrix_float * m, const size_t i,
                         const size_t offset, const size_t n);

_gsl_vector_float_view
gsl_matrix_float_subcolumn (gsl_matrix_float * m, const size_t j,
                            const size_t offset, const size_t n);

_gsl_matrix_float_view
gsl_matrix_float_view_array (float * base,
                             const size_t n1,
                             const size_t n2);

_gsl_matrix_float_view
gsl_matrix_float_view_array_with_tda (float * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

_gsl_matrix_float_view
gsl_matrix_float_view_vector (gsl_vector_float * v,
                              const size_t n1,
                              const size_t n2);

_gsl_matrix_float_view
gsl_matrix_float_view_vector_with_tda (gsl_vector_float * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

_gsl_matrix_float_const_view
gsl_matrix_float_const_submatrix (const gsl_matrix_float * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

_gsl_vector_float_const_view
gsl_matrix_float_const_row (const gsl_matrix_float * m,
                            const size_t i);

_gsl_vector_float_const_view
gsl_matrix_float_const_column (const gsl_matrix_float * m,
                               const size_t j);

_gsl_vector_float_const_view
gsl_matrix_float_const_diagonal (const gsl_matrix_float * m);

_gsl_vector_float_const_view
gsl_matrix_float_const_subdiagonal (const gsl_matrix_float * m,
                                    const size_t k);

_gsl_vector_float_const_view
gsl_matrix_float_const_superdiagonal (const gsl_matrix_float * m,
                                      const size_t k);

_gsl_vector_float_const_view
gsl_matrix_float_const_subrow (const gsl_matrix_float * m, const size_t i,
                               const size_t offset, const size_t n);

_gsl_vector_float_const_view
gsl_matrix_float_const_subcolumn (const gsl_matrix_float * m, const size_t j,
                                  const size_t offset, const size_t n);

_gsl_matrix_float_const_view
gsl_matrix_float_const_view_array (const float * base,
                                   const size_t n1,
                                   const size_t n2);

_gsl_matrix_float_const_view
gsl_matrix_float_const_view_array_with_tda (const float * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

_gsl_matrix_float_const_view
gsl_matrix_float_const_view_vector (const gsl_vector_float * v,
                                    const size_t n1,
                                    const size_t n2);

_gsl_matrix_float_const_view
gsl_matrix_float_const_view_vector_with_tda (const gsl_vector_float * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

void gsl_matrix_float_set_zero (gsl_matrix_float * m);
void gsl_matrix_float_set_identity (gsl_matrix_float * m);
void gsl_matrix_float_set_all (gsl_matrix_float * m, float x);

int gsl_matrix_float_fread (FILE * stream, gsl_matrix_float * m) ;
int gsl_matrix_float_fwrite (FILE * stream, const gsl_matrix_float * m) ;
int gsl_matrix_float_fscanf (FILE * stream, gsl_matrix_float * m);
int gsl_matrix_float_fprintf (FILE * stream, const gsl_matrix_float * m, const char * format);

int gsl_matrix_float_memcpy(gsl_matrix_float * dest, const gsl_matrix_float * src);
int gsl_matrix_float_swap(gsl_matrix_float * m1, gsl_matrix_float * m2);

int gsl_matrix_float_swap_rows(gsl_matrix_float * m, const size_t i, const size_t j);
int gsl_matrix_float_swap_columns(gsl_matrix_float * m, const size_t i, const size_t j);
int gsl_matrix_float_swap_rowcol(gsl_matrix_float * m, const size_t i, const size_t j);
int gsl_matrix_float_transpose (gsl_matrix_float * m);
int gsl_matrix_float_transpose_memcpy (gsl_matrix_float * dest, const gsl_matrix_float * src);

float gsl_matrix_float_max (const gsl_matrix_float * m);
float gsl_matrix_float_min (const gsl_matrix_float * m);
void gsl_matrix_float_minmax (const gsl_matrix_float * m, float * min_out, float * max_out);

void gsl_matrix_float_max_index (const gsl_matrix_float * m, size_t * imax, size_t *jmax);
void gsl_matrix_float_min_index (const gsl_matrix_float * m, size_t * imin, size_t *jmin);
void gsl_matrix_float_minmax_index (const gsl_matrix_float * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

int gsl_matrix_float_equal (const gsl_matrix_float * a, const gsl_matrix_float * b);

int gsl_matrix_float_isnull (const gsl_matrix_float * m);
int gsl_matrix_float_ispos (const gsl_matrix_float * m);
int gsl_matrix_float_isneg (const gsl_matrix_float * m);
int gsl_matrix_float_isnonneg (const gsl_matrix_float * m);

int gsl_matrix_float_add (gsl_matrix_float * a, const gsl_matrix_float * b);
int gsl_matrix_float_sub (gsl_matrix_float * a, const gsl_matrix_float * b);
int gsl_matrix_float_mul_elements (gsl_matrix_float * a, const gsl_matrix_float * b);
int gsl_matrix_float_div_elements (gsl_matrix_float * a, const gsl_matrix_float * b);
int gsl_matrix_float_scale (gsl_matrix_float * a, const double x);
int gsl_matrix_float_add_constant (gsl_matrix_float * a, const double x);
int gsl_matrix_float_add_diagonal (gsl_matrix_float * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
int gsl_matrix_float_get_row(gsl_vector_float * v, const gsl_matrix_float * m, const size_t i);
int gsl_matrix_float_get_col(gsl_vector_float * v, const gsl_matrix_float * m, const size_t j);
int gsl_matrix_float_set_row(gsl_matrix_float * m, const size_t i, const gsl_vector_float * v);
int gsl_matrix_float_set_col(gsl_matrix_float * m, const size_t j, const gsl_vector_float * v);
/***********************************************************************/

/* inline functions if you are using GCC */

INLINE_DECL float   gsl_matrix_float_get(const gsl_matrix_float * m, const size_t i, const size_t j);
INLINE_DECL void    gsl_matrix_float_set(gsl_matrix_float * m, const size_t i, const size_t j, const float x);
INLINE_DECL float * gsl_matrix_float_ptr(gsl_matrix_float * m, const size_t i, const size_t j);
INLINE_DECL const float * gsl_matrix_float_const_ptr(const gsl_matrix_float * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
float
gsl_matrix_float_get(const gsl_matrix_float * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
        }
    }
#endif
  return m->data[i * m->tda + j] ;
}

INLINE_FUN
void
gsl_matrix_float_set(gsl_matrix_float * m, const size_t i, const size_t j, const float x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  m->data[i * m->tda + j] = x ;
}

INLINE_FUN
float *
gsl_matrix_float_ptr(gsl_matrix_float * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (float *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const float *
gsl_matrix_float_const_ptr(const gsl_matrix_float * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (const float *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_FLOAT_H__ */


/* matrix/gsl_matrix_complex_float.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_COMPLEX_FLOAT_H__
#define __GSL_MATRIX_COMPLEX_FLOAT_H__

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

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

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  float * data;
  gsl_block_complex_float * block;
  int owner;
} gsl_matrix_complex_float ;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_view;

typedef _gsl_matrix_complex_float_view gsl_matrix_complex_float_view;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_const_view;

typedef const _gsl_matrix_complex_float_const_view gsl_matrix_complex_float_const_view;

/* Allocation */

gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc (const size_t n1, const size_t n2);

gsl_matrix_complex_float *
gsl_matrix_complex_float_calloc (const size_t n1, const size_t n2);

gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_block (gsl_block_complex_float * b,
                                           const size_t offset,
                                           const size_t n1, const size_t n2, const size_t d2);

gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_matrix (gsl_matrix_complex_float * b,
                                            const size_t k1, const size_t k2,
                                            const size_t n1, const size_t n2);

gsl_vector_complex_float *
gsl_vector_complex_float_alloc_row_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t i);

gsl_vector_complex_float *
gsl_vector_complex_float_alloc_col_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t j);

void gsl_matrix_complex_float_free (gsl_matrix_complex_float * m);

/* Views */

_gsl_matrix_complex_float_view
gsl_matrix_complex_float_submatrix (gsl_matrix_complex_float * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

_gsl_vector_complex_float_view
gsl_matrix_complex_float_row (gsl_matrix_complex_float * m, const size_t i);

_gsl_vector_complex_float_view
gsl_matrix_complex_float_column (gsl_matrix_complex_float * m, const size_t j);

_gsl_vector_complex_float_view
gsl_matrix_complex_float_diagonal (gsl_matrix_complex_float * m);

_gsl_vector_complex_float_view
gsl_matrix_complex_float_subdiagonal (gsl_matrix_complex_float * m, const size_t k);

_gsl_vector_complex_float_view
gsl_matrix_complex_float_superdiagonal (gsl_matrix_complex_float * m, const size_t k);

_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array (float * base,
                             const size_t n1,
                             const size_t n2);

_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array_with_tda (float * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector (gsl_vector_complex_float * v,
                              const size_t n1,
                              const size_t n2);

_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector_with_tda (gsl_vector_complex_float * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_submatrix (const gsl_matrix_complex_float * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_row (const gsl_matrix_complex_float * m,
                            const size_t i);

_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_column (const gsl_matrix_complex_float * m,
                               const size_t j);

_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_diagonal (const gsl_matrix_complex_float * m);

_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_subdiagonal (const gsl_matrix_complex_float * m,
                                    const size_t k);

_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_superdiagonal (const gsl_matrix_complex_float * m,
                                      const size_t k);

_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array (const float * base,
                                   const size_t n1,
                                   const size_t n2);

_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array_with_tda (const float * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector (const gsl_vector_complex_float * v,
                                    const size_t n1,
                                    const size_t n2);

_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector_with_tda (const gsl_vector_complex_float * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

gsl_complex_float gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m, const size_t i, const size_t j);
void gsl_matrix_complex_float_set(gsl_matrix_complex_float * m, const size_t i, const size_t j, const gsl_complex_float x);

gsl_complex_float * gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m, const size_t i, const size_t j);
const gsl_complex_float * gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m, const size_t i, const size_t j);

void gsl_matrix_complex_float_set_zero (gsl_matrix_complex_float * m);
void gsl_matrix_complex_float_set_identity (gsl_matrix_complex_float * m);
void gsl_matrix_complex_float_set_all (gsl_matrix_complex_float * m, gsl_complex_float x);

int gsl_matrix_complex_float_fread (FILE * stream, gsl_matrix_complex_float * m) ;
int gsl_matrix_complex_float_fwrite (FILE * stream, const gsl_matrix_complex_float * m) ;
int gsl_matrix_complex_float_fscanf (FILE * stream, gsl_matrix_complex_float * m);
int gsl_matrix_complex_float_fprintf (FILE * stream, const gsl_matrix_complex_float * m, const char * format);

int gsl_matrix_complex_float_memcpy(gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);
int gsl_matrix_complex_float_swap(gsl_matrix_complex_float * m1, gsl_matrix_complex_float * m2);

int gsl_matrix_complex_float_swap_rows(gsl_matrix_complex_float * m, const size_t i, const size_t j);
int gsl_matrix_complex_float_swap_columns(gsl_matrix_complex_float * m, const size_t i, const size_t j);
int gsl_matrix_complex_float_swap_rowcol(gsl_matrix_complex_float * m, const size_t i, const size_t j);

int gsl_matrix_complex_float_transpose (gsl_matrix_complex_float * m);
int gsl_matrix_complex_float_transpose_memcpy (gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);

int gsl_matrix_complex_float_isnull (const gsl_matrix_complex_float * m);

int gsl_matrix_complex_float_add (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
int gsl_matrix_complex_float_sub (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
int gsl_matrix_complex_float_mul_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
int gsl_matrix_complex_float_div_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
int gsl_matrix_complex_float_scale (gsl_matrix_complex_float * a, const gsl_complex_float x);
int gsl_matrix_complex_float_add_constant (gsl_matrix_complex_float * a, const gsl_complex_float x);
int gsl_matrix_complex_float_add_diagonal (gsl_matrix_complex_float * a, const gsl_complex_float x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
int gsl_matrix_complex_float_get_row(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t i);
int gsl_matrix_complex_float_get_col(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t j);
int gsl_matrix_complex_float_set_row(gsl_matrix_complex_float * m, const size_t i, const gsl_vector_complex_float * v);
int gsl_matrix_complex_float_set_col(gsl_matrix_complex_float * m, const size_t j, const gsl_vector_complex_float * v);

#ifdef HAVE_INLINE

extern inline
gsl_complex_float
gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m,
                     const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  gsl_complex_float zero = {{0,0}};

  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, zero) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, zero) ;
    }
#endif
  return *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

extern inline
void
gsl_matrix_complex_float_set(gsl_matrix_complex_float * m,
                     const size_t i, const size_t j, const gsl_complex_float x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) = x ;
}

extern inline
gsl_complex_float *
gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m,
                             const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

extern inline
const gsl_complex_float *
gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m,
                                   const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (const gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_MATRIX_COMPLEX_FLOAT_H__ */


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

#ifndef MCAS_HSTORE_HEAP_MM_EPHEMERAL_H
#define MCAS_HSTORE_HEAP_MM_EPHEMERAL_H

#include "heap_ephemeral.h"

#include "hstore_config.h"
#include "histogram_log2.h"
#include "hop_hash_log.h"
#include <mm_plugin_itf.h>
#include "persistent.h"

#include <ccpm/interfaces.h> /* ownership_callback, (IHeap_expandable, region_vector_t) */
#include <common/byte_span.h>
#include <common/string_view.h>
#include <nupm/region_descriptor.h>
#include <gsl/span>

#include <algorithm> /* min, swap */
#include <cstddef> /* size_t */
#include <vector>

struct heap_mm_ephemeral
	: private heap_ephemeral
{
private:
	using byte_span = common::byte_span;

	/* Although the mm heap tracks managed regions, its definition of a managed
	 * region probably differs from what hstore needs:
	 *
	 * The hstore managed region must include the hstore metadata area, as that
	 * must be include in the "maanged regions" returned through the kvstore
	 * interface.
	 *
	 * The mm heap managed region cannot include the hstore metadata area, as
	 * that would allow the allocator to use the area for memory allocation.
	 *
	 * Therefore, heap_mr_ephemeral keeps its own copy of the managed regions.
	 */
	nupm::region_descriptor _managed_regions;
protected:
	using heap_ephemeral::_alloc_mutex;

protected:
	using hist_type = util::histogram_log2<std::size_t>;
	hist_type _hist_alloc;
	hist_type _hist_inject;
	hist_type _hist_free;
private:
	/* Rca_LB seems not to allocate at or above about 2GiB. Limit reporting to 16 GiB. */
	static constexpr unsigned hist_report_upper_bound = 24U;
	static constexpr unsigned log_min_alignment = 3U; /* log (sizeof(void *)) */
	static_assert(sizeof(void *) == 1U << log_min_alignment, "log_min_alignment does not match sizeof(void *)");

protected:
	virtual void add_managed_region_to_heap(byte_span r_heap) = 0;
public:
	template <bool B>
		void write_hist(const byte_span pool_) const
		{
			static bool suppress = false;
			if ( ! suppress )
			{
				hop_hash_log<B>::write(LOG_LOCATION, "pool ", ::base(pool_));
				std::size_t lower_bound = 0;
				auto limit = std::min(std::size_t(hist_report_upper_bound), _hist_alloc.data().size());
				for ( unsigned i = log_min_alignment; i != limit; ++i )
				{
					const std::size_t upper_bound = 1ULL << i;
					hop_hash_log<B>::write(LOG_LOCATION
						, "[", lower_bound, "..", upper_bound, "): "
						, _hist_alloc.data()[i], " ", _hist_inject.data()[i], " ", _hist_free.data()[i]
						, " "
					);
					lower_bound = upper_bound;
				}
				suppress = true;
			}
		}

	using common::log_source::debug_level;
	explicit heap_mm_ephemeral(
		unsigned debug_level
		, nupm::region_descriptor managed_regions
	);
	virtual ~heap_mm_ephemeral() {}

	virtual std::size_t allocated() const = 0;
	virtual std::size_t capacity() const = 0;

	virtual void allocate(persistent_t<void *> &p, std::size_t sz, std::size_t alignment) = 0;
	virtual std::size_t free(persistent_t<void *> &p_, std::size_t sz_) = 0;
	virtual void free_tracked(const void *p, std::size_t sz) = 0;

	nupm::region_descriptor get_managed_regions() const { return _managed_regions; }
	nupm::region_descriptor set_managed_regions(nupm::region_descriptor n)
	{
		using std::swap;
		swap(n, _managed_regions);
		return n;
	}
	void add_managed_region(byte_span r_full, byte_span r_heap);
	void reconstitute_managed_region(
		const byte_span r_full
		, const byte_span r_heap
		, ccpm::ownership_callback_t f
	);
	virtual void reconstitute_managed_region_to_heap(byte_span r_heap, ccpm::ownership_callback_t f) = 0;
	virtual bool is_crash_consistent() const = 0;
	virtual bool can_reconstitute() const = 0;
};

#endif