/* -*- linux-c -*- */
/* cluster.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 <time.h>
#include <getopt.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_roots.h>
#include "fewbody.h"
#include "cluster.h"

/* print the usage */
void print_usage(FILE *stream)
{
	fprintf(stream, "USAGE:\n");
	fprintf(stream, "  cluster [options...]\n");
	fprintf(stream, "\n");
	fprintf(stream, "OPTIONS:\n");
	fprintf(stream, "  -n --n <n>                    : set number of stars in cluster [%d]\n", FB_N);
	fprintf(stream, "  -m --m <m/MSUN>               : set mass of each star [%.6g]\n", FB_M/FB_CONST_MSUN);
	fprintf(stream, "  -r --r <r/RSUN>               : set radius of each star [%.6g]\n", FB_R/FB_CONST_RSUN);
	fprintf(stream, "  -S --sigma <sigma/(km/s)>     : set central velocity dispersion [%.6g]\n", FB_SIGMA/1.0e5);
	fprintf(stream, "  -T --rmax <rmax/PARSEC>       : set truncation radius [%.6g]\n", FB_RMAX/FB_CONST_PARSEC);
	fprintf(stream, "  -t --tstop <tstop/t_dyn>      : set stopping time [%.6g]\n", FB_TSTOP);
	fprintf(stream, "  -P --tphysstop <tphysstop/yr> : set physical stopping time [%.6g]\n", FB_TPHYSSTOP/FB_CONST_YR);
	fprintf(stream, "  -D --dt <dt/t_dyn>            : set approximate output dt [%.6g]\n", FB_DT);
	fprintf(stream, "  -c --tcpustop <tcpustop/sec>  : set cpu stopping time [%.6g]\n", FB_TCPUSTOP);
	fprintf(stream, "  -A --absacc <absacc>          : set integrator's absolute accuracy [%.6g]\n", FB_ABSACC);
	fprintf(stream, "  -R --relacc <relacc>          : set integrator's relative accuracy [%.6g]\n", FB_RELACC);
	fprintf(stream, "  -N --ncount <ncount>          : set number of integration steps between calls\n");
	fprintf(stream, "                                  to fb_classify() [%d]\n", FB_NCOUNT);
	fprintf(stream, "  -z --tidaltol <tidaltol>      : set tidal tolerance [%.6g]\n", FB_TIDALTOL);
	fprintf(stream, "  -x --fexp <f_exp>             : set expansion factor of merger product [%.6g]\n", FB_FEXP);
	fprintf(stream, "  -k --ks                       : turn K-S regularization on or off [%d]\n", FB_KS);
	fprintf(stream, "  -s --seed                     : set random seed [%ld]\n", FB_SEED);
	fprintf(stream, "  -d --debug                    : turn on debugging\n");
	fprintf(stream, "  -V --version                  : print version info\n");
	fprintf(stream, "  -h --help                     : display this help text\n");
}

/* calculate the units used (N-body units) */
void calc_units(fb_hier_t hier, fb_units_t *units)
{
	int i, j, k;
	double petot, ketot, r[3];

	/* the unit of mass is defined so that M_tot=1 */
	units->m = 0.0;
	for (i=0; i<hier.nstar; i++) {
		units->m += hier.hier[hier.hi[1]+i].m;
	}

	/* calculate total potential and kinetic energy */
	petot = 0.0;
	for (i=0; i<hier.nstar-1; i++) {
		for (j=i+1; j<hier.nstar; j++) {
			for (k=0; k<3; k++) {
				r[k] = hier.hier[hier.hi[1]+j].x[k] - hier.hier[hier.hi[1]+i].x[k];
			}
			petot += - FB_CONST_G * hier.hier[hier.hi[1]+i].m * hier.hier[hier.hi[1]+j].m / fb_mod(r);
		}
	}

	ketot = 0.0;
	for (i=0; i<hier.nstar; i++) {
		ketot += 0.5 * hier.hier[hier.hi[1]+i].m * fb_dot(hier.hier[hier.hi[1]+i].v, hier.hier[hier.hi[1]+i].v);
	}

	/* the unit of energy is defined so that E=-1/4 */
	units->E = -4.0 * (petot + ketot);

	/* with G=1, all other units are derived */
	units->t = FB_CONST_G * pow(units->m, 2.5) * pow(units->E, -1.5);
	units->l = FB_CONST_G * fb_sqr(units->m) / units->E;
	units->v = units->l / units->t;
}

/* f_0-f(v/v_esc) for the Plummer model */
double fv(double v, void *params)
{
	double f;

	f = ((double *)params)[0];
	return(f - 512.0/(7.0*FB_CONST_PI) * (sqrt(1.0-fb_sqr(v))*(-7.0*v/256.0 + 121.0*fb_cub(v)/384.0 - 263.0*fb_sqr(v)*fb_cub(v)/480.0 + 31.0*v*fb_sqr(fb_cub(v))/80.0 - fb_cub(fb_cub(v))/10.0) + 7.0*asin(v)/256.0));
}

/* get speed from Plummer distribution */
double vf(double f)
{
	int status, iter;
	double v, params[1];
	gsl_function F;
	const gsl_root_fsolver_type *T;
	gsl_root_fsolver *s;

	/* set up the root solver */
	F.function = &fv;
	F.params = &params;

	/* set the parameters */
	params[0] = f;

	T = gsl_root_fsolver_brent;
	s = gsl_root_fsolver_alloc(T);
	gsl_root_fsolver_set(s, &F, 0.0, 1.0);

	/* get v/v_esc by root-finding */
	iter = 0;
	do {
		iter++;
		gsl_root_fsolver_iterate(s);
		status = gsl_root_test_interval(gsl_root_fsolver_x_lower(s), gsl_root_fsolver_x_upper(s), \
						FB_ROOTSOLVER_ABS_ACC, FB_ROOTSOLVER_REL_ACC);
	} while (status == GSL_CONTINUE && iter < FB_ROOTSOLVER_MAX_ITER);

	if (iter >= FB_ROOTSOLVER_MAX_ITER) {
		fprintf(stderr, "Root finder failed to converge.\n");
		exit(1);
	}

	/* we've got the root */
	v = gsl_root_fsolver_root(s);

	/* free memory associated with root solver */
	gsl_root_fsolver_free(s);

	return(v);
}

/* the main attraction */
int main(int argc, char *argv[])
{
	int i, j, n;
	unsigned long int seed;
	double tphysstop, m, r, Ei, Li[3], Lint[3], t, M, a, sigma, rmax, Xmax, radius, v, theta, phi, xcm[3], vcm[3];
	fb_hier_t hier;
	fb_input_t input;
	fb_ret_t retval;
	fb_units_t units;
	char string1[FB_MAX_STRING_LENGTH], string2[FB_MAX_STRING_LENGTH];
	gsl_rng *rng;
	const gsl_rng_type *rng_type=gsl_rng_mt19937;
	const char *short_opts = "n:m:r:S:T:t:P:D:c:A:R:N:z:x:k:s:dVh";
	const struct option long_opts[] = {
		{"n", required_argument, NULL, 'n'},
		{"m", required_argument, NULL, 'm'},
		{"r", required_argument, NULL, 'r'},
		{"sigma", required_argument, NULL, 'S'},
		{"rmax", required_argument, NULL, 'T'},
		{"tstop", required_argument, NULL, 't'},
		{"tphysstop", required_argument, NULL, 'P'},
		{"dt", required_argument, NULL, 'D'},
		{"tcpustop", required_argument, NULL, 'c'},
		{"absacc", required_argument, NULL, 'A'},
		{"relacc", required_argument, NULL, 'R'},
		{"ncount", required_argument, NULL, 'N'},
		{"tidaltol", required_argument, NULL, 'z'},
		{"fexp", required_argument, NULL, 'x'},
		{"ks", required_argument, NULL, 'k'},
		{"seed", required_argument, NULL, 's'},
		{"debug", no_argument, NULL, 'd'},
		{"version", no_argument, NULL, 'V'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	/* set parameters to default values */
	n = FB_N;
	m = FB_M;
	r = FB_R;
	sigma = FB_SIGMA;
	rmax = FB_RMAX;
	input.ks = FB_KS;
	input.tstop = FB_TSTOP;
	tphysstop = FB_TPHYSSTOP;
	input.Dflag = 0;
	input.dt = FB_DT;
	input.tcpustop = FB_TCPUSTOP;
	input.absacc = FB_ABSACC;
	input.relacc = FB_RELACC;
	input.ncount = FB_NCOUNT;
	input.tidaltol = FB_TIDALTOL;
	input.fexp = FB_FEXP;
	seed = FB_SEED;
	fb_debug = FB_DEBUG;

	while ((i = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
		switch (i) {
		case 'n':
			n = atoi(optarg);
			break;
		case 'm':
			m = atof(optarg) * FB_CONST_MSUN;
			break;
		case 'r':
			r = atof(optarg) * FB_CONST_RSUN;
			break;
		case 'S':
			sigma = atof(optarg) * 1.0e5;
			break;
		case 'T':
			rmax = atof(optarg) * FB_CONST_PARSEC;
			break;
		case 't':
			input.tstop = atof(optarg);
			break;
		case 'P':
			tphysstop = atof(optarg) * FB_CONST_YR;
			break;
		case 'D':
			input.Dflag = 1;
			input.dt = atof(optarg);
			break;
		case 'c':
			input.tcpustop = atof(optarg);
			break;
		case 'A':
			input.absacc = atof(optarg);
			break;
		case 'R':
			input.relacc = atof(optarg);
			break;
		case 'N':
			input.ncount = atoi(optarg);
			break;
		case 'z':
			input.tidaltol = atof(optarg);
			break;
		case 'x':
			input.fexp = atof(optarg);
			break;
		case 'k':
			input.ks = atoi(optarg);
			break;
		case 's':
			seed = atol(optarg);
			break;
		case 'd':
			fb_debug = 1;
			break;
		case 'V':
			fb_print_version(stdout);
			return(0);
		case 'h':
			fb_print_version(stdout);
			fprintf(stdout, "\n");
			print_usage(stdout);
			return(0);
		default:
			break;
		}
	}

	/* check to make sure there was nothing crazy on the command line */
	if (optind < argc) {
		print_usage(stdout);
		return(1);
	}

	/* set up parameters for Plummer model */
	M = ((double) n) * m;
	a = FB_CONST_G * M / (6.0*fb_sqr(sigma));
	Xmax = pow(1.0+fb_sqr(a/rmax), -1.5);

	/* initialize a few things for integrator */
	t = 0.0;
	hier.nstarinit = n;
	hier.nstar = n;
	fb_malloc_hier(&hier);
	fb_init_hier(&hier);

	/* put stuff in log entry */
	snprintf(input.firstlogentry, FB_MAX_LOGENTRY_LENGTH, "  command line:");
	for (i=0; i<argc; i++) {
		snprintf(&(input.firstlogentry[strlen(input.firstlogentry)]),
			 FB_MAX_LOGENTRY_LENGTH-strlen(input.firstlogentry), " %s", argv[i]);
	}
	snprintf(&(input.firstlogentry[strlen(input.firstlogentry)]),
		 FB_MAX_LOGENTRY_LENGTH-strlen(input.firstlogentry), "\n");

	/* print out values of paramaters */
	fprintf(stderr, "PARAMETERS:\n");
	fprintf(stderr, "  ks=%d  seed=%ld\n", input.ks, seed);
	fprintf(stderr, "  M=%.6g MSUN  sigma=%.6g km/s  a=%.6g PARSEC  rmax=%.6g PARSEC  rmax/a=%.6g\n",
		M/FB_CONST_MSUN, sigma/1.0e5, a/FB_CONST_PARSEC, rmax/FB_CONST_PARSEC, rmax/a);
	fprintf(stderr, "  n=%d  m=%.6g MSUN  r=%.6g RSUN  tstop=%.6g  tphysstop=%.6g yr  tcpustop=%.6g\n", \
		n, m/FB_CONST_MSUN, r/FB_CONST_RSUN, input.tstop, tphysstop/FB_CONST_YR, input.tcpustop);
	fprintf(stderr, "  tidaltol=%.6g  abs_acc=%.6g  rel_acc=%.6g  ncount=%d  fexp=%.6g\n\n", \
		input.tidaltol, input.absacc, input.relacc, input.ncount, input.fexp);

	/* initialize GSL rng */
	gsl_rng_env_setup();
	rng = gsl_rng_alloc(rng_type);
	gsl_rng_set(rng, seed);

	/* prepare to calculate the center of mass position and velocity */
	for (i=0; i<3; i++) {
		xcm[i] = 0.0;
		vcm[i] = 0.0;
	}

	/* give the objects some properties */
	for (j=0; j<hier.nstar; j++) {
		hier.hier[hier.hi[1]+j].ncoll = 1;
		hier.hier[hier.hi[1]+j].id[0] = j;
		snprintf(hier.hier[hier.hi[1]+j].idstring, FB_MAX_STRING_LENGTH, "%d", j);
		hier.hier[hier.hi[1]+j].n = 1;
		hier.hier[hier.hi[1]+j].obj[0] = NULL;
		hier.hier[hier.hi[1]+j].obj[1] = NULL;
		hier.hier[hier.hi[1]+j].Eint = 0.0;
		hier.hier[hier.hi[1]+j].Lint[0] = 0.0;
		hier.hier[hier.hi[1]+j].Lint[1] = 0.0;
		hier.hier[hier.hi[1]+j].Lint[2] = 0.0;
		hier.hier[hier.hi[1]+j].m = m;
		hier.hier[hier.hi[1]+j].R = r;

		/* draw radius from Plummer distribution */
		radius = a / sqrt(pow(Xmax*gsl_rng_uniform(rng), -2.0/3.0)-1.0);
		theta = acos(2.0 * gsl_rng_uniform(rng) - 1.0);
		phi = 2.0 * FB_CONST_PI * gsl_rng_uniform(rng);
		hier.hier[hier.hi[1]+j].x[0] = radius * sin(theta) * cos(phi);
		hier.hier[hier.hi[1]+j].x[1] = radius * sin(theta) * sin(phi);
		hier.hier[hier.hi[1]+j].x[2] = radius * cos(theta);

		/* draw velocity from Plummer distribution */
		v = vf(gsl_rng_uniform(rng)) * sqrt(2.0*FB_CONST_G*M/(a*sqrt(1.0+fb_sqr(radius/a))));
		theta = acos(2.0 * gsl_rng_uniform(rng) - 1.0);
		phi = 2.0 * FB_CONST_PI * gsl_rng_uniform(rng);
		hier.hier[hier.hi[1]+j].v[0] = v * sin(theta) * cos(phi);
		hier.hier[hier.hi[1]+j].v[1] = v * sin(theta) * sin(phi);
		hier.hier[hier.hi[1]+j].v[2] = v * cos(theta);

		/* calculate center of mass position and velocity */
		for (i=0; i<3; i++) {
			xcm[i] += hier.hier[hier.hi[1]+j].x[i] / ((double) n);
			vcm[i] += hier.hier[hier.hi[1]+j].v[i] / ((double) n);
		}
	}

	/* transform to center of mass frame */
	for (j=0; j<hier.nstar; j++) {
		for (i=0; i<3; i++) {
			hier.hier[hier.hi[1]+j].x[i] -= xcm[i];
			hier.hier[hier.hi[1]+j].v[i] -= vcm[i];
		}
	}

	/* get the units and normalize */
	calc_units(hier, &units);
	fb_normalize(&hier, units);

	/* reset input.tstop if necessary, based on the value of tphysstop */
	if (tphysstop/units.t < input.tstop) {
		input.tstop = tphysstop/units.t;
		fb_dprintf("decreasing tstop in accord with tphysstop: tstop=%.6g\n", input.tstop);
	}

	fb_dprintf("virial ratio: q=%.6g\n", -fb_ketot(&(hier.hier[hier.hi[1]]), hier.nstar)/fb_petot(&(hier.hier[hier.hi[1]]), hier.nstar));

	fprintf(stderr, "UNITS:\n");
	fprintf(stderr, "  v=%.6g km/s  l=%.6g AU  t=t_dyn=%.6g yr\n", \
		units.v/1.0e5, units.l/FB_CONST_AU, units.t/FB_CONST_YR);
	fprintf(stderr, "  M=%.6g M_sun  E=%.6g erg\n\n", units.m/FB_CONST_MSUN, units.E);

	/* calculate the initial energy and angular momentum, for bookkeeping */
	Ei = fb_petot(&(hier.hier[hier.hi[1]]), hier.nstar) + fb_ketot(&(hier.hier[hier.hi[1]]), hier.nstar) +
		fb_einttot(&(hier.hier[hier.hi[1]]), hier.nstar);
	fb_angmom(&(hier.hier[hier.hi[1]]), hier.nstar, Li);
	fb_angmomint(&(hier.hier[hier.hi[1]]), hier.nstar, Lint);
	for (j=0; j<3; j++) {
		Li[j] += Lint[j];
	}

	/* integrate along */
	fb_dprintf("calling fewbody()...\n");

	/* call fewbody! */
	retval = fewbody(input, &hier, &t);

	/* print information to screen */
	fprintf(stderr, "OUTCOME:\n");
	if (retval.retval == 1) {
		fprintf(stderr, "  encounter complete:  t=%.6g (%.6g yr)  %s  (%s)\n\n",
			t, t * units.t/FB_CONST_YR,
			fb_sprint_hier(hier, string1),
			fb_sprint_hier_hr(hier, string2));
	} else {
		fprintf(stderr, "  encounter NOT complete:  t=%.6g (%.6g yr)  %s  (%s)\n\n",
			t, t * units.t/FB_CONST_YR,
			fb_sprint_hier(hier, string1),
			fb_sprint_hier_hr(hier, string2));
	}

	fb_dprintf("there were %ld integration steps\n", retval.count);
	fb_dprintf("fb_classify() was called %ld times\n", retval.iclassify);

	fprintf(stderr, "FINAL:\n");
	fprintf(stderr, "  t_final=%.6g (%.6g yr)  t_cpu=%.6g s\n", \
		t, t*units.t/FB_CONST_YR, retval.tcpu);

	fprintf(stderr, "  L0=%.6g  DeltaL/L0=%.6g  DeltaL=%.6g\n", fb_mod(Li), retval.DeltaLfrac, retval.DeltaL);
	fprintf(stderr, "  E0=%.6g  DeltaE/E0=%.6g  DeltaE=%.6g\n", Ei, retval.DeltaEfrac, retval.DeltaE);
	fprintf(stderr, "  Rmin=%.6g (%.6g RSUN)  Rmin_i=%d  Rmin_j=%d\n", \
		retval.Rmin, retval.Rmin*units.l/FB_CONST_RSUN, retval.Rmin_i, retval.Rmin_j);
	fprintf(stderr, "  Nosc=%d (%s)\n", retval.Nosc, (retval.Nosc>=1?"resonance":"non-resonance"));

	/* free GSL stuff */
	gsl_rng_free(rng);

	/* free our own stuff */
	fb_free_hier(hier);

	/* done! */
	return(0);
}


#include <stdio.h>
#include <cblas.h>
#include <sys/time.h>
#include <stdlib.h>
#include <omp.h>

#define NUM 64

#define GEMM_K 320
#define GEMM_N 4096
#define GEMM_M 256

void PACKA(float* A, float* Ac, long M, long K, long LK)
{
	long ii, jj, kk;
	for( ii = 0 ; ii < M; ii = ii + 8)
	{

		float *temp = A + ii * LK + kk;

		asm volatile(
				"	ldr		x0, %[Ac]							\n"
				"	ldr		x1, %[K]							\n"
				"	ldr		x2, %[temp]						\n"
				"	ldr		x30, %[LK]						\n"

				"	add		x3, x2, x30, lsl #2		\n"
				"	add		x4, x2, x30, lsl #3		\n"
				"	add		x5, x3, x30, lsl #3		\n"
				"	add		x6, x4, x30, lsl #3		\n"
				"	add		x7, x5, x30, lsl #3		\n"
				"	add		x8, x6, x30, lsl #3		\n"
				"	add		x9, x7, x30, lsl #3		\n"

				"	lsr		x21, x1, #3						\n"
				"	cmp		x21, #0								\n"
				"	beq		PACKA_END							\n"

				"PACKA:												\n"

				"	prfm	PLDL1KEEP, [x2, #128]	\n"
				"	prfm	PLDL1KEEP, [x3, #128]	\n"

				"	ldr		q0, [x2], #16					\n"
				"	ldr		q1, [x3], #16					\n"
				"	ldr		q2, [x4], #16					\n"
				"	ldr		q3, [x5], #16					\n"

				"	prfm	PLDL1KEEP, [x4, #128]	\n"
				"	prfm	PLDL1KEEP, [x5, #128]	\n"

				"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"

				"	ldr		q4, [x6], #16					\n"
				"	ldr		q5, [x7], #16					\n"
				"	ldr		q6, [x8], #16					\n"
				"	ldr		q7, [x9], #16					\n"

				"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"

				"	prfm	PLDL1KEEP, [x6, #128]	\n"
				"	prfm	PLDL1KEEP, [x7, #128]	\n"

				"	ldr		q8, [x2], #16					\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[1], [x0], #16	\n"
				"	ldr		q9, [x3], #16					\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[1], [x0], #16	\n"
				"	ldr		q10, [x4], #16				\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[2], [x0], #16	\n"
				"	ldr		q11, [x5], #16				\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[2], [x0], #16	\n"

				"	prfm	PLDL1KEEP, [x8, #128]	\n"
				"	prfm	PLDL1KEEP, [x9, #128]	\n"

				"	ldr		q12, [x6], #16				\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[3], [x0], #16		\n"
				"	ldr		q13, [x7], #16				\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[3], [x0], #16		\n"
				"	ldr		q14, [x8], #16				\n"
				"	st4		{v8.s, v9.s, v10.s, v11.s}[0], [x0], #16	\n"
				"	ldr		q15, [x9], #16				\n"
				"	st4		{v12.s, v13.s, v14.s, v15.s}[0], [x0], #16	\n"

				"	subs	x21, x21, #1					\n"

				"	st4		{v8.s, v9.s, v10.s, v11.s}[1], [x0], #16	\n"
				"	st4		{v12.s, v13.s, v14.s, v15.s}[1], [x0], #16	\n"
				"	st4		{v8.s, v9.s, v10.s, v11.s}[2], [x0], #16	\n"
				"	st4		{v12.s, v13.s, v14.s, v15.s}[2], [x0], #16	\n"
				"	st4		{v8.s, v9.s, v10.s, v11.s}[3], [x0], #16	\n"
				"	st4		{v12.s, v13.s, v14.s, v15.s}[3], [x0], #16	\n"

				"	bgt		PACKA 						\n"

				"	ands	x22, x1, #7				\n"
				"	beq		PACKA_END					\n"

				"	cmp		x22, #4						\n"
				"	blt 	K1_PACKA					\n"

				"K4_PACKA:								\n"

				"	ldr		q0, [x2], #16			\n"
				"	ldr		q1, [x3], #16			\n"
				"	ldr		q2, [x4], #16			\n"
				"	ldr		q3, [x5], #16			\n"

				"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"
				"	ldr		q4, [x6], #16			\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[1], [x0], #16	\n"
				"	ldr		q5, [x7], #16			\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[2], [x0], #16	\n"
				"	ldr		q6, [x8], #16			\n"
				"	st4		{v0.s, v1.s, v2.s, v3.s}[3], [x0], #16	\n"
				"	ldr		q7, [x9], #16			\n"

				"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[1], [x0], #16	\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[2], [x0], #16	\n"
				"	st4		{v4.s, v5.s, v6.s, v7.s}[3], [x0], #16	\n"

				"	subs	x22, x22, #4			\n"
				"	beq		PACKA_END					\n"

				"K1_PACKA:								\n"

				"	ldr		s0, [x2], #4			\n"
				"	ldr		s1, [x3], #4			\n"
				"	ldr		s2, [x4], #4			\n"
				"	ldr		s3, [x5], #4			\n"

				"	subs 	x22, x22, #1			\n"

				"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"
				"	ldr		s4, [x6], #4			\n"
				"	ldr		s5, [x7], #4			\n"
				"	ldr		s6, [x8], #4			\n"
				"	ldr		s7, [x9], #4			\n"

				"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"

				"	bgt		K1_PACKA				\n"

				"PACKA_END:							\n"

				:
				:
				[temp] "m" (temp),
				[Ac] "m" (Ac),
				[K] "m" (K),
				[LK] "m" (LK)
				:"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8",
				"x9", "x10", "x11", "x12", "x13","x14", "x15", "x16",
				"x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24","x25",
				"x26", "x27", "x28", "x29", "x30",
				"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
				"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
				"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
				"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"

		);

		Ac = Ac + K * 8;

	}

}

void Sin_PACK(float* A, float* Ac, long M, long K, long LK)
{
	long ii, jj, kk;
	long Kc;

	for( kk =0 ; kk < K; kk = kk + Kc)
	{

		Kc = GEMM_K;
		if(K - kk < GEMM_K)
			Kc= K - kk;

		for( ii = 0 ; ii < M; ii = ii + 8)
		{

			float *temp = A + ii * LK + kk;

			asm volatile(
					"	ldr		x0, %[Ac]							\n"
					"	ldr		x1, %[Kc]							\n"
					"	ldr		x2, %[temp]						\n"
					"	ldr		x30, %[LK]						\n"

					"	add		x3, x2, x30, lsl #2		\n"
					"	add		x4, x2, x30, lsl #3		\n"
					"	add		x5, x3, x30, lsl #3		\n"
					"	add		x6, x4, x30, lsl #3		\n"
					"	add		x7, x5, x30, lsl #3		\n"
					"	add		x8, x6, x30, lsl #3		\n"
					"	add		x9, x7, x30, lsl #3		\n"

					"	lsr		x21, x1, #3						\n"
					"	cmp		x21, #0								\n"
					"	beq		Sin_PACKA_END					\n"

					"Sin_PACKA:										\n"

					"	prfm	PLDL1KEEP, [x2, #128]	\n"
					"	prfm	PLDL1KEEP, [x3, #128]	\n"

					"	ldr		q0, [x2], #16					\n"
					"	ldr		q1, [x3], #16					\n"
					"	ldr		q2, [x4], #16					\n"
					"	ldr		q3, [x5], #16					\n"

					"	prfm	PLDL1KEEP, [x4, #128]	\n"
					"	prfm	PLDL1KEEP, [x5, #128]	\n"

					"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"

					"	ldr		q4, [x6], #16					\n"
					"	ldr		q5, [x7], #16					\n"
					"	ldr		q6, [x8], #16					\n"
					"	ldr		q7, [x9], #16					\n"

					"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"

					"	prfm	PLDL1KEEP, [x6, #128]	\n"
					"	prfm	PLDL1KEEP, [x7, #128]	\n"

					"	ldr		q8, [x2], #16					\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[1], [x0], #16	\n"
					"	ldr		q9, [x3], #16					\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[1], [x0], #16	\n"
					"	ldr		q10, [x4], #16				\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[2], [x0], #16	\n"
					"	ldr		q11, [x5], #16				\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[2], [x0], #16	\n"

					"	prfm	PLDL1KEEP, [x8, #128]	\n"
					"	prfm	PLDL1KEEP, [x9, #128]	\n"

					"	ldr		q12, [x6], #16				\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[3], [x0], #16		\n"
					"	ldr		q13, [x7], #16				\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[3], [x0], #16		\n"
					"	ldr		q14, [x8], #16				\n"
					"	st4		{v8.s, v9.s, v10.s, v11.s}[0], [x0], #16	\n"
					"	ldr		q15, [x9], #16				\n"
					"	st4		{v12.s, v13.s, v14.s, v15.s}[0], [x0], #16	\n"

					"	subs	x21, x21, #1					\n"

					"	st4		{v8.s, v9.s, v10.s, v11.s}[1], [x0], #16	\n"
					"	st4		{v12.s, v13.s, v14.s, v15.s}[1], [x0], #16	\n"
					"	st4		{v8.s, v9.s, v10.s, v11.s}[2], [x0], #16	\n"
					"	st4		{v12.s, v13.s, v14.s, v15.s}[2], [x0], #16	\n"
					"	st4		{v8.s, v9.s, v10.s, v11.s}[3], [x0], #16	\n"
					"	st4		{v12.s, v13.s, v14.s, v15.s}[3], [x0], #16	\n"

					"	bgt		Sin_PACKA 				\n"

					"	ands	x22, x1, #7				\n"
					"	beq		Sin_PACKA_END			\n"

					"	cmp		x22, #4						\n"
					"	blt 	Sin_K1_PACKA			\n"

					"Sin_K4_PACKA:						\n"

					"	ldr		q0, [x2], #16			\n"
					"	ldr		q1, [x3], #16			\n"
					"	ldr		q2, [x4], #16			\n"
					"	ldr		q3, [x5], #16			\n"

					"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"
					"	ldr		q4, [x6], #16			\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[1], [x0], #16	\n"
					"	ldr		q5, [x7], #16			\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[2], [x0], #16	\n"
					"	ldr		q6, [x8], #16			\n"
					"	st4		{v0.s, v1.s, v2.s, v3.s}[3], [x0], #16	\n"
					"	ldr		q7, [x9], #16			\n"

					"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[1], [x0], #16	\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[2], [x0], #16	\n"
					"	st4		{v4.s, v5.s, v6.s, v7.s}[3], [x0], #16	\n"

					"	subs	x22, x22, #4			\n"
					"	beq		Sin_PACKA_END			\n"

					"Sin_K1_PACKA:						\n"

					"	ldr		s0, [x2], #4			\n"
					"	ldr		s1, [x3], #4			\n"
					"	ldr		s2, [x4], #4			\n"
					"	ldr		s3, [x5], #4			\n"

					"	subs 	x22, x22, #1			\n"

					"	st4		{v0.s, v1.s, v2.s, v3.s}[0], [x0], #16	\n"
					"	ldr		s4, [x6], #4			\n"
					"	ldr		s5, [x7], #4			\n"
					"	ldr		s6, [x8], #4			\n"
					"	ldr		s7, [x9], #4			\n"

					"	st4		{v4.s, v5.s, v6.s, v7.s}[0], [x0], #16	\n"

					"	bgt		Sin_K1_PACKA			\n"

					"Sin_PACKA_END:						\n"

					:
	        :
	      		[temp] "m" (temp),
	      		[Ac] "m" (Ac),
	      		[Kc] "m" (Kc),
	      		[LK] "m" (LK)
	        :"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8",
	        "x9", "x10", "x11", "x12", "x13","x14", "x15", "x16",
	        "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24","x25",
	        "x26", "x27", "x28", "x29", "x30",
	        "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
	        "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
	        "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
	        "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"

			);

			Ac = Ac + Kc * 8;

		}
	}
}


/* Odeint solver */
#include <pygsl/utils.h>
#include <pygsl/block_helpers.h>
#include <pygsl/error_helpers.h>
#include <Python.h>
#include <gsl/gsl_odeiv.h>
#include <gsl/gsl_errno.h>

char odeiv_module_doc[] = "XXX odeiv module doc missing!\n";

static char this_file[] = __FILE__;
static PyObject *module = NULL; /* set by initodeiv */

static void			/* generic instance destruction */
generic_dealloc (PyObject *self)
{
  DEBUG_MESS(1, " *** generic_dealloc %p\n", (void *) self);
  PyMem_Free(self);
}

typedef struct {
     PyObject_HEAD
     gsl_odeiv_step * step;
     gsl_odeiv_system system;
     PyObject *py_func;
     PyObject *py_jac;
     PyObject *arguments;
     jmp_buf buffer;
}
PyGSL_odeiv_step;

typedef struct {
  PyObject_HEAD
  PyGSL_odeiv_step * step;
  gsl_odeiv_control * control;
} PyGSL_odeiv_control;

typedef struct {
  PyObject_HEAD
  PyGSL_odeiv_step * step;
  PyGSL_odeiv_control * control;
  gsl_odeiv_evolve * evolve;
} PyGSL_odeiv_evolve;

typedef struct {
  PyObject_HEAD
  gsl_odeiv_step_type * step_type;
} PyGSL_odeiv_step_type;

typedef struct {
  PyObject_HEAD
  gsl_odeiv_control_type * control_type;
} PyGSL_odeiv_control_type;

/*---------------------------------------------------------------------------
 * Declaration of the various Methods
 *---------------------------------------------------------------------------*/
/*
 * stepper
 */
static int
PyGSL_odeiv_func(double t, const double y[], double f[], void *params);
static int
PyGSL_odeiv_jac(double t, const double y[], double *dfdy, double dfdt[],
		void *params);
static PyObject *
PyGSL_odeiv_step_apply(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args);
static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self);
static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_order(PyGSL_odeiv_step *self, PyObject *args);

/*
 * control
 */
static PyObject *
PyGSL_odeiv_control_hadjust(PyGSL_odeiv_control *self, PyObject *args);
static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self);
static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args);

/*
 * evolve
 */
static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self);
static PyObject *
PyGSL_odeiv_evolve_apply(PyGSL_odeiv_evolve *self, PyObject *args);
static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args);
/*---------------------------------------------------------------------------*/

static char PyGSL_odeiv_step_type_doc[] = "A odeiv step type\n";
static char PyGSL_odeiv_control_type_doc[] = "A odeiv control type\n";
static char PyGSL_odeiv_evolve_type_doc[] = "A odeiv evolve type\n";

#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL                               \
static PyTypeObject PyGSL_ODEIV_GENERIC_TYPE_PYTYPE = {		          \
  PyObject_HEAD_INIT(NULL)	/* fix up the type slot in initodeiv */	  \
  0,				/* ob_size */				  \
  PyGSL_ODEIV_GENERIC_TYPE_NAME,     	/* tp_name */			  \
  sizeof(PyGSL_ODEIV_GENERIC_TYPE),       /* tp_basicsize */		  \
  0,				/* tp_itemsize */			  \
									  \
  /* standard methods */						  \
  (destructor)  generic_dealloc,   /* tp_dealloc  ref-count==0  */	  \
  (printfunc)   0,		   /* tp_print    "print x"     */	  \
  (getattrfunc) 0,                 /* tp_getattr  "x.attr"      */	  \
  (setattrfunc) 0,		   /* tp_setattr  "x.attr=v"    */	  \
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */	  \
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */	  \
									  \
  /* type categories */							  \
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/ \
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/  \
  0,				/* tp_as_mapping  [key], len, ...*/	  \
									  \
  /* more methods */							  \
  (hashfunc)     0,		/* tp_hash    "dict[x]" */		  \
  (ternaryfunc)  0,             /* tp_call    "x()"     */		  \
  (reprfunc)     0,             /* tp_str     "str(x)"  */		  \
  (getattrofunc) 0,		/* tp_getattro */			  \
  (setattrofunc) 0,		/* tp_setattro */			  \
  0,				/* tp_as_buffer */			  \
  0L,				/* tp_flags */				  \
  PyGSL_ODEIV_GENERIC_TYPE_DOC       /* tp_doc */                         \
};

#define PyGSL_ODEIV_GENERIC_TYPE        PyGSL_odeiv_step_type
#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE PyGSL_odeiv_step_type_pytype
#define PyGSL_ODEIV_GENERIC_TYPE_NAME   "PyGSL_odeiv_step_type"
#define PyGSL_ODEIV_GENERIC_TYPE_DOC    PyGSL_odeiv_step_type_doc
PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL

#undef PyGSL_ODEIV_GENERIC_TYPE
#undef PyGSL_ODEIV_GENERIC_TYPE_PYTYPE
#undef PyGSL_ODEIV_GENERIC_TYPE_NAME
#undef PyGSL_ODEIV_GENERIC_TYPE_DOC
#define PyGSL_ODEIV_GENERIC_TYPE        PyGSL_odeiv_control_type
#define PyGSL_ODEIV_GENERIC_TYPE_PYTYPE PyGSL_odeiv_control_type_pytype
#define PyGSL_ODEIV_GENERIC_TYPE_NAME   "PyGSL_odeiv_control_type"
#define PyGSL_ODEIV_GENERIC_TYPE_DOC    PyGSL_odeiv_control_type_doc
PyGSL_ODEIV_GENERIC_TYPE_PYTYPE_ALL

#define PyGSLOdeivStepType_Check(v)    ((v)->ob_type == &PyGSL_odeiv_step_type_pytype)
#define PyGSLOdeivControlType_Check(v) ((v)->ob_type == &PyGSL_odeiv_control_type_pytype)
#define PyGSLOdeivEvolveType_Check(v)  ((v)->ob_type == &PyGSL_odeiv_evolve_type_pytype)

#define PyGSL_ODEIV_GENERIC_ALL                                                           \
static PyObject *									  \
PyGSL_ODEIV_GENERIC_GETATTR(PyGSL_ODEIV_GENERIC *self, char *name)		          \
{											  \
     PyObject *tmp = NULL;								  \
											  \
     FUNC_MESS_BEGIN();									  \
 											  \
     tmp = Py_FindMethod(PyGSL_ODEIV_GENERIC_METHODS, (PyObject *) self, name);	          \
     if(NULL == tmp){	  								  \
	  PyGSL_add_traceback(module, __FILE__, "odeiv.__attr__", __LINE__ - 1);	  \
	  return NULL;									  \
     }											  \
     return tmp;                                                                          \
}                                                                                \
static PyTypeObject PyGSL_ODEIV_GENERIC_PYTYPE = {					  \
  PyObject_HEAD_INIT(NULL)	/* fix up the type slot in initcrng */			  \
  0,				/* ob_size */						  \
  PyGSL_ODEIV_GENERIC_NAME,			/* tp_name */			          \
  sizeof(PyGSL_ODEIV_GENERIC),  /* tp_basicsize */					  \
  0,				/* tp_itemsize */					  \
											  \
  /* standard methods */								  \
  (destructor)  PyGSL_ODEIV_GENERIC_DELETE,       /* tp_dealloc  ref-count==0  */	  \
  (printfunc)   0,		                      /* tp_print    "print x"     */	  \
  (getattrfunc) PyGSL_ODEIV_GENERIC_GETATTR,       /* tp_getattr  "x.attr"      */	  \
  (setattrfunc) 0,		   /* tp_setattr  "x.attr=v"    */			  \
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */			  \
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */			  \
											  \
  /* type categories */									  \
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/		  \
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/		  \
  0,				/* tp_as_mapping  [key], len, ...*/			  \
											  \
  /* more methods */									  \
  (hashfunc)     0,		/* tp_hash    "dict[x]" */				  \
  (ternaryfunc)  0,             /* tp_call    "x()"     */				  \
  (reprfunc)     0,             /* tp_str     "str(x)"  */				  \
  (getattrofunc) 0,		/* tp_getattro */					  \
  (setattrofunc) 0,		/* tp_setattro */					  \
  0,				/* tp_as_buffer */					  \
  0L,				/* tp_flags */						  \
  PyGSL_ODEIV_GENERIC_DOC       /* doc */                                                 \
};

#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_step
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_step"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_step_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_step_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_step_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_step_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_step_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_control
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_control"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_control_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_control_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_control_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_control_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_control_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_evolve
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_evolve"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_evolve_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_evolve_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_evolve_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_evolve_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_evolve_free
PyGSL_ODEIV_GENERIC_ALL
/**/;

static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     Py_DECREF(self->py_func);
     Py_XDECREF(self->py_jac);
     Py_DECREF(self->arguments);
     gsl_odeiv_step_free(self->step);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     gsl_odeiv_step_reset(self->step);
     Py_INCREF(Py_None);
     return Py_None;
}

static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyString_FromString(gsl_odeiv_step_name(self->step));
}

{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyInt_FromLong((long) gsl_odeiv_step_order(self->step));
}

/* --------------------------------------------------------------------------- */
/* control_hadjust needs a few arrays */
/*
extern int
gsl_odeiv_control_hadjust (gsl_odeiv_control * c, gsl_odeiv_step * s,
			   const double y0[],  const double yerr[],
			   const double dydt[], double * h);
*/

static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     Py_DECREF(self->step);
     //gsl_odeiv_control_free(self->control);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     return PyString_FromString(gsl_odeiv_control_name(self->control));
}

static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     Py_DECREF(self->step);
     Py_DECREF(self->control);
     gsl_odeiv_evolve_free(self->evolve);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     gsl_odeiv_evolve_reset(self->evolve);
     Py_INCREF(Py_None);
     return Py_None;
}

#if 0
static
void create_odeiv_step_types(PyObject *module_dict)
{

     PyGSL_odeiv_step_type *a_odeiv_step = NULL;
     PyObject *item=NULL;

     gsl_odeiv_step_type ** thistype;
     gsl_odeiv_step_type const * const step_types[]  ={
	  gsl_odeiv_step_rk2,
	  gsl_odeiv_step_rk4,
	  gsl_odeiv_step_rkf45,
	  gsl_odeiv_step_rkck,
	  gsl_odeiv_step_rk8pd,
	  gsl_odeiv_step_rk2imp,
	  gsl_odeiv_step_rk4imp,
	  gsl_odeiv_step_bsimp,
	  gsl_odeiv_step_gear1,
	  gsl_odeiv_step_gear2,
	  NULL
     };

     FUNC_MESS_BEGIN();

     thistype = (gsl_odeiv_step_type **) step_types;
     while((*thistype) != NULL){
	  a_odeiv_step =  PyObject_NEW(PyGSL_odeiv_step_type, &PyGSL_odeiv_step_type_pytype);
	  assert(a_odeiv_step);
	  a_odeiv_step->step_type = (gsl_odeiv_step_type *) *thistype;
	  item = PyString_FromString((*thistype)->name);
	  DEBUG_MESS(2, "Preparing step type -->%s<--", PyString_AsString(item));
	  PyGSL_clear_name(PyString_AsString(item), PyString_Size(item));
	  DEBUG_MESS(2, "Adding step type -->%s<--",  PyString_AsString(item));
	  assert(item);
	  PyDict_SetItem(module_dict, item, (PyObject *) a_odeiv_step);
	  /* Py_DECREF(item); */
	  item = NULL;
	  thistype++;

     }
     FUNC_MESS_END();
}
#endif

static PyMethodDef PyGSL_odeiv_module_functions[] = {
     {"step_rk2",    PyGSL_odeiv_step_init_rk2,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4",    PyGSL_odeiv_step_init_rk4,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkf45",  PyGSL_odeiv_step_init_rkf45,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkck",   PyGSL_odeiv_step_init_rkck,   METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk8pd",  PyGSL_odeiv_step_init_rk8pd,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk2imp", PyGSL_odeiv_step_init_rk2imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4imp", PyGSL_odeiv_step_init_rk4imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_bsimp",  PyGSL_odeiv_step_init_bsimp,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear1",  PyGSL_odeiv_step_init_gear1,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear2",  PyGSL_odeiv_step_init_gear2,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"control_standard_new", PyGSL_odeiv_control_init_standard_new, METH_VARARGS, NULL},
     {"control_y_new",        PyGSL_odeiv_control_init_y_new,        METH_VARARGS, NULL},
     {"control_yp_new",       PyGSL_odeiv_control_init_yp_new,       METH_VARARGS, NULL},
     {"evolve", PyGSL_odeiv_evolve_init, METH_VARARGS, NULL},
     {NULL, NULL, 0}        /* Sentinel */
};

void
initodeiv(void)
{
     PyObject *m=NULL, *item=NULL, *dict=NULL;

     FUNC_MESS_BEGIN();
     fprintf(stderr, "Compiled at %s %s\n", __DATE__, __TIME__);
     m = Py_InitModule("odeiv", PyGSL_odeiv_module_functions);
     assert(m);
     module = m;
     import_array();
     init_pygsl();

     PyGSL_odeiv_step_type_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_control_type_pytype.ob_type = &PyType_Type;

     PyGSL_odeiv_step_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_control_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_evolve_pytype.ob_type = &PyType_Type;

     dict = PyModule_GetDict(m);
     /* create_odeiv_step_types(dict); */
     if(!dict)
	  goto fail;

     if (!(item = PyString_FromString(odeiv_module_doc))){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not generate module doc string!");
	  goto fail;
     }
     if (PyDict_SetItemString(dict, "__doc__", item) != 0){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not init doc string!");
	  goto fail;
     }

     FUNC_MESS_END();
     return;
 fail:
     FUNC_MESS("Fail");
     fprintf(stderr, "Import of module odeiv failed!\n");
}


/**
 * File: scaleback_utils.c
 * Subroutine for the WFC3 background scaling
 *
 */
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_sort.h>
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spc_FITScards.h"
#include "spce_sect.h"
#include "spce_fitting.h"
#include "spce_is_in.h"
#include "spc_back.h"
#include "spce_pathlength.h"
#include "trfit_utils.h"
#include "nicback_utils.h"
#include "scaleback_utils.h"

/**
 * Function: fit_to_FITScards
 * Fills the results of the fitting into a set of fits cards.
 *
 * Parameters:
 * @param bck_vals   - vector with the fit results
 * @param npixels    - dimension of all images
 *
 * Returns:
 * @return cards - the list of fits header cards
 */
FITScards *fit_to_FITScards(const gsl_vector* bck_vals, const px_point npixels)
{
    char templt[FLEN_CARD];
    int i=0,keytype, f_status=0;
    int npix_tot;

    FITScards *cards;

    // allocate the cards
    cards = allocate_FITScards(7);

    // compute the number of pixels
    npix_tot = npixels.x * npixels.y;

    i=0;
    sprintf(templt,"SCALVAL = %e / computed scale value", gsl_vector_get(bck_vals, 0));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"SCALERR = %e / error for scale value", gsl_vector_get(bck_vals, 1));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NPIXINI = %e / initial fill value", gsl_vector_get(bck_vals, 2));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NPIXFIN = %e / final fill value", gsl_vector_get(bck_vals, 3));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"FRACINI = %e / initial fill value", gsl_vector_get(bck_vals, 2) / (float)npix_tot);
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"FRACFIN = %e / final fill value", gsl_vector_get(bck_vals, 3) / (float)npix_tot);
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NITER = %d / number of iterations", (int)gsl_vector_get(bck_vals, 4));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);

    // return the filled cards
    return cards;
}

/**
 * Function: make_scale_back
 * Loads the various image data inputs and performs a fit using a kappa-sigma
 * clipping iteration. Then a scaled version of one of the input images
 * is produced and written to disk. Optionally, an ASCII list with the
 * pixel values before the fitting is produced.
 *
 * Parameters:
 * @param grism_image - full pathname to the grism image
 * @param grism_mask  - full pathname to the mask image
 * @param conf_file   - full pathname to the configuration file
 * @param scale_image - full pathname to the scaling (=master sky) image
 * @param bck_image   - full pathname to the scaled (=background) image
 * @param plist_name  - full pathname to the pixel list
 * @param scale_to_master - integer/boolean to specify what should be scaled as output
 * @param make_plis   - integer/boolean to request a pixel list as output
 *
 * Returns:
 * @return -
 */
void
make_scale_back(char grism_image[], const char grism_mask[], char conf_file[],
		const char scale_image[], char bck_image[], const char plist_name[],
		const int scale_to_master, const int make_plis)
{
  px_point npixels;

  gsl_matrix *gr_img;
  gsl_matrix *gr_dqval;
  gsl_matrix *gr_mask;
  gsl_matrix *sc_img;
  gsl_matrix *bck_img;

  gsl_vector *bck_vals;

  fitbck_data *fbck_data;
  //int f_status=0;

  FITScards *cards;

  aperture_conf  *conf;

  char  ID[MAXCHAR];

  // fix the extension name
  sprintf (ID, "BCK");

  // read the configuration file
  // determine all extensions
  conf = get_aperture_descriptor(conf_file);
  get_extension_numbers(grism_image, conf, conf->optkey1, conf->optval1);

  // load the scale image
  fprintf(stdout,"Loading DATA from: %s...", scale_image);
  sc_img = FITSimage_to_gsl(scale_image, 1, 1);
  fprintf(stdout,". Done.\n");

  // load the grism image
  fprintf(stdout,"Loading DATA from: %s...", grism_image);
  gr_img = FITSimage_to_gsl(grism_image, conf->science_numext, 1);
  fprintf(stdout,". Done.\n");

  if (conf->dq_numext < 0)
    {
    // allocate the space for the dq-image
    // set all values to 0.0
    gr_dqval = gsl_matrix_alloc(gr_img->size1, gr_img->size2);
    gsl_matrix_set_all (gr_dqval, 0.0);
    }
  else
    {
    // load the scale dq values
    fprintf(stdout,"Loading DQ from: %s...", grism_image);
    gr_dqval = FITSimage_to_gsl(grism_image, conf->dq_numext, 1);
    fprintf(stdout,". Done.\n");
    }

  // load the grism mask
  fprintf(stdout,"Loading DATA from: %s...", grism_mask);
  gr_mask = FITSimage_to_gsl(grism_mask, 2, 1);
  fprintf(stdout,". Done.\n");

  // get the number of pixels
  npixels.x = sc_img->size1;
  npixels.y = sc_img->size2;

  // report the number of pixels
  //fprintf(stdout,"Loading DATA from: %i pix\n", npixels.x * npixels.y);

  // allocate memory for the fit data
  fbck_data = alloc_fitbck_data(npixels.x * npixels.y);

  // fill the data into the structure
  if (scale_to_master)
    fill_cont_data(gr_img, gr_dqval, gr_mask, sc_img, fbck_data, conf->dqmask);
  else
    fill_mask_data(gr_img, gr_dqval, gr_mask, sc_img, fbck_data, conf->dqmask);

  // make a pixel list if desired
  if (make_plis)
    print_plis(fbck_data, plist_name);

  // make the fit
  bck_vals = make_ksig_scalefit(fbck_data);

  // report the result of the fit onto the screen
  fprintf(stdout, "\nScale result image %s : c0 = %f +- %f", grism_image, gsl_vector_get(bck_vals, 0), gsl_vector_get(bck_vals, 1));
  fprintf(stdout, "\nInitial fill factor: %.1f%%, final: %.1f%%", 100.0 * gsl_vector_get(bck_vals, 2) / ((float)npixels.x * (float)npixels.y), 100.0 *gsl_vector_get(bck_vals, 3) / ((float)npixels.x * (float)npixels.y));
  fprintf(stdout, "\nNumber of iterations: % 2i\n\n", (int)gsl_vector_get(bck_vals, 4));

  if (scale_to_master)
    bck_img = compute_scale_grism(gr_img, gr_dqval, gr_mask, conf->dqmask, bck_vals);
  else
    bck_img = compute_scale_master(sc_img, bck_vals);

  // write the background image to a file
  fprintf(stdout,"Writing data to: %s...", bck_image);
  gsl_to_FITSimage (bck_img, bck_image, 1, ID);
  cards = fit_to_FITScards(bck_vals, npixels);
  put_FITS_cards(bck_image, 1, cards);
  fprintf(stdout,". Done.\n");

  // release memory
  free_fitbck_data(fbck_data);
  free_FITScards(cards);
  gsl_matrix_free(sc_img);
  gsl_matrix_free(gr_img);
  gsl_matrix_free(gr_dqval);
  gsl_matrix_free(gr_mask);
  gsl_matrix_free(bck_img);
  gsl_vector_free(bck_vals);
  free_aperture_conf(conf);
}

/**
 * Function: print_plis
 * Prints the content of a pixel list structure
 * to an ASCII file.
 *
 * Parameters:
 * @param fbck_data  - the list of pixel values
 * @param plist_name - full pathname to the pixel list
 *
 * Returns:
 * @return -
 */
void
print_plis(const fitbck_data *fbck_data, const char plist_name[])
{
    int index=0;
    char Buffer[10240];
    FILE *fout;

    // open the pixel list
    fout = fopen(plist_name, "w");

    // go over all data values
    for (index=0; index < fbck_data->n_data; index++)
      {
      // put the values into the buffer
      sprintf (Buffer, "%i %i %e %e\n",fbck_data->x_pos[index], fbck_data->y_pos[index],
          fbck_data->x_values[index], fbck_data->y_values[index]);

      // push the buffer to the  file
      fputs (Buffer, fout);
      }

    // close the pixel file
    fclose(fout);
}

/**
 * Function: compute_scale_grism
 * Computes a scaled version of the input grism images, using the
 * scale given as input. Pixels masked in other images are given
 * a fixed value.
 *
 * Parameters:
 * @param gr_img  - the list of pixel values
 * @param gr_dqval - full pathname to the pixel list
 * @param gr_mask - full pathname to the pixel list
 * @param bck_vals - full pathname to the pixel list
 *
 * Returns:
 * @return bck_img - the scaled grism image
 */
gsl_matrix *
compute_scale_grism(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
		int dqmask, gsl_vector *bck_vals)
{
    gsl_matrix *bck_img;

    int ii, jj;
    double scale=0.0;

    // get the scale
    scale = gsl_vector_get(bck_vals, 0);

    // allocate the space for the background image
    bck_img = gsl_matrix_alloc(gr_img->size1, gr_img->size2);

    // go over each row
    for (ii=0; ii < (int)gr_img->size1; ii++)
      // go over each column
      for (jj=0; jj < (int)gr_img->size2; jj++)
        // compute and set the value in the background image
        if (gsl_matrix_get(gr_mask, ii, jj) > 0.0 ||
            (int)gsl_matrix_get(gr_dqval, ii, jj) & dqmask)
          gsl_matrix_set(bck_img, ii, jj, MASK_VALUE);
        else
          // using the fit values only
          gsl_matrix_set(bck_img, ii, jj, gsl_matrix_get(gr_img, ii, jj) * scale);

    // return the background image
    return bck_img;
}

/**
 * Function: compute_scale_master
 * Computes a scaled version of the scaling (=master sky) image,
 * using the scale given as input.
 *
 * Parameters:
 * @param sc_img  - the list of pixel valuessc_img
 * @param bck_vals - full pathname to the pixel list
 *
 * Returns:
 * @return bck_img - the scaled grism image
 */
gsl_matrix *
compute_scale_master(const gsl_matrix *sc_img, const gsl_vector *bck_vals)
{
    gsl_matrix *bck_img;

    int ii, jj;
    double scale=0.0;

    // get the scale
    scale = gsl_vector_get(bck_vals, 0);

    // allocate the space for the background image
    bck_img = gsl_matrix_alloc(sc_img->size1, sc_img->size2);

    // go over each row
    for (ii=0; ii < (int)sc_img->size1; ii++)
      // go over each column
      for (jj=0; jj < (int)sc_img->size2; jj++)
        // scale the master sky
        gsl_matrix_set(bck_img, ii, jj, gsl_matrix_get(sc_img, ii, jj) * scale);

    // return the background image
    return bck_img;
}

/**
 * Function: fill_mask_data
 * Fills the pixel value structure with the data for the x/y-positions,
 * the grism image and the scaling image values and a weigth. dq-flagged
 * pixels and masked pixel are neglected. The mask is supposed to be a background
 * mask where pixel with a value < 900000 shall be neglected.
 * The weights are initially all set to 1.
 *
 * Parameters:
 * @param gr_img    - the grism image array
 * @param gr_dqval  - the dq-value array
 * @param gr_mask   - the grism mask array
 * @param sc_img    - the scaling image array
 * @param fbck_data - the pixel list structure
 * @param dqmask    - dq value from the configuration file
 *
 * Returns:
 * @return -
 */
void
fill_mask_data(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
		gsl_matrix *sc_img, fitbck_data *fbck_data, int dqmask)
{
    int ix=0;
    int iy=0;
    int index=0;

    // intitialize the index for
    // the fit_data structure
    index = 0;

    // go over all pixels
    // in the grism image
    for (ix=0; ix < (int)sc_img->size1; ix++)
      {
      for (iy=0; iy < (int)sc_img->size2; iy++)
        {
        // set the scale image pixel as independent variable
        fbck_data->x_values[index] = gsl_matrix_get(gr_img, ix, iy);

        // set the grism image pixel as dependent variable
        fbck_data->y_values[index] = gsl_matrix_get(sc_img, ix, iy);;

        // set the x- and y-positions
        fbck_data->x_pos[index] = ix;
        fbck_data->y_pos[index] = iy;

        // check whether the pixel was masked out
        // or is part of an object
        if (gsl_matrix_get(sc_img, ix, iy) < 0.0 ||
            gsl_matrix_get(gr_mask, ix, iy) < -900000.0 ||
            ((int)gsl_matrix_get(gr_dqval, ix, iy) & dqmask))
          {
          continue;
          }
        else
          {
          // give the pixel full weight
          //x,y,value_back,value_grism_imag
          fbck_data->e_values[index] = 1.0;

          // enhance the counter
          index++;
          }
        }
      }

    fbck_data->n_data = index;
    fprintf(stdout, "\nNumber of pixels in structure: %i\n\n", fbck_data->n_data);

    if (fbck_data->n_data < 1)
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
          "aXe_SCALEBCK: no data left to determine the scale!\n");
}

/**
 * Function: fill_cont_data
 * Fills the pixel value structure with the data for the x/y-positions,
 * the grism image and the scaling image values and a weigth. dq-flagged
 * pixels and masked pixel are neglected. The mask is supposed to be a geometric
 * contamination where pixel with a value > 0 shall be neglected.
 * The weights are initially all set to 1.
 *
 * Parameters:
 * @param gr_img    - the grism image array
 * @param gr_dqval  - the dq-value array
 * @param gr_mask   - the grism mask array
 * @param sc_img    - the scaling image array
 * @param fbck_data - the pixel list structure
 *
 * Returns:
 * @return -
 */
void
fill_cont_data(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
                gsl_matrix *sc_img, fitbck_data *fbck_data, int dqmask)
{
    int ix=0;
    int iy=0;
    int index=0;

    // intitialize the index for
    // the fit_data structure
    index = 0;

    // go over all pixels
    // in the grism image
    for (ix=0; ix < (int)sc_img->size1; ix++)
      {
      for (iy=0; iy < (int)sc_img->size2; iy++)
        {
        // set the scale image pixel as independent variable
        fbck_data->x_values[index] = gsl_matrix_get(sc_img, ix, iy);

        // set the grism image pixel as dependent variable
        fbck_data->y_values[index] = gsl_matrix_get(gr_img, ix, iy);;

        // set the x- and y-positions
        fbck_data->x_pos[index] = ix;
        fbck_data->y_pos[index] = iy;

        // check whether the pixel was masked out
        // or is part of an object
        if (gsl_matrix_get(sc_img, ix, iy) < 0.0 ||
            gsl_matrix_get(gr_mask, ix, iy) > 0.0 ||
            ((int)gsl_matrix_get(gr_dqval, ix, iy) & dqmask))
          continue;
        else
          {
          // give the pixel full weight
          //x,y,value_back,value_grism_imag
          fbck_data->e_values[index] = 1.0;

          // enhance the counter
          index++;
          }
        }
      }

    fbck_data->n_data = index;
    fprintf(stdout, "\nNumber of pixels in structure: %i\n\n", fbck_data->n_data);

    if (fbck_data->n_data < 1)
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
          "aXe_SCALEBCK: no data left to determine the scale!\n");
}

/**
 * Function: make_ksig_scalefit
 * Determines the scale from the values in the pixel value structure.
 * The results are determined iteratively while rejecting pixels via
 * kappa-sigma clipping to get robust measurements. The results are
 * the scale value, the standard deviation, the final number of
 * not rejected points and the number of iterations.
 *
 * Parameters:
 * @param fbck_data - the pixel value structure
 *
 * Returns:
 * @return bck_vals - a vector with the fit results
 */
gsl_vector *
make_ksig_scalefit(fitbck_data *fbck_data)
{
  gsl_vector *bck_vals=NULL;

  float old_scale;
  int index=0;
  int clipped=1;

  bck_vals = gsl_vector_alloc(6);

  // check whether iterations still
  // must be done and whether
  // the data was changed from clipping
  old_scale= 1.0e+06;
  while (index < N_BCKSCALE_ITER && clipped)
    {

    // make a non-weighted linear fit
    get_bck_scale(fbck_data->x_values, fbck_data->y_values, fbck_data->e_values,
        fbck_data->n_data, bck_vals);

    // make a clipping iteration
    clipped = clipp_scale_data(fbck_data, bck_vals, N_BCKSCALE_KAPPA);

    // break the iteration if the scale change is below a certain threshold
    if (fabs((gsl_vector_get(bck_vals, 0)-old_scale)/gsl_vector_get(bck_vals, 0)) < N_BCKSCALE_ACCUR)
      clipped = 0;

    // store the new scale
    old_scale = gsl_vector_get(bck_vals, 0);

    // inhance the clipping counter
    index++;
    }

  // set the number of iterations
  gsl_vector_set(bck_vals, 4, (double)index);

  // return the fit result
  return bck_vals;
}

/**
 * Function: get_bck_scale
 * Computes the scale value from the pixel values in two arrays.
 * The scale value is the median value of all input with weight.
 * Also the standard deviation, the total number of input and
 * the number of input with weight is returned.
 *
 * Parameters:
 * @param xs       - pixel values from one image
 * @param ys       - pixel values from second image
 * @param ws       - weight values
 * @param n_elem   - number of elements
 * @param bck_vals - vector for the result
 *
 * Returns:
 * @return -
 */
void
get_bck_scale(const double *xs, double *ys, double *ws,
		  const int n_elem, gsl_vector *bck_vals)
{
  int i, m;
  double *tmp;
  double median;
  double stdev;

  // allocate space for temporary vectors
  tmp = (double *) malloc (n_elem * sizeof (double));

  // initialize the array
  for (i = 0; i < n_elem; i++)
    tmp[i] = 0.0;

  // fill the temporary vectors
  // with scale values and weights
  m = 0;
  for (i = 0; i < n_elem; i++)
  {
    if (ws[i] > 0.0 && ys[i] != 0.0)
      {
      tmp[m] = xs[i] / ys[i];
      m++;
      }
  }

  // sort the vector
  gsl_sort( tmp, 1, m);

  // just confirm the sorting
  for (i = 1; i < m; i++)
    if (tmp[i] < tmp[i-1])
      fprintf(stdout, "Wrong: %f <--> %f\n", tmp[i],tmp[i-1]);

  // take the home made median
  median = tmp[(int)m/2];

  // get the standard deviation
  stdev = comp_stdev_from_array(tmp, m, median);

  // put the results in the vector
  gsl_vector_set(bck_vals, 0, median);
  gsl_vector_set(bck_vals, 1, stdev);
  gsl_vector_set(bck_vals, 2, (double)n_elem);
  gsl_vector_set(bck_vals, 3, (double)m);

  // release memory
  free(tmp);
}

/**
 * Function: clipp_scale_data
 * Set the weight in the pixel value structure such as to clip
 * point that deviate from the mean/characteristic value by
 * more than the allowed amount.
 *
 * Parameters:
 * @param fbck_data - pixel values from one image
 * @param bck_vals  - vector for the result
 * @param kappa     - vector for the result
 *
 * Returns:
 * @return nclip - integer/boolean indicating new clipping
 */
int
clipp_scale_data(fitbck_data *fbck_data, const gsl_vector *bck_vals,
		const float kappa)
{
    int index;
    int nclip=0;
    int newclip=0;
    double mean;
    //double stdev;
    double absdev;

    // get the mean value
    mean = gsl_vector_get(bck_vals, 0);

    // compute the maximum allowed deviation
    absdev = kappa * gsl_vector_get(bck_vals, 1);

    // go over all data values
    for (index=0; index < fbck_data->n_data; index++)
      {
      // avoid zero values
      if (fbck_data->y_values[index] != 0.0)
        {
        // check whether the actual value is outside the allowed range
        if (fabs(mean - (fbck_data->x_values[index]/fbck_data->y_values[index])) > absdev)
          {
          // check and mark clip changes
          if (fbck_data->e_values[index])
            newclip = 1;

          // set the weight, change the counter
          fbck_data->e_values[index] = 0.0;
          nclip++;
          }
        else
          {
          // set the weight
          fbck_data->e_values[index] = 1.0;
          }
        }
      else
        {
        // set the weight, change the counter
        fbck_data->e_values[index] = 0.0;
        nclip++;
        }
      }

    // return the newclip indicator
    return newclip;
}