/*
 * neuro.c
 *
 *  Created on: Oct 9, 2019
 *      Author: alexey
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_rng.h>
#include <png.h>
#include <malloc.h>

#include "neuro.h"

typedef struct _neuro_skeleton {
	int input_l;
	int output_l;
	int hidden_l;
	int number_of_hidden;
	double alpha;
	gsl_matrix *input_m;
	gsl_matrix *output_m;
	gsl_matrix **hidden_m;
	gsl_matrix **dropuot;
	gsl_matrix **layers;
	gsl_matrix **layers_delta;
	gsl_matrix *temp;
	gsl_matrix *train;
	gsl_matrix *error;
	int layers_numb;
	activation activ;
	activation *activ_l;
} neuro_skeleton;

const gsl_rng_type * T;
gsl_rng * r;

extern unsigned long int gsl_rng_default_seed;

#define CLEAN_BLOB() bayrepo_clean_neuro((void *) blob); \
		return NULL
#define CLEAN_IF_NULL(x) if (!blob->x) { \
		CLEAN_BLOB(); \
	}

#define ISDEBUGINFO_BEG() if (getenv("MATRIXD") && !strcmp(getenv("MATRIXD"),"1")) {
#define ISDEBUGINFO_END() }
#define DEBUGINFO(A, B, C) ISDEBUGINFO_BEG()\
		bayrepo_print_matrix(A, B, C); \
		ISDEBUGINFO_END()

#define MAX_LAYER_NAME_LEN 1024
#define MAX_BUFFER_LEN 4096

static void bayrepo_print_matrix(gsl_matrix *a, const char *matrix_name,
		int index_matrix) {
	int index, jndex;
	if (index_matrix >= 0) {
		printf("Matrix %s[%d]:\n", matrix_name, index_matrix);
	} else {
		printf("Matrix %s:\n", matrix_name);
	}
	for (index = 0; index < a->size1; index++) {
		printf("==");
		for (jndex = 0; jndex < a->size2; jndex++) {
			printf("%.3f   ", gsl_matrix_get(a, index, jndex));
		}
		printf("==\n");
	}
}

static void bayrepo_layers_clean(void *blob) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (data->layers) {
			int index = 0;
			for (index = 0; index < data->layers_numb; index++) {
				if (data->layers[index])
					gsl_matrix_free(data->layers[index]);
			}
			free(data->layers);
			data->layers = NULL;
		}
		if (data->layers_delta) {
			int index = 0;
			for (index = 0; index < data->layers_numb; index++) {
				if (data->layers_delta[index])
					gsl_matrix_free(data->layers_delta[index]);
			}
			data->layers_numb = 0;
			free(data->layers_delta);
			data->layers_delta = NULL;
		}
		data->layers_numb = 0;
	}
}

void bayrepo_clean_neuro(void *blob) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (data->input_m) {
			gsl_matrix_free(data->input_m);
			data->input_m = NULL;
		}
		if (data->output_m) {
			gsl_matrix_free(data->output_m);
			data->output_m = NULL;
		}
		if (data->train) {
			gsl_matrix_free(data->train);
			data->train = NULL;
		}
		if (data->temp) {
			gsl_matrix_free(data->temp);
			data->temp = NULL;
		}
		if (data->error) {
			gsl_matrix_free(data->error);
			data->error = NULL;
		}
		if (data->activ_l) {
			free(data->activ_l);
			data->activ_l = NULL;
		}
		if (data->number_of_hidden > 0 && data->hidden_m) {
			int i;
			for (i = 0; i < data->number_of_hidden; i++) {
				if (data->hidden_m[i]) {
					gsl_matrix_free(data->hidden_m[i]);
					data->hidden_m[i] = NULL;
				}
			}
			free(data->hidden_m);
		}
		if (data->number_of_hidden > 0 && data->dropuot) {
			int i;
			for (i = 0; i < data->number_of_hidden; i++) {
				if (data->dropuot[i]) {
					gsl_matrix_free(data->dropuot[i]);
					data->dropuot[i] = NULL;
				}
			}
			free(data->dropuot);
		}
		bayrepo_layers_clean(blob);
		free(data);
	}
	if (r) {
		gsl_rng_free(r);
		r = NULL;
	}
}

void bayrepo_set_layer_activ(void * blob, int layer_number, activation activ) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (data->number_of_hidden) {
			if (layer_number <= data->number_of_hidden) {
				data->activ_l[layer_number] = activ;
			}
		} else {
			if (!layer_number) {
				data->activ_l[0] = activ;
			}
		}
	}
}

static int bayrepo_random_matrix(gsl_matrix *a, int m, int n, int is_negative) {
	int index, jndex;

	if (!r)
		return -1;

	for (index = 0; index < m; index++) {
		for (jndex = 0; jndex < n; jndex++) {
			gsl_matrix_set(a, index, jndex,
					gsl_rng_uniform(r) - (is_negative ? 0.5 : 0.0));
		}
	}

	return 0;
}

void *bayrepo_init_neuro(int input, int output, int hidden, int hidden_num,
		double alpha, activation activ) {
	int index;

	gsl_rng_default_seed = time(NULL);

	T = gsl_rng_knuthran2;
	r = gsl_rng_alloc(T);

	if (hidden_num < 0) {
		hidden_num = 0;
	}
	neuro_skeleton *blob = calloc(1, sizeof(neuro_skeleton));
	if (!blob)
		return NULL;
	blob->input_m = gsl_matrix_alloc(1, input);
	CLEAN_IF_NULL(input_m);
	blob->train = gsl_matrix_alloc(1, output);
	CLEAN_IF_NULL(train);
	blob->error = gsl_matrix_alloc(1, output);
	CLEAN_IF_NULL(error);

	if (hidden_num) {
		blob->output_m = gsl_matrix_alloc(hidden, output);
		CLEAN_IF_NULL(output_m);
		blob->hidden_m = (gsl_matrix **) calloc(hidden_num,
				sizeof(gsl_matrix *));
		CLEAN_IF_NULL(hidden_m);
		blob->dropuot = (gsl_matrix **) calloc(hidden_num,
				sizeof(gsl_matrix *));
		CLEAN_IF_NULL(dropuot);
		for (index = 1; index < hidden_num; index++) {
			blob->hidden_m[index] = gsl_matrix_alloc(hidden, hidden);
			CLEAN_IF_NULL(hidden_m[index]);
			blob->dropuot[index] = gsl_matrix_alloc(1, hidden);
			CLEAN_IF_NULL(dropuot[index]);
		}
		blob->hidden_m[0] = gsl_matrix_alloc(input, hidden);
		CLEAN_IF_NULL(hidden_m[0]);
		blob->dropuot[0] = gsl_matrix_alloc(1, hidden);
		CLEAN_IF_NULL(dropuot[0]);
		blob->temp = gsl_matrix_alloc(1, hidden);
		CLEAN_IF_NULL(temp);
	} else {
		blob->output_m = gsl_matrix_alloc(input, output);
		CLEAN_IF_NULL(output_m);
	}
	blob->alpha = alpha;
	blob->input_l = input;
	blob->output_l = output;
	blob->hidden_l = hidden;
	blob->number_of_hidden = hidden_num;

	gsl_matrix_set_zero(blob->input_m);
	gsl_matrix_set_zero(blob->train);
	if (hidden_num) {
		if (bayrepo_random_matrix(blob->output_m, hidden, output,
				(activ == RELU ? 0 : 1)) < 0) {
			CLEAN_BLOB()
;		}
		for (index = 1; index < hidden_num; index++) {
			if (bayrepo_random_matrix(blob->hidden_m[index], hidden, hidden, (activ==RELU?0:1))<0) {
				CLEAN_BLOB();
			}
		}
		if (bayrepo_random_matrix(blob->hidden_m[0], input, hidden, (activ==RELU?0:1))<0) {
			CLEAN_BLOB();
		}
	} else {
		if (bayrepo_random_matrix(blob->output_m, input, output, (activ==RELU?0:1))<0) {
			CLEAN_BLOB();
		}
	}

	int lyr_nmb = 1 + blob->number_of_hidden ? (blob->number_of_hidden + 1) : 0;

	blob->layers = (gsl_matrix **) calloc(lyr_nmb, sizeof(gsl_matrix *));
	CLEAN_IF_NULL(layers);

	if (hidden_num) {
		for (index = 1; index < hidden_num; index++) {
			blob->layers[index] = gsl_matrix_alloc(1, hidden);
			CLEAN_IF_NULL(layers[index]);
		}
		blob->layers[0] = gsl_matrix_alloc(1, hidden);
		CLEAN_IF_NULL(layers[0]);
		blob->layers[lyr_nmb - 1] = gsl_matrix_alloc(1, output);
		CLEAN_IF_NULL(layers[lyr_nmb-1]);
	} else {
		blob->layers[0] = gsl_matrix_alloc(1, output);
		CLEAN_IF_NULL(layers[0]);
	}

	blob->layers_numb = lyr_nmb;
	blob->layers_delta = (gsl_matrix **) calloc(lyr_nmb, sizeof(gsl_matrix *));
	CLEAN_IF_NULL(layers_delta);

	if (hidden_num) {
		for (index = 1; index < hidden_num; index++) {
			blob->layers_delta[index] = gsl_matrix_alloc(1, hidden);
			CLEAN_IF_NULL(layers_delta[index]);
		}
		blob->layers_delta[0] = gsl_matrix_alloc(1, hidden);
		CLEAN_IF_NULL(layers_delta[0]);
		blob->layers_delta[lyr_nmb - 1] = gsl_matrix_alloc(1, output);
		CLEAN_IF_NULL(layers_delta[lyr_nmb-1]);
	} else {
		blob->layers_delta[0] = gsl_matrix_alloc(1, output);
		CLEAN_IF_NULL(layers_delta[0]);
	}
	blob->activ = activ;
	blob->activ_l = calloc(blob->number_of_hidden + 1, sizeof(activation));
	CLEAN_IF_NULL(activ_l);
	for (index = 0; index < (blob->number_of_hidden + 1); index++) {
		if (index == blob->number_of_hidden) {
			blob->activ_l[index] = NOACTIV;
		} else {
			blob->activ_l[index] = DEFLT;
		}
	}

	return (void *) blob;
}

static void bayrepo_fill_dropout(gsl_matrix *a, int m, int n) {
	int index, jndex;
	if (!r) {
		for (index = 0; index < m; index++) {
			for (jndex = 0; jndex < n; jndex++) {
				gsl_matrix_set(a, index, jndex, 1.0);
			}
		}
		return;
	}

	for (index = 0; index < m; index++) {
		for (jndex = 0; jndex < n; jndex++) {
			gsl_matrix_set(a, index, jndex, gsl_rng_uniform_int(r, 2) * 1.0);
		}
	}

	return;
}

void bayrepo_fill_input(void *blob, int position, double scaled_value) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (position < data->input_l) {
			gsl_matrix_set(data->input_m, 0, position, scaled_value);
		}
	}
}

void bayrepo_fill_train(void *blob, int position, double scaled_value) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (position < data->output_l) {
			gsl_matrix_set(data->train, 0, position, scaled_value);
		}
	}
}

void bayrepo_fill_hidden(void *blob, int index, int position_x, int position_y,
		double scaled_value) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		gsl_matrix_set(data->hidden_m[index], position_x, position_y,
				scaled_value);
	}
}

void bayrepo_fill_outm(void *blob, int position_x, int position_y,
		double scaled_value) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		gsl_matrix_set(data->output_m, position_x, position_y, scaled_value);
	}
}

static void bayrepo_zero_layers(void *blob) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		int index;
		for (index = 0; index < data->layers_numb; index++) {
			gsl_matrix_set_zero(data->layers[index]);
		}
	}
}

static double bayrepo_activation_func(double elem, neuro_skeleton *data,
		int lyn) {
	activation act = data->activ;
	if ((lyn < (data->number_of_hidden + 1)) && (data->activ_l[lyn] != DEFLT)) {
		act = data->activ_l[lyn];
	}
	switch (act) {
	case RELU:
		return elem > 0.0 ? elem : 0.0;
	case TANH:
		return tanh(elem);
	case SIGMOID:
		return 1.0 / (1.0 + exp(-elem));
	default:
		return elem;
	}
}

activation bayrepo_get_layer_func(void *blob, int lyn) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if ((lyn < (data->number_of_hidden + 1))
				&& (data->activ_l[lyn] != DEFLT)) {
			return data->activ_l[lyn];
		}
	}
	return NOACTIV;
}

activation bayrepo_get_sublayer_func(void *blob, int lyn) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (lyn < (data->number_of_hidden + 1)) {
			return data->activ_l[lyn];
		}
	}
	return NOACTIV;
}

static double bayrepo_activation_deriv(double elem, neuro_skeleton *data,
		int lyn) {
	activation act = data->activ;
	if ((lyn < (data->number_of_hidden + 1)) && (data->activ_l[lyn] != DEFLT)) {
		act = data->activ_l[lyn];
	}
	switch (act) {
	case RELU:
		return elem > 0.0 ? 1.0 : 0.0;
	case TANH:
		return 1.0 - (elem * elem);
	case SIGMOID:
		return elem * (1.0 - elem);
	default:
		return elem;
	}
}

static void bayrepo_matix_customize(gsl_matrix *a, int size,
		neuro_skeleton *data, int lyn) {
	int index;
	for (index = 0; index < size; index++) {
		gsl_matrix_set(a, 0, index,
				bayrepo_activation_func(gsl_matrix_get(a, 0, index), data,
						lyn));
	}
}

static void bayrepo_matix_deriv(gsl_matrix *a, int size, neuro_skeleton *data,
		int lyn) {
	int index;
	for (index = 0; index < size; index++) {
		gsl_matrix_set(a, 0, index,
				bayrepo_activation_deriv(gsl_matrix_get(a, 0, index), data,
						lyn));
	}
}

static void bayrepo_query_internal(void *blob, int dropout) {
	int index;
	if (blob) {
		ISDEBUGINFO_BEG()
			printf("=====================Query=========================\n");
		ISDEBUGINFO_END()
		neuro_skeleton *data = (neuro_skeleton *) blob;
		bayrepo_zero_layers(blob);

		if (data->number_of_hidden) {
			if (dropout == 1) {
				for (index = 0; index < data->number_of_hidden; index++) {
					bayrepo_fill_dropout(data->dropuot[index], 1,
							data->hidden_l);
				}
			}
			int cnt = 0;
			gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, data->input_m,
					data->hidden_m[0], 0.0, data->layers[0]);
			bayrepo_matix_customize(data->layers[0], data->hidden_l, data, 0);
			if (dropout == 1) {
				gsl_matrix_mul_elements(data->layers[0], data->dropuot[0]);
				gsl_matrix_scale(data->layers[0], 2.0);
			}
			cnt++;
			for (index = 1; index < data->number_of_hidden; index++) {
				gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
						data->layers[index - 1], data->hidden_m[index], 0.0,
						data->layers[index]);
				bayrepo_matix_customize(data->layers[index], data->hidden_l,
						data, index);
				if (dropout == 1) {
					gsl_matrix_mul_elements(data->layers[index],
							data->dropuot[index]);
					gsl_matrix_scale(data->layers[index], 2.0);
				}
				cnt++;
			}
			gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
					data->layers[cnt - 1], data->output_m, 0.0,
					data->layers[cnt]);
			bayrepo_matix_customize(data->layers[cnt], data->output_l, data,
					cnt);

		} else {
			gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, data->input_m,
					data->output_m, 0.0, data->layers[0]);
			bayrepo_matix_customize(data->layers[0], data->output_l, data, 0);
		}

		DEBUGINFO(data->input_m, "INPUTM", -1);
		if (data->number_of_hidden) {
			for (index = 0; index < data->number_of_hidden; index++) {
				DEBUGINFO(data->hidden_m[index], "HIDDEN", index);
			}
		}
		DEBUGINFO(data->output_m, "OUTPUTM", -1);
		for (index = 0; index < data->layers_numb; index++) {
			DEBUGINFO(data->layers[index], "LAYER", index);
		}
	}
}

void bayrepo_query(void *blob) {
	bayrepo_query_internal(blob, 0);
}

double bayrepo_get_result(void *blob, int position) {
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		if (data->layers && position < data->output_l) {
			return gsl_matrix_get(data->layers[data->layers_numb - 1], 0,
					position);
		}
	}
	return -10000.0;
}

double bayrepo_get_sum(gsl_matrix *a) {
	double result = 0.0;
	int index, jndex = 0;
	for (index = 0; index < a->size1; index++) {
		for (jndex = 0; jndex < a->size2; jndex++) {
			result += gsl_matrix_get(a, index, jndex);
		}
	}
	return result;
}

void bayrepo_train(void *blob, int epoch, int use_dropout) {
	int index;
	if (blob) {
		while (epoch--) {
			ISDEBUGINFO_BEG()
				printf(
						"=====================Epoch %d=========================\n",
						epoch);
			ISDEBUGINFO_END()
			neuro_skeleton *data = (neuro_skeleton *) blob;
			bayrepo_query_internal(blob, use_dropout);
			gsl_matrix_memcpy(data->layers_delta[data->layers_numb - 1],
					data->layers[data->layers_numb - 1]);
			ISDEBUGINFO_BEG()
				gsl_matrix_memcpy(data->error,
						data->layers[data->layers_numb - 1]);
			ISDEBUGINFO_END();
			gsl_matrix_sub(data->layers_delta[data->layers_numb - 1],
					data->train);
			ISDEBUGINFO_BEG()
				gsl_matrix_sub(data->error, data->train);
				gsl_matrix_mul_elements(data->error, data->error);
				printf("=======>Error=%f\n", bayrepo_get_sum(data->error));
			ISDEBUGINFO_END();
			if (data->number_of_hidden) {
				int index;
				for (index = (data->layers_numb - 2); index >= 0; index--) {
					gsl_matrix_set_zero(data->temp);
					gsl_matrix_memcpy(data->temp, data->layers[index]);
					bayrepo_matix_deriv(data->temp, data->hidden_l, data,
							index);
					if (index == (data->layers_numb - 2)) {
						gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0,
								data->layers_delta[index + 1], data->output_m,
								0.0, data->layers_delta[index]);
					} else {
						gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0,
								data->layers_delta[index + 1],
								data->hidden_m[index + 1], 0.0,
								data->layers_delta[index]);
					}
					gsl_matrix_mul_elements(data->layers_delta[index],
							data->temp);
				}

				if (use_dropout) {
					for (index = 0; index < data->number_of_hidden; index++) {
						gsl_matrix_mul_elements(data->layers_delta[index],
								data->dropuot[index]);
					}
				}

				for (index = (data->layers_numb - 1); index >= 0; index--) {
					if (index == (data->layers_numb - 1)) {
						gsl_blas_dgemm(CblasTrans, CblasNoTrans, -data->alpha,
								data->layers[index - 1],
								data->layers_delta[index], 1.0, data->output_m);
					} else if (index == 0) {
						gsl_blas_dgemm(CblasTrans, CblasNoTrans, -data->alpha,
								data->input_m, data->layers_delta[index], 1.0,
								data->hidden_m[0]);
					} else {
						gsl_blas_dgemm(CblasTrans, CblasNoTrans, -data->alpha,
								data->layers[index - 1],
								data->layers_delta[index], 1.0,
								data->hidden_m[index]);
					}
				}
			} else {
				gsl_blas_dgemm(CblasTrans, CblasNoTrans, -data->alpha,
						data->input_m, data->layers_delta[0], 1.0,
						data->output_m);
			}
			for (index = 0; index < data->layers_numb; index++) {
				DEBUGINFO(data->layers_delta[index], "LAYER_DELTA", index);
			}
		}
	}
}

static void byrepo_png_write_data(png_structp png_ptr, png_bytep data,
		png_size_t length) {

	bayrepo_mem_encode* p = (bayrepo_mem_encode*) png_get_io_ptr(png_ptr);
	size_t nsize = p->size + length;

	if (p->buffer)
		p->buffer = realloc(p->buffer, nsize);
	else
		p->buffer = malloc(nsize);

	if (!p->buffer)
		png_error(png_ptr, "Write Error");

	memcpy(p->buffer + p->size, data, length);
	p->size += length;
}

static void bayrepo_png_flush(png_structp png_ptr) {
}

int bayrepo_write_matrix(void *blob, FILE *fp, int width, int height,
		bayrepo_mem_encode *buffer) {
	int code = 0;
	if (buffer) {
		buffer->buffer = NULL;
		buffer->size = 0;
	}
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;
	png_bytep row = NULL;
	if ((blob == NULL) || (fp == NULL)) {
		return -1;
	}

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		code = -3;
		goto finalise;
	}

	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		code = -4;
		goto finalise;
	}

	if (setjmp(png_jmpbuf(png_ptr))) {
		code = -5;
		goto finalise;
	}

	if (buffer) {
		png_set_write_fn(png_ptr, buffer, byrepo_png_write_data,
				bayrepo_png_flush);
	} else {
		png_init_io(png_ptr, fp);
	}

	neuro_skeleton *data = (neuro_skeleton *) blob;

	int picSize = (height + 3) + (height + 3) * data->number_of_hidden;

	png_set_IHDR(png_ptr, info_ptr, width, picSize, 8, PNG_COLOR_TYPE_GRAY,
	PNG_INTERLACE_NONE,
	PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

	png_write_info(png_ptr, info_ptr);

	row = (png_bytep) malloc(3 * width * sizeof(png_byte));
	if (!row) {
		code = -5;
		goto finalise;
	}

	int index = 0;
	for (index = 0; index < data->number_of_hidden + 1; index++) {
		gsl_matrix * m = NULL;
		if (data->number_of_hidden) {
			m = (index == data->number_of_hidden) ?
					data->output_m : data->hidden_m[index];
		} else {
			m = data->output_m;
		}

		double rangeMax = gsl_matrix_max(m);
		double rangeMin = gsl_matrix_min(m);

		if (((width / m->size1) == 0) || ((height / m->size2) == 0)) {
			return -2;
		}

		int sizeX = width / m->size1;
		int sizeY = height / m->size2;

		int x, y;
		for (y = 0; y < height; y++) {
			for (x = 0; x < width; x++) {
				int curX = x / sizeX;
				int curY = y / sizeY;
				if ((curX >= m->size1) || (curY >= m->size2)) {
					row[x] = (png_byte) 255;
				} else {
					png_byte res_color = (png_byte) ((gsl_matrix_get(m, curX,
							curY) - rangeMin) / (rangeMax - rangeMin) * 255.0);
					if (res_color > 255.0)
						res_color = 255.0;
					row[x] = (png_byte) (255.0 - res_color);
				}
			}
			png_write_row(png_ptr, row);
		}
		for (y = 0; y < 3; y++) {
			for (x = 0; x < width; x++) {
				if (y != 1) {
					row[x] = (png_byte) 255;
				} else {
					row[x] = (png_byte) 0;
				}
			}
			png_write_row(png_ptr, row);
		}
	}

	png_write_end(png_ptr, NULL);

	finalise: if (info_ptr != NULL)
		png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
	if (png_ptr != NULL)
		png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
	if (row != NULL)
		free(row);
	return code;

}

void bayrepo_print_matrix_custom(void *blob, bayrepo_decorator *decor) {
	char layer_name[MAX_LAYER_NAME_LEN];
	if (blob) {
		neuro_skeleton *data = (neuro_skeleton *) blob;
		int index = 0;
		for (index = 0; index < (data->number_of_hidden + 1); index++) {
			gsl_matrix * m = NULL;
			if (data->number_of_hidden) {
				m = (index == data->number_of_hidden) ?
						data->output_m : data->hidden_m[index];
				if (index == data->number_of_hidden) {
					snprintf(layer_name, MAX_LAYER_NAME_LEN, "Output layer");
				} else {
					snprintf(layer_name, MAX_LAYER_NAME_LEN, "Hidden layer %d",
							index);
				}
			} else {
				m = data->output_m;
				snprintf(layer_name, MAX_LAYER_NAME_LEN, "Output layer");
			}
			int posX, posY;
			if (decor->table_header)
				decor->table_header(decor->user_data, layer_name, m->size1);
			for (posX = 0; posX < m->size2; posX++) {
				if (decor->pre_column)
					decor->pre_column(decor->user_data);
				for (posY = 0; posY < m->size1; posY++) {
					if (decor->print_func)
						decor->print_func(decor->user_data,
								gsl_matrix_get(m, posY, posX));
				}
				if (decor->post_column)
					decor->post_column(decor->user_data);
			}
			if (decor->table_footer)
				decor->table_footer(decor->user_data);
		}
	}
}

int input_l;
int output_l;
int hidden_l;
int number_of_hidden;
double alpha;
gsl_matrix *input_m;
gsl_matrix *output_m;
gsl_matrix **hidden_m;
gsl_matrix **dropuot;
gsl_matrix **layers;
gsl_matrix **layers_delta;
gsl_matrix *temp;
gsl_matrix *train;
gsl_matrix *error;
int layers_numb;
activation activ;
activation *activ_l;

/*
 * Save format
 *
 *  [int input_l]\n
 *  [int output_l]\n
 *  [int hidden_l]\n
 *  [int number_of_hidden]\n
 *  [double alpha]\n
 *  [int activ]\n
 *  [ACTBEG]\n
 *  [int layer_number]:[int activ]\n
 *  [ACTEND]\n
 *  [HIDDEN_BEG]\n
 *  [int X]:[int Y]:[double value]\n
 *  [HIDDEN_END]
 *  [OUTPUT_BEG]\n
 *  [int X]:[int Y]:[double value]\n
 *  [OUTPUT_END]\n
 */

static char *bayrepo_reallocate_buffer(char *buffer, char *newdata, int *len,
		int newlen) {
	buffer = (char *) realloc(buffer, *len + newlen);
	if (!buffer) {
		return NULL;
	}
	memcpy(buffer + *len, newdata, newlen);
	*len = *len + newlen;
	return buffer;
}

int bayrepo_save_to_buffer(void *blob, char **buffer) {
	char buff[MAX_LAYER_NAME_LEN];
	int index;
	if (blob) {
		int len = 0;
		char *ptr = NULL;
		neuro_skeleton *data = (neuro_skeleton *) blob;
		snprintf(buff, MAX_LAYER_NAME_LEN, "%d\n", data->input_l);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		snprintf(buff, MAX_LAYER_NAME_LEN, "%d\n", data->output_l);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		snprintf(buff, MAX_LAYER_NAME_LEN, "%d\n", data->hidden_l);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		snprintf(buff, MAX_LAYER_NAME_LEN, "%d\n", data->number_of_hidden);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		snprintf(buff, MAX_LAYER_NAME_LEN, "%f\n", data->alpha);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		snprintf(buff, MAX_LAYER_NAME_LEN, "%d\n", (int) data->activ);
		ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len, strlen(buff));
		ptr = bayrepo_reallocate_buffer(ptr, (char *) "[ACTBEG]\n", &len,
				strlen("[ACTBEG]\n"));
		for (index = 0; index < (data->number_of_hidden + 1); index++) {
			snprintf(buff, MAX_LAYER_NAME_LEN, "%d:%d\n", index,
					(int) data->activ_l[index]);
			ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len,
					strlen(buff));
		}
		ptr = bayrepo_reallocate_buffer(ptr, (char *) "[ACTEND]\n", &len,
				strlen("[ACTEND]\n"));
		for (index = 0; index < data->number_of_hidden; index++) {
			ptr = bayrepo_reallocate_buffer(ptr, (char *) "[HIDDEN_BEG]\n",
					&len, strlen("[HIDDEN_BEG]\n"));
			int x, y;
			for (x = 0; x < data->hidden_m[index]->size1; x++) {
				for (y = 0; y < data->hidden_m[index]->size2; y++) {
					snprintf(buff, MAX_LAYER_NAME_LEN, "%d:%d:%f\n", x, y,
							gsl_matrix_get(data->hidden_m[index], x, y));
					ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len,
							strlen(buff));
				}
			}
			ptr = bayrepo_reallocate_buffer(ptr, (char *) "[HIDDEN_END]\n",
					&len, strlen("[HIDDEN_END]\n"));
		}
		ptr = bayrepo_reallocate_buffer(ptr, (char *) "[OUTPUT_BEG]\n", &len,
				strlen("[OUTPUT_BEG]\n"));
		int x, y;
		for (x = 0; x < data->output_m->size1; x++) {
			for (y = 0; y < data->output_m->size2; y++) {
				snprintf(buff, MAX_LAYER_NAME_LEN, "%d:%d:%f\n", x, y,
						gsl_matrix_get(data->output_m, x, y));
				ptr = bayrepo_reallocate_buffer(ptr, (char *) buff, &len,
						strlen(buff));
			}
		}
		ptr = bayrepo_reallocate_buffer(ptr, (char *) "[OUTPUT_END]\n", &len,
				strlen("[OUTPUT_END]\n"));
		*buffer = ptr;
		return len;
	}
	return 0;
}

void *bayrepo_restore_buffer(char *buffer, int buffer_len) {
	FILE *fp = fmemopen((void*) buffer, buffer_len, "r");
	int input_l = 0;
	int output_l = 0;
	int hidden_l = 0;
	int numb_of_hidden = 0;
	activation act = RELU;
	int act_beg = 0;
	double alpha = 0.0;
	void *blob = NULL;
	int is_hidden = 0;
	int hidden_index = 0;
	int is_out = 0;
	if (fp) {
		char result_buf[MAX_BUFFER_LEN];
		int counter = 0;

		while (!feof(fp)) {
			if (fgets(result_buf, MAX_BUFFER_LEN, fp)) {
				switch (counter) {
				case 0: {
					result_buf[strlen(result_buf) - 1] = 0;
					input_l = atoi(result_buf);
				}
					break;
				case 1: {
					result_buf[strlen(result_buf) - 1] = 0;
					output_l = atoi(result_buf);
				}
					break;
				case 2: {
					result_buf[strlen(result_buf) - 1] = 0;
					hidden_l = atoi(result_buf);
				}
					break;
				case 3: {
					result_buf[strlen(result_buf) - 1] = 0;
					numb_of_hidden = atoi(result_buf);
				}
					break;
				case 4: {
					char *tptr;
					result_buf[strlen(result_buf) - 1] = 0;
					alpha = strtod(result_buf, &tptr);
				}
					break;
				case 5: {
					result_buf[strlen(result_buf) - 1] = 0;
					act = (activation) atoi(result_buf);
					if (input_l > 0 && output_l > 0 && hidden_l >= 0
							&& numb_of_hidden >= 0 && alpha > 0.0) {
						blob = bayrepo_init_neuro(input_l, output_l, hidden_l,
								numb_of_hidden, alpha, act);
					}
				}
					break;
				default: {
					if (!blob)
						return NULL;
					if (strstr(result_buf, "[ACTBEG]")) {
						act_beg = 1;
					} else if (strstr(result_buf, "[ACTEND]")) {
						act_beg = 0;
					} else if (act_beg) {
						result_buf[strlen(result_buf) - 1] = 0;
						char *ptr = strchr(result_buf, ':');
						if (ptr) {
							*ptr = 0;
							ptr++;
							int ly = atoi(result_buf);
							int ac = atoi(ptr);
							bayrepo_set_layer_activ(blob, ly, (activation) ac);
						}
					} else if (strstr(result_buf, "[HIDDEN_BEG]")) {
						is_hidden = 1;
					} else if (strstr(result_buf, "[HIDDEN_END]")) {
						hidden_index++;
						is_hidden = 0;
					} else if (is_hidden) {
						result_buf[strlen(result_buf) - 1] = 0;
						char *ptr1 = strchr(result_buf, ':');
						if (ptr1) {
							*ptr1 = 0;
							ptr1++;
							char *ptr2 = strchr(ptr1, ':');
							if (ptr2) {
								*ptr2 = 0;
								ptr2++;
								char *tptr = NULL;
								int x = atoi(result_buf);
								int y = atoi(ptr1);
								double vl = strtod(ptr2, &tptr);
								bayrepo_fill_hidden(blob, hidden_index, x, y,
										vl);
							}
						}
					} else if (strstr(result_buf, "[OUTPUT_BEG]")) {
						is_out = 1;
					} else if (strstr(result_buf, "[OUTPUT_END]")) {
						hidden_index++;
						is_out = 0;
					} else if (is_out) {
						result_buf[strlen(result_buf) - 1] = 0;
						char *ptr1 = strchr(result_buf, ':');
						if (ptr1) {
							*ptr1 = 0;
							ptr1++;
							char *ptr2 = strchr(ptr1, ':');
							if (ptr2) {
								*ptr2 = 0;
								ptr2++;
								char *tptr = NULL;
								int x = atoi(result_buf);
								int y = atoi(ptr1);
								double vl = strtod(ptr2, &tptr);
								bayrepo_fill_outm(blob, x, y, vl);
							}
						}
					}
				}
				}
			}
			counter++;
		}
		return blob;

	}
	return NULL;
}


/*
 * Copyright 2020 Makani Technologies LLC
 *
 * 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.
 */

// The GSL extra library provides linear algebra functions using a
// similar interface to the rest of the GSL library.

#ifndef COMMON_C_MATH_GSL_LINALG_EXTRA_H_
#define COMMON_C_MATH_GSL_LINALG_EXTRA_H_

#include <stdint.h>

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

#ifdef __cplusplus
extern "C" {
#endif

int32_t GslTriangularSolve(CBLAS_SIDE_t side, CBLAS_UPLO_t uplo,
                           CBLAS_TRANSPOSE_t transpose, const gsl_matrix *T,
                           const gsl_matrix *B, gsl_matrix *X);
void GslTrapezoidalToTriangular(const gsl_matrix *R, gsl_matrix *T,
                                gsl_vector *tau);
void GslTrapezoidalToTriangularZTMat(const gsl_matrix *T, const gsl_vector *tau,
                                     const gsl_matrix *X, gsl_matrix *Zt_X);
int32_t GslMatrixDivide(CBLAS_SIDE_t side, const gsl_matrix *A,
                        const gsl_matrix *B, gsl_matrix *X);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif  // COMMON_C_MATH_GSL_LINALG_EXTRA_H_


/* rng/ranlux.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>

/* This is a lagged fibonacci generator with skipping developed by Luescher.
   The sequence is a series of 24-bit integers, x_n,

   x_n = d_n + b_n

   where d_n = x_{n-10} - x_{n-24} - c_{n-1}, b_n = 0 if d_n >= 0 and
   b_n = 2^24 if d_n < 0, c_n = 0 if d_n >= 0 and c_n = 1 if d_n < 0,
   where after 24 samples a group of p integers are "skipped", to
   reduce correlations. By default p = 199, but can be increased to
   365.

   The period of the generator is around 10^171.

   From: M. Luescher, "A portable high-quality random number generator
   for lattice field theory calculations", Computer Physics
   Communications, 79 (1994) 100-110.

   Available on the net as hep-lat/9309020 at http://xxx.lanl.gov/

   See also,

   F. James, "RANLUX: A Fortran implementation of the high-quality
   pseudo-random number generator of Luscher", Computer Physics
   Communications, 79 (1994) 111-114

   Kenneth G. Hamilton, F. James, "Acceleration of RANLUX", Computer
   Physics Communications, 101 (1997) 241-248

   Kenneth G. Hamilton, "Assembler RANLUX for PCs", Computer Physics
   Communications, 101 (1997) 249-253  */

static inline unsigned long int ranlux_get (void *vstate);
static double ranlux_get_double (void *vstate);
static void ranlux_set_lux (void *state, unsigned long int s, unsigned int luxury);
static void ranlux_set (void *state, unsigned long int s);
static void ranlux389_set (void *state, unsigned long int s);

static const unsigned long int mask_lo = 0x00ffffffUL;	/* 2^24 - 1 */
static const unsigned long int mask_hi = ~0x00ffffffUL;
static const unsigned long int two24 = 16777216;	/* 2^24 */

typedef struct
  {
    unsigned int i;
    unsigned int j;
    unsigned int n;
    unsigned int skip;
    unsigned int carry;
    unsigned long int u[24];
  }
ranlux_state_t;

static inline unsigned long int increment_state (ranlux_state_t * state);

static inline unsigned long int
increment_state (ranlux_state_t * state)
{
  unsigned int i = state->i;
  unsigned int j = state->j;
  long int delta = state->u[j] - state->u[i] - state->carry;

  if (delta & mask_hi)
    {
      state->carry = 1;
      delta &= mask_lo;
    }
  else
    {
      state->carry = 0;
    }

  state->u[i] = delta;

  if (i == 0)
    {
      i = 23;
    }
  else
    {
      i--;
    }

  state->i = i;

  if (j == 0)
    {
      j = 23;
    }
  else
    {
      j--;
    }

  state->j = j;

  return delta;
}

static inline unsigned long int
ranlux_get (void *vstate)
{
  ranlux_state_t *state = (ranlux_state_t *) vstate;
  const unsigned int skip = state->skip;
  unsigned long int r = increment_state (state);

  state->n++;

  if (state->n == 24)
    {
      unsigned int i;
      state->n = 0;
      for (i = 0; i < skip; i++)
	increment_state (state);
    }

  return r;
}

static double
ranlux_get_double (void *vstate)
{
  return ranlux_get (vstate) / 16777216.0;
}

static void
ranlux_set_lux (void *vstate, unsigned long int s, unsigned int luxury)
{
  ranlux_state_t *state = (ranlux_state_t *) vstate;
  int i;

  long int seed;

  if (s == 0)
    s = 314159265;	/* default seed is 314159265 */

  seed = s;

  /* This is the initialization algorithm of F. James, widely in use
     for RANLUX. */

  for (i = 0; i < 24; i++)
    {
      unsigned long int k = seed / 53668;
      seed = 40014 * (seed - k * 53668) - k * 12211;
      if (seed < 0)
	{
	  seed += 2147483563;
	}
      state->u[i] = seed % two24;
    }

  state->i = 23;
  state->j = 9;
  state->n = 0;
  state->skip = luxury - 24;

  if (state->u[23] & mask_hi)
    {
      state->carry = 1;
    }
  else
    {
      state->carry = 0;
    }
}

static void
ranlux_set (void *vstate, unsigned long int s)
{
  ranlux_set_lux (vstate, s, 223);
}

static void
ranlux389_set (void *vstate, unsigned long int s)
{
  ranlux_set_lux (vstate, s, 389);
}

static const gsl_rng_type ranlux_type =
{"ranlux",			/* name */
 0x00ffffffUL,			/* RAND_MAX */
 0,				/* RAND_MIN */
 sizeof (ranlux_state_t),
 &ranlux_set,
 &ranlux_get,
 &ranlux_get_double};

static const gsl_rng_type ranlux389_type =
{"ranlux389",			/* name */
 0x00ffffffUL,			/* RAND_MAX */
 0,				/* RAND_MIN */
 sizeof (ranlux_state_t),
 &ranlux389_set,
 &ranlux_get,
 &ranlux_get_double};

const gsl_rng_type *gsl_rng_ranlux = &ranlux_type;
const gsl_rng_type *gsl_rng_ranlux389 = &ranlux389_type;


/* blas/gsl_blas.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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.
 */

/*
 * Author:  G. Jungman
 */
#ifndef __GSL_BLAS_H__
#define __GSL_BLAS_H__

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

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

#include <gsl/gsl_blas_types.h>

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

__BEGIN_DECLS

/* ========================================================================
 * Level 1
 * ========================================================================
 */

GSL_FUN int gsl_blas_sdsdot (float alpha,
                     const gsl_vector_float * X,
                     const gsl_vector_float * Y,
                     float * result
                     );

GSL_FUN int gsl_blas_dsdot (const gsl_vector_float * X,
                    const gsl_vector_float * Y,
                    double * result
                    );

GSL_FUN int gsl_blas_sdot (const gsl_vector_float * X,
                   const gsl_vector_float * Y,
                   float * result
                   );

GSL_FUN int gsl_blas_ddot (const gsl_vector * X,
                   const gsl_vector * Y,
                   double * result
                   );

GSL_FUN int  gsl_blas_cdotu (const gsl_vector_complex_float * X,
                     const gsl_vector_complex_float * Y,
                     gsl_complex_float * dotu);

GSL_FUN int  gsl_blas_cdotc (const gsl_vector_complex_float * X,
                     const gsl_vector_complex_float * Y,
                     gsl_complex_float * dotc);

GSL_FUN int  gsl_blas_zdotu (const gsl_vector_complex * X,
                     const gsl_vector_complex * Y,
                     gsl_complex * dotu);

GSL_FUN int  gsl_blas_zdotc (const gsl_vector_complex * X,
                     const gsl_vector_complex * Y,
                     gsl_complex * dotc);

GSL_FUN float  gsl_blas_snrm2  (const gsl_vector_float * X);
GSL_FUN float  gsl_blas_sasum  (const gsl_vector_float * X);
GSL_FUN double gsl_blas_dnrm2  (const gsl_vector * X);
GSL_FUN double gsl_blas_dasum  (const gsl_vector * X);
GSL_FUN float  gsl_blas_scnrm2 (const gsl_vector_complex_float * X);
GSL_FUN float  gsl_blas_scasum (const gsl_vector_complex_float * X);
GSL_FUN double gsl_blas_dznrm2 (const gsl_vector_complex * X);
GSL_FUN double gsl_blas_dzasum (const gsl_vector_complex * X);

GSL_FUN CBLAS_INDEX_t gsl_blas_isamax (const gsl_vector_float * X);
GSL_FUN CBLAS_INDEX_t gsl_blas_idamax (const gsl_vector * X);
GSL_FUN CBLAS_INDEX_t gsl_blas_icamax (const gsl_vector_complex_float * X);
GSL_FUN CBLAS_INDEX_t gsl_blas_izamax (const gsl_vector_complex * X);

GSL_FUN int  gsl_blas_sswap (gsl_vector_float * X,
                     gsl_vector_float * Y);

GSL_FUN int  gsl_blas_scopy (const gsl_vector_float * X,
                     gsl_vector_float * Y);

GSL_FUN int  gsl_blas_saxpy (float alpha,
                     const gsl_vector_float * X,
                     gsl_vector_float * Y);

GSL_FUN int  gsl_blas_dswap (gsl_vector * X,
                     gsl_vector * Y);

GSL_FUN int  gsl_blas_dcopy (const gsl_vector * X,
                     gsl_vector * Y);

GSL_FUN int  gsl_blas_daxpy (double alpha,
                     const gsl_vector * X,
                     gsl_vector * Y);

GSL_FUN int  gsl_blas_cswap (gsl_vector_complex_float * X,
                     gsl_vector_complex_float * Y);

GSL_FUN int  gsl_blas_ccopy (const gsl_vector_complex_float * X,
                     gsl_vector_complex_float * Y);

GSL_FUN int  gsl_blas_caxpy (const gsl_complex_float alpha,
                     const gsl_vector_complex_float * X,
                     gsl_vector_complex_float * Y);

GSL_FUN int  gsl_blas_zswap (gsl_vector_complex * X,
                     gsl_vector_complex * Y);

GSL_FUN int  gsl_blas_zcopy (const gsl_vector_complex * X,
                     gsl_vector_complex * Y);

GSL_FUN int  gsl_blas_zaxpy (const gsl_complex alpha,
                     const gsl_vector_complex * X,
                     gsl_vector_complex * Y);

GSL_FUN int  gsl_blas_srotg (float a[], float b[], float c[], float s[]);

GSL_FUN int  gsl_blas_srotmg (float d1[], float d2[], float b1[], float b2, float P[]);

GSL_FUN int  gsl_blas_srot (gsl_vector_float * X,
                    gsl_vector_float * Y,
                    float c, float s);

GSL_FUN int  gsl_blas_srotm (gsl_vector_float * X,
                     gsl_vector_float * Y,
                     const float P[]);

GSL_FUN int  gsl_blas_drotg (double a[], double b[], double c[], double s[]);

GSL_FUN int  gsl_blas_drotmg (double d1[], double d2[], double b1[],
                      double b2, double P[]);

GSL_FUN int  gsl_blas_drot (gsl_vector * X,
                    gsl_vector * Y,
                    const double c, const double s);

GSL_FUN int  gsl_blas_drotm (gsl_vector * X,
                     gsl_vector * Y,
                     const double P[]);

GSL_FUN void gsl_blas_sscal  (float  alpha, gsl_vector_float * X);
GSL_FUN void gsl_blas_dscal  (double alpha, gsl_vector * X);
GSL_FUN void gsl_blas_cscal  (const gsl_complex_float alpha, gsl_vector_complex_float * X);
GSL_FUN void gsl_blas_zscal  (const gsl_complex alpha, gsl_vector_complex * X);
GSL_FUN void gsl_blas_csscal (float  alpha, gsl_vector_complex_float * X);
GSL_FUN void gsl_blas_zdscal (double alpha, gsl_vector_complex * X);

/* ===========================================================================
 * Level 2
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
GSL_FUN int  gsl_blas_sgemv (CBLAS_TRANSPOSE_t TransA,
                     float alpha,
                     const gsl_matrix_float * A,
                     const gsl_vector_float * X,
                     float beta,
                     gsl_vector_float * Y);

GSL_FUN int  gsl_blas_strmv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_float * A,
                     gsl_vector_float * X);

GSL_FUN int  gsl_blas_strsv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_float * A,
                     gsl_vector_float * X);

GSL_FUN int  gsl_blas_dgemv (CBLAS_TRANSPOSE_t TransA,
                     double alpha,
                     const gsl_matrix * A,
                     const gsl_vector * X,
                     double beta,
                     gsl_vector * Y);

GSL_FUN int  gsl_blas_dtrmv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix * A,
                     gsl_vector * X);

GSL_FUN int  gsl_blas_dtrsv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix * A,
                     gsl_vector * X);

GSL_FUN int  gsl_blas_cgemv (CBLAS_TRANSPOSE_t TransA,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_vector_complex_float * X,
                     const gsl_complex_float beta,
                     gsl_vector_complex_float * Y);

GSL_FUN int  gsl_blas_ctrmv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_complex_float * A,
                     gsl_vector_complex_float * X);

GSL_FUN int  gsl_blas_ctrsv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_complex_float * A,
                     gsl_vector_complex_float * X);

GSL_FUN int  gsl_blas_zgemv (CBLAS_TRANSPOSE_t TransA,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_vector_complex * X,
                     const gsl_complex beta,
                     gsl_vector_complex * Y);

GSL_FUN int  gsl_blas_ztrmv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_complex * A,
                     gsl_vector_complex * X);

GSL_FUN int  gsl_blas_ztrsv (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                     const gsl_matrix_complex * A,
                     gsl_vector_complex *X);

/*
 * Routines with S and D prefixes only
 */
GSL_FUN int  gsl_blas_ssymv (CBLAS_UPLO_t Uplo,
                     float alpha,
                     const gsl_matrix_float * A,
                     const gsl_vector_float * X,
                     float beta,
                     gsl_vector_float * Y);

GSL_FUN int  gsl_blas_sger (float alpha,
                    const gsl_vector_float * X,
                    const gsl_vector_float * Y,
                    gsl_matrix_float * A);

GSL_FUN int  gsl_blas_ssyr (CBLAS_UPLO_t Uplo,
                    float alpha,
                    const gsl_vector_float * X,
                    gsl_matrix_float * A);

GSL_FUN int  gsl_blas_ssyr2 (CBLAS_UPLO_t Uplo,
                     float alpha,
                     const gsl_vector_float * X,
                     const gsl_vector_float * Y,
                     gsl_matrix_float * A);

GSL_FUN int  gsl_blas_dsymv (CBLAS_UPLO_t Uplo,
                     double alpha,
                     const gsl_matrix * A,
                     const gsl_vector * X,
                     double beta,
                     gsl_vector * Y);
GSL_FUN int  gsl_blas_dger (double alpha,
                    const gsl_vector * X,
                    const gsl_vector * Y,
                    gsl_matrix * A);

GSL_FUN int  gsl_blas_dsyr (CBLAS_UPLO_t Uplo,
                    double alpha,
                    const gsl_vector * X,
                    gsl_matrix * A);

GSL_FUN int  gsl_blas_dsyr2 (CBLAS_UPLO_t Uplo,
                     double alpha,
                     const gsl_vector * X,
                     const gsl_vector * Y,
                     gsl_matrix * A);

/*
 * Routines with C and Z prefixes only
 */

GSL_FUN int  gsl_blas_chemv (CBLAS_UPLO_t Uplo,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_vector_complex_float * X,
                     const gsl_complex_float beta,
                     gsl_vector_complex_float * Y);

GSL_FUN int  gsl_blas_cgeru (const gsl_complex_float alpha,
                     const gsl_vector_complex_float * X,
                     const gsl_vector_complex_float * Y,
                     gsl_matrix_complex_float * A);

GSL_FUN int  gsl_blas_cgerc (const gsl_complex_float alpha,
                     const gsl_vector_complex_float * X,
                     const gsl_vector_complex_float * Y,
                     gsl_matrix_complex_float * A);

GSL_FUN int  gsl_blas_cher (CBLAS_UPLO_t Uplo,
                    float alpha,
                    const gsl_vector_complex_float * X,
                    gsl_matrix_complex_float * A);

GSL_FUN int  gsl_blas_cher2 (CBLAS_UPLO_t Uplo,
                     const gsl_complex_float alpha,
                     const gsl_vector_complex_float * X,
                     const gsl_vector_complex_float * Y,
                     gsl_matrix_complex_float * A);

GSL_FUN int  gsl_blas_zhemv (CBLAS_UPLO_t Uplo,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_vector_complex * X,
                     const gsl_complex beta,
                     gsl_vector_complex * Y);

GSL_FUN int  gsl_blas_zgeru (const gsl_complex alpha,
                     const gsl_vector_complex * X,
                     const gsl_vector_complex * Y,
                     gsl_matrix_complex * A);

GSL_FUN int  gsl_blas_zgerc (const gsl_complex alpha,
                     const gsl_vector_complex * X,
                     const gsl_vector_complex * Y,
                     gsl_matrix_complex * A);

GSL_FUN int  gsl_blas_zher (CBLAS_UPLO_t Uplo,
                    double alpha,
                    const gsl_vector_complex * X,
                    gsl_matrix_complex * A);

GSL_FUN int  gsl_blas_zher2 (CBLAS_UPLO_t Uplo,
                     const gsl_complex alpha,
                     const gsl_vector_complex * X,
                     const gsl_vector_complex * Y,
                     gsl_matrix_complex * A);

/*
 * ===========================================================================
 * Prototypes for level 3 BLAS
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
GSL_FUN int  gsl_blas_sgemm (CBLAS_TRANSPOSE_t TransA,
                     CBLAS_TRANSPOSE_t TransB,
                     float alpha,
                     const gsl_matrix_float * A,
                     const gsl_matrix_float * B,
                     float beta,
                     gsl_matrix_float * C);

GSL_FUN int  gsl_blas_ssymm (CBLAS_SIDE_t Side, CBLAS_UPLO_t Uplo,
                     float alpha,
                     const gsl_matrix_float * A,
                     const gsl_matrix_float * B,
                     float beta,
                     gsl_matrix_float * C);

GSL_FUN int  gsl_blas_ssyrk (CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t Trans,
                     float alpha,
                     const gsl_matrix_float * A,
                     float beta,
                     gsl_matrix_float * C);

GSL_FUN int  gsl_blas_ssyr2k (CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t Trans,
                      float alpha,
                      const gsl_matrix_float * A,
                      const gsl_matrix_float * B,
                      float beta,
                      gsl_matrix_float * C);

GSL_FUN int  gsl_blas_strmm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     float alpha,
                     const gsl_matrix_float * A,
                     gsl_matrix_float * B);

GSL_FUN int  gsl_blas_strsm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     float alpha,
                     const gsl_matrix_float * A,
                     gsl_matrix_float * B);

GSL_FUN int  gsl_blas_dgemm (CBLAS_TRANSPOSE_t TransA,
                     CBLAS_TRANSPOSE_t TransB,
                     double alpha,
                     const gsl_matrix * A,
                     const gsl_matrix * B,
                     double beta,
                     gsl_matrix * C);

GSL_FUN int  gsl_blas_dsymm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo,
                     double alpha,
                     const gsl_matrix * A,
                     const gsl_matrix * B,
                     double beta,
                     gsl_matrix * C);

GSL_FUN int  gsl_blas_dsyrk (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t Trans,
                     double alpha,
                     const gsl_matrix * A,
                     double beta,
                     gsl_matrix * C);

GSL_FUN int  gsl_blas_dsyr2k (CBLAS_UPLO_t Uplo,
                      CBLAS_TRANSPOSE_t Trans,
                      double alpha,
                      const  gsl_matrix * A,
                      const  gsl_matrix * B,
                      double beta,
                      gsl_matrix * C);

GSL_FUN int  gsl_blas_dtrmm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     double alpha,
                     const gsl_matrix * A,
                     gsl_matrix * B);

GSL_FUN int  gsl_blas_dtrsm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     double alpha,
                     const gsl_matrix * A,
                     gsl_matrix * B);

GSL_FUN int  gsl_blas_cgemm (CBLAS_TRANSPOSE_t TransA,
                     CBLAS_TRANSPOSE_t TransB,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_matrix_complex_float * B,
                     const gsl_complex_float beta,
                     gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_csymm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_matrix_complex_float * B,
                     const gsl_complex_float beta,
                     gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_csyrk (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t Trans,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_complex_float beta,
                     gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_csyr2k (CBLAS_UPLO_t Uplo,
                      CBLAS_TRANSPOSE_t Trans,
                      const gsl_complex_float alpha,
                      const gsl_matrix_complex_float * A,
                      const gsl_matrix_complex_float * B,
                      const gsl_complex_float beta,
                      gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_ctrmm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     gsl_matrix_complex_float * B);

GSL_FUN int  gsl_blas_ctrsm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     gsl_matrix_complex_float * B);

GSL_FUN int  gsl_blas_zgemm (CBLAS_TRANSPOSE_t TransA,
                     CBLAS_TRANSPOSE_t TransB,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_matrix_complex * B,
                     const gsl_complex beta,
                     gsl_matrix_complex * C);

GSL_FUN int  gsl_blas_zsymm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_matrix_complex * B,
                     const gsl_complex beta,
                     gsl_matrix_complex * C);

GSL_FUN int  gsl_blas_zsyrk (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t Trans,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_complex beta,
                     gsl_matrix_complex * C);

GSL_FUN int  gsl_blas_zsyr2k (CBLAS_UPLO_t Uplo,
                      CBLAS_TRANSPOSE_t Trans,
                      const gsl_complex alpha,
                      const gsl_matrix_complex * A,
                      const gsl_matrix_complex * B,
                      const gsl_complex beta,
                      gsl_matrix_complex *C);

GSL_FUN int  gsl_blas_ztrmm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     gsl_matrix_complex * B);

GSL_FUN int  gsl_blas_ztrsm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                     CBLAS_DIAG_t Diag,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     gsl_matrix_complex * B);

/*
 * Routines with prefixes C and Z only
 */
GSL_FUN int  gsl_blas_chemm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo,
                     const gsl_complex_float alpha,
                     const gsl_matrix_complex_float * A,
                     const gsl_matrix_complex_float * B,
                     const gsl_complex_float beta,
                     gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_cherk (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t Trans,
                     float alpha,
                     const gsl_matrix_complex_float * A,
                     float beta,
                     gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_cher2k (CBLAS_UPLO_t Uplo,
                      CBLAS_TRANSPOSE_t Trans,
                      const gsl_complex_float alpha,
                      const gsl_matrix_complex_float * A,
                      const gsl_matrix_complex_float * B,
                      float beta,
                      gsl_matrix_complex_float * C);

GSL_FUN int  gsl_blas_zhemm (CBLAS_SIDE_t Side,
                     CBLAS_UPLO_t Uplo,
                     const gsl_complex alpha,
                     const gsl_matrix_complex * A,
                     const gsl_matrix_complex * B,
                     const gsl_complex beta,
                     gsl_matrix_complex * C);

GSL_FUN int  gsl_blas_zherk (CBLAS_UPLO_t Uplo,
                     CBLAS_TRANSPOSE_t Trans,
                     double alpha,
                     const gsl_matrix_complex * A,
                     double beta,
                     gsl_matrix_complex * C);

GSL_FUN int  gsl_blas_zher2k (CBLAS_UPLO_t Uplo,
                      CBLAS_TRANSPOSE_t Trans,
                      const gsl_complex alpha,
                      const gsl_matrix_complex * A,
                      const gsl_matrix_complex * B,
                      double beta,
                      gsl_matrix_complex * C);

__END_DECLS

#endif /* __GSL_BLAS_H__ */


/*
** libproj -- library of cartographic projections
**
** Copyright (c) 2003, 2006   Gerald I. Evenden
*/
static const char
LIBPROJ_ID[] = "Id";
/*
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define N_TOL 1e-6
#define PROJ_LIB__
#ifdef PROJ_HAVE_GSL
#include <gsl/gsl_integration.h>
#define GSL_WORK 1000
#define PROJ_PARMS__ \
  double n[2]; \
  gsl_function func; \
  gsl_integration_workspace *work; \
  int mode;
#include  <lib_proj.h>
  double
mayr_kernel(double x, void * n) {
  return (pow(cos(x), *(double*)n));
}
#else
#define PROJ_PARMS__ \
  int mode;
#include  <lib_proj.h>
#endif
PROJ_HEAD(mayr, "Mayr (Tobler Meridian  Geometric Mean)") "\n\tPCyl., Sph., NoInv.";
/* definition of segment bounds for <1e-7 error (unit sphere)  */
#define SEG1 1.4
#define SEG2 1.55
#define SEG3 1.57
#define BASE1 1.151132004484049
#define BASE2 1.196140916241303
#define BASE3 1.19812525384759
#define HALFN 4
/* Gauss Legendre weights/abscissa */
double X[] = {
    0.96028985649753618,
    0.79666647741362673,
    0.52553240991632899,
    0.18343464249564981 };
double W[] = {
    0.10122853629037638,
    0.22238103445337443,
    0.31370664587788744,
    0.36268378337836199 };
/* Mayr kernel */
#define func(v) sqrt(cos(v))
  static
double gauss_legendre(double x0, double x1) {
  int i = 0;
  double s = 0., *x = X,  *w = W, xsize, xmean, arg;

  xmean = 0.5 * (x1 + x0);
  xsize = 0.5 * (x1 - x0);
  while ( i++ < HALFN ) {
    arg = xsize * *x++;
    s += *w++ * (func(xmean - arg) + func(xmean + arg));
  };
  return xsize * s;
}
  static
double integrate(double val) {
  double out;

  if (val <= SEG1)
    out = gauss_legendre(0., val);
  else if (val <= SEG2)
    out = BASE1 + gauss_legendre(SEG1, val);
  else if (val <= SEG3)
    out = BASE2 + gauss_legendre(SEG2, val);
  else
    out = BASE3 + gauss_legendre(SEG3, val);
  return out;
}
FORWARD(s_forward); /* spheroid */
  (void) P; /* avoid warning */

  xy.x = lp.lam * func(lp.phi);
  xy.y = integrate(fabs(lp.phi));
  if (lp.phi < 0.)
    xy.y = -xy.y;
  return (xy);
}
#ifdef PROJ_HAVE_GSL
FORWARD(s_forwardg); /* numerical integration n <> 0.5 */
  double error;

  gsl_integration_qags(&P->func, 0., fabs(lp.phi), 1e-7, 1e-8, GSL_WORK,
    P->work, &xy.y, &error);
  xy.x = lp.lam * pow(cos(lp.phi), P->n[1]);
  if (lp.phi < 0.)
    xy.y = -xy.y;
  return (xy);
}
#endif
FREEUP;
  if (P) {
#if PROJ_HAVE_GSL
    if (P->mode)
      gsl_integration_workspace_free(P->work);
#endif
    free(P);
  }
}
ENTRY0(mayr)
  P->es = 0;
  if (proj_param(P->params, "tn").i) {
#if PROJ_HAVE_GSL
    P->n[0] = proj_param(P->params, "dn").f;
    if ((P->n[0] < N_TOL) || (P->n[0] > 1.-N_TOL))
      E_ERROR(-40)
    P->fwd = s_forwardg;
    P->n[1] = 1. - P->n[0];
    P->func.function = &mayr_kernel;
    P->func.params = P->n;
    P->work = gsl_integration_workspace_alloc (GSL_WORK);
    P->mode = 1;
#else
    E_ERROR(-47)
#endif
  } else {
    P->mode = 0;
    P->fwd = s_forward;
  }
ENDENTRY(P)
/*
** Log: proj_mayr.c
** Revision 3.2  2006/01/19 01:52:59  gie
** correct some casting ^)*^&^
**
** Revision 3.1  2006/01/11 01:38:18  gie
** Initial
**
*/


/* gmres.c
 *
 * Copyright (C) 2014 Patrick Alken
 *
 * 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.
 */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_spmatrix.h>
#include <gsl/gsl_spblas.h>
#include <gsl/gsl_splinalg.h>

/*
 * The code in this module is based on the Householder GMRES
 * algorithm described in
 *
 * [1] H. F. Walker, Implementation of the GMRES method using
 *     Householder transformations, SIAM J. Sci. Stat. Comput.
 *     9(1), 1988.
 *
 * [2] Y. Saad, Iterative methods for sparse linear systems,
 *     2nd edition, SIAM, 2003.
 */

typedef struct
{
  size_t n;        /* size of linear system */
  size_t m;        /* dimension of Krylov subspace K_m */
  gsl_vector *r;   /* residual vector r = b - A*x */
  gsl_matrix *H;   /* Hessenberg matrix n-by-(m+1) */
  gsl_vector *tau; /* householder scalars */
  gsl_vector *y;   /* least squares rhs and solution vector */

  double *c;       /* Givens rotations */
  double *s;

  double normr;    /* residual norm ||r|| */
} gmres_state_t;

static void gmres_free(void *vstate);
static int gmres_iterate(const gsl_spmatrix *A, const gsl_vector *b,
                         const double tol, gsl_vector *x, void *vstate);

/*
gmres_alloc()
  Allocate a GMRES workspace for solving an n-by-n system A x = b

Inputs: n        - size of system
        krylov_m - size of Krylov subspace (ie: number of inner iterations)
                   if this parameter is 0, the value GSL_MIN(n,10) is
                   used

Return: pointer to workspace
*/

static void *
gmres_alloc(const size_t n, const size_t m)
{
  gmres_state_t *state;

  if (n == 0)
    {
      GSL_ERROR_NULL("matrix dimension n must be a positive integer",
                     GSL_EINVAL);
    }

  state = calloc(1, sizeof(gmres_state_t));
  if (!state)
    {
      GSL_ERROR_NULL("failed to allocate gmres state", GSL_ENOMEM);
    }

  state->n = n;

  /* compute size of Krylov subspace */
  if (m == 0)
    state->m = GSL_MIN(n, 10);
  else
    state->m = GSL_MIN(n, m);

  state->r = gsl_vector_alloc(n);
  if (!state->r)
    {
      gmres_free(state);
      GSL_ERROR_NULL("failed to allocate r vector", GSL_ENOMEM);
    }

  state->H = gsl_matrix_alloc(n, state->m + 1);
  if (!state->H)
    {
      gmres_free(state);
      GSL_ERROR_NULL("failed to allocate H matrix", GSL_ENOMEM);
    }

  state->tau = gsl_vector_alloc(state->m + 1);
  if (!state->tau)
    {
      gmres_free(state);
      GSL_ERROR_NULL("failed to allocate tau vector", GSL_ENOMEM);
    }

  state->y = gsl_vector_alloc(state->m + 1);
  if (!state->y)
    {
      gmres_free(state);
      GSL_ERROR_NULL("failed to allocate y vector", GSL_ENOMEM);
    }

  state->c = malloc(state->m * sizeof(double));
  state->s = malloc(state->m * sizeof(double));
  if (!state->c || !state->s)
    {
      gmres_free(state);
      GSL_ERROR_NULL("failed to allocate Givens vectors", GSL_ENOMEM);
    }

  state->normr = 0.0;

  return state;
} /* gmres_alloc() */

static void
gmres_free(void *vstate)
{
  gmres_state_t *state = (gmres_state_t *) vstate;

  if (state->r)
    gsl_vector_free(state->r);

  if (state->H)
    gsl_matrix_free(state->H);

  if (state->tau)
    gsl_vector_free(state->tau);

  if (state->y)
    gsl_vector_free(state->y);

  if (state->c)
    free(state->c);

  if (state->s)
    free(state->s);

  free(state);
} /* gmres_free() */

/*
gmres_iterate()
  Solve A*x = b using GMRES algorithm

Inputs: A    - sparse square matrix
        b    - right hand side vector
        tol  - stopping tolerance (see below)
        x    - (input/output) on input, initial estimate x_0;
               on output, solution vector
        work - workspace

Return:
GSL_SUCCESS if converged to solution (solution stored in x). In
this case the following will be true:

||b - A*x|| <= tol * ||b||

GSL_CONTINUE if not yet converged; in this case x contains the
most recent solution vector and calling this function more times
with the input x could result in convergence (ie: restarted GMRES)

Notes:
1) Based on algorithm 2.2 of (Walker, 1998 [1]) and algorithm 6.10 of
(Saad, 2003 [2])

2) On output, work->normr contains ||b - A*x||
*/

static int
gmres_iterate(const gsl_spmatrix *A, const gsl_vector *b,
              const double tol, gsl_vector *x,
              void *vstate)
{
  const size_t N = A->size1;
  gmres_state_t *state = (gmres_state_t *) vstate;

  if (N != A->size2)
    {
      GSL_ERROR("matrix must be square", GSL_ENOTSQR);
    }
  else if (N != b->size)
    {
      GSL_ERROR("matrix does not match right hand side", GSL_EBADLEN);
    }
  else if (N != x->size)
    {
      GSL_ERROR("matrix does not match solution vector", GSL_EBADLEN);
    }
  else if (N != state->n)
    {
      GSL_ERROR("matrix does not match workspace", GSL_EBADLEN);
    }
  else
    {
      int status = GSL_SUCCESS;
      const size_t maxit = state->m;
      const double normb = gsl_blas_dnrm2(b); /* ||b|| */
      const double reltol = tol * normb;      /* tol*||b|| */
      double normr;                           /* ||r|| */
      size_t m, k;
      double tau;                             /* householder scalar */
      gsl_matrix *H = state->H;               /* Hessenberg matrix */
      gsl_vector *r = state->r;               /* residual vector */
      gsl_vector *w = state->y;               /* least squares RHS */
      gsl_matrix_view Rm;                     /* R_m = H(1:m,2:m+1) */
      gsl_vector_view ym;                     /* y(1:m) */
      gsl_vector_view h0 = gsl_matrix_column(H, 0);

      /*
       * The Hessenberg matrix will have the following structure:
       *
       * H = [ ||r_0|| | v_1 v_2 ... v_m     ]
       *     [   u_1   | u_2 u_3 ... u_{m+1} ]
       *
       * where v_j are the orthonormal vectors spanning the Krylov
       * subpsace of length j + 1 and u_{j+1} are the householder
       * vectors of length n - j - 1.
       * In fact, u_{j+1} has length n - j since u_{j+1}[0] = 1,
       * but this 1 is not stored.
       */
      gsl_matrix_set_zero(H);

      /* Step 1a: compute r = b - A*x_0 */
      gsl_vector_memcpy(r, b);
      gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, r);

      /* Step 1b */
      gsl_vector_memcpy(&h0.vector, r);
      tau = gsl_linalg_householder_transform(&h0.vector);

      /* store tau_1 */
      gsl_vector_set(state->tau, 0, tau);

      /* initialize w (stored in state->y) */
      gsl_vector_set_zero(w);
      gsl_vector_set(w, 0, gsl_vector_get(&h0.vector, 0));

      for (m = 1; m <= maxit; ++m)
        {
          size_t j = m - 1; /* C indexing */
          double c, s;      /* Givens rotation */

          /* v_m */
          gsl_vector_view vm = gsl_matrix_column(H, m);

          /* v_m(m:end) */
          gsl_vector_view vv = gsl_vector_subvector(&vm.vector, j, N - j);

          /* householder vector u_m for projection P_m */
          gsl_vector_view um = gsl_matrix_subcolumn(H, j, j, N - j);

          /* Step 2a: form v_m = P_m e_m = e_m - tau_m w_m */
          gsl_vector_set_zero(&vm.vector);
          gsl_vector_memcpy(&vv.vector, &um.vector);
          tau = gsl_vector_get(state->tau, j); /* tau_m */
          gsl_vector_scale(&vv.vector, -tau);
          gsl_vector_set(&vv.vector, 0, 1.0 - tau);

          /* Step 2a: v_m <- P_1 P_2 ... P_{m-1} v_m */
          for (k = j; k > 0 && k--; )
            {
              gsl_vector_view uk =
                gsl_matrix_subcolumn(H, k, k, N - k);
              gsl_vector_view vk =
                gsl_vector_subvector(&vm.vector, k, N - k);
              tau = gsl_vector_get(state->tau, k);
              gsl_linalg_householder_hv(tau, &uk.vector, &vk.vector);
            }

          /* Step 2a: v_m <- A*v_m */
          gsl_spblas_dgemv(CblasNoTrans, 1.0, A, &vm.vector, 0.0, r);
          gsl_vector_memcpy(&vm.vector, r);

          /* Step 2a: v_m <- P_m ... P_1 v_m */
          for (k = 0; k <= j; ++k)
            {
              gsl_vector_view uk = gsl_matrix_subcolumn(H, k, k, N - k);
              gsl_vector_view vk = gsl_vector_subvector(&vm.vector, k, N - k);
              tau = gsl_vector_get(state->tau, k);
              gsl_linalg_householder_hv(tau, &uk.vector, &vk.vector);
            }

          /* Steps 2c,2d: find P_{m+1} and set v_m <- P_{m+1} v_m */
          if (m < N)
            {
              /* householder vector u_{m+1} for projection P_{m+1} */
              gsl_vector_view ump1 = gsl_matrix_subcolumn(H, m, m, N - m);

              tau = gsl_linalg_householder_transform(&ump1.vector);
              gsl_vector_set(state->tau, j + 1, tau);
            }

          /* Step 2e: v_m <- J_{m-1} ... J_1 v_m */
          for (k = 0; k < j; ++k)
            {
              gsl_linalg_givens_gv(&vm.vector, k, k + 1,
                                   state->c[k], state->s[k]);
            }

          if (m < N)
            {
              /* Step 2g: find givens rotation J_m for v_m(m:m+1) */
              gsl_linalg_givens(gsl_vector_get(&vm.vector, j),
                                gsl_vector_get(&vm.vector, j + 1),
                                &c, &s);

              /* store givens rotation for later use */
              state->c[j] = c;
              state->s[j] = s;

              /* Step 2h: v_m <- J_m v_m */
              gsl_linalg_givens_gv(&vm.vector, j, j + 1, c, s);

              /* Step 2h: w <- J_m w */
              gsl_linalg_givens_gv(w, j, j + 1, c, s);
            }

          /*
           * Step 2i: R_m = [ R_{m-1}, v_m ] - already taken care
           * of due to our memory storage scheme
           */

          /* Step 2j: check residual w_{m+1} for convergence */
          normr = fabs(gsl_vector_get(w, j + 1));
          if (normr <= reltol)
            {
              /*
               * method has converged, break out of loop to compute
               * update to solution vector x
               */
              break;
            }
        }

      /*
       * At this point, we have either converged to a solution or
       * completed all maxit iterations. In either case, compute
       * an update to the solution vector x and test again for
       * convergence.
       */

      /* rewind m if we exceeded maxit iterations */
      if (m > maxit)
        m--;

      /* Step 3a: solve triangular system R_m y_m = w, in place */
      Rm = gsl_matrix_submatrix(H, 0, 1, m, m);
      ym = gsl_vector_subvector(w, 0, m);
      gsl_blas_dtrsv(CblasUpper, CblasNoTrans, CblasNonUnit,
                     &Rm.matrix, &ym.vector);

      /*
       * Step 3b: update solution vector x; the loop below
       * uses a different but equivalent formulation from
       * Saad, algorithm 6.10, step 14; store Krylov projection
       * V_m y_m in 'r'
       */
      gsl_vector_set_zero(r);
      for (k = m; k > 0 && k--; )
        {
          double ymk = gsl_vector_get(&ym.vector, k);
          gsl_vector_view uk = gsl_matrix_subcolumn(H, k, k, N - k);
          gsl_vector_view rk = gsl_vector_subvector(r, k, N - k);

          /* r <- n_k e_k + r */
          gsl_vector_set(r, k, gsl_vector_get(r, k) + ymk);

          /* r <- P_k r */
          tau = gsl_vector_get(state->tau, k);
          gsl_linalg_householder_hv(tau, &uk.vector, &rk.vector);
        }

      /* x <- x + V_m y_m */
      gsl_vector_add(x, r);

      /* compute new residual r = b - A*x */
      gsl_vector_memcpy(r, b);
      gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, r);
      normr = gsl_blas_dnrm2(r);

      if (normr <= reltol)
        status = GSL_SUCCESS;  /* converged */
      else
        status = GSL_CONTINUE; /* not yet converged */

      /* store residual norm */
      state->normr = normr;

      return status;
    }
} /* gmres_iterate() */

static double
gmres_normr(const void *vstate)
{
  const gmres_state_t *state = (const gmres_state_t *) vstate;
  return state->normr;
} /* gmres_normr() */

static const gsl_splinalg_itersolve_type gmres_type =
{
  "gmres",
  &gmres_alloc,
  &gmres_iterate,
  &gmres_normr,
  &gmres_free
};

const gsl_splinalg_itersolve_type * gsl_splinalg_itersolve_gmres =
  &gmres_type;


// xi2bandpow.c
// 31.08.2018
// converts xi_+/-, xi_g+/x, and xi_gg to bandpowers

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_sf_bessel.h>
#include "bjutils.h"

#define xi_pm_balance 0.5 // default weight for xi_+/- in P_E

void xi_to_derived2pt();
double K_bandpow_ee();
double K_bandpow_ne();
double K_bandpow_nn();
double apod_upper();
double apod_lower();

int main(int argc, char *argv[])
{
  int i,j;
  double dr,xip,xim,scale_terms,kernel;
  FILE *dat;
  char name[400],path[200],ident[200],outident[200];

  if (argc!=14) {
    printf("Syntax: %s\n1: <working directory>\n2: <input file identifier>\n3: <output file identifier>\n4: <number of input angular bins>\n5: <min input separation to use in conversion [arcmin] (xi_+ in case of ee)>\n6: <max input separation to use in conversion [arcmin] (xi_+ in case of ee)>\n7: <min input separation to use in conversion [arcmin] (xi_- in case of ee; otherwise unused)>\n8: <max input separation to use in conversion [arcmin] (xi_- in case of ee; otherwise unused)>\n9: <number of output angular bins>\n10: <min output angular frequency>\n11: <max output angular frequency>\n12: <correlation type (1: ee; 2: ne; 3: gg)>\n13: <log width of apodisation window [total width of apodised range is tmax/tmin=exp(width) in arcmin; <0 for no apodisation]>\n\n",argv[0]);
    exit(-1);
  }
  sprintf(path,"%s",argv[1]);
  sprintf(ident,"%s",argv[2]);
  sprintf(outident,"%s",argv[3]);
  const int NR=atoi(argv[4]);
  const double angmin1=atof(argv[5]);
  const double angmax1=atof(argv[6]);
  const double angmin2=atof(argv[7]);
  const double angmax2=atof(argv[8]);
  const int NOUT=atoi(argv[9]);
  const double ellmin=atof(argv[10]);
  const double ellmax=atof(argv[11]);
  const int corrtype=atoi(argv[12]);
  const double logwidth=atof(argv[13]);

  // allocations
  if (corrtype>3) {
    printf("Error: incorrect input for correlation type!\n");
    exit(-1);
  }

  double *tin=bj_alloc(NR);
  double *ell_centre=bj_alloc(NOUT);
  double *ell_bound=bj_alloc(NOUT+1);
  double *weight=bj_alloc(NR);
  double *weight_xim=bj_alloc(NR);
  double *pfrac=bj_alloc(NOUT);

  gsl_vector *xi=gsl_vector_calloc(2*NR);
  gsl_vector *derived2pt=gsl_vector_calloc(2*NOUT);
  gsl_matrix *trans=gsl_matrix_calloc(2*NOUT,2*NR);
  gsl_matrix *xierr=gsl_matrix_calloc(2*NR,2*NR);
  gsl_matrix *derived2pterr=gsl_matrix_calloc(2*NOUT,2*NOUT);

  double logbinwidth=log(ellmax/ellmin)/(1.*NOUT);  // output binning
  for (i=0;i<NOUT;i++) {
    ell_centre[i]=ellmin*exp((i+0.5)*logbinwidth);   // use log centre of bin
    ell_bound[i]=ellmin*exp(i*logbinwidth);
  }
  ell_bound[NOUT]=ellmin*exp(NOUT*logbinwidth);

  // read input data
  sprintf(name,"%s/xi2bandpow_input_%s.dat",path,ident);
  dat=bj_fileopen("r",name);
  for (i=0;i<NR;i++) {
    if (corrtype==3) {
      fscanf(dat,"%lf  %lf",&tin[i],&xip);  //theta (bin centres; in arcmin), w_gg
      xim=0.0;   // use only first half of vector
    }
    else {
      fscanf(dat,"%lf  %lf  %lf",&tin[i],&xip,&xim);  //theta (bin centres), xi_+, xi_- for ee; theta (bin centres), xi_g+, xi_gx for ne
    }

    gsl_vector_set(xi,i,xip);
    gsl_vector_set(xi,NR+i,xim);
    gsl_matrix_set(xierr,i,i,xip*xip);       // treat xi as noise, uncorrelated so diagonal
    gsl_matrix_set(xierr,NR+i,NR+i,xim*xim);

    if (logwidth<0) {   // no apodisation
      if ((tin[i]>angmin1)&&(tin[i]<angmax1)) {
	weight[i]=1.0;
      }
      else {
	weight[i]=0.0;
      }
    }
    else {
      weight[i]=apod_lower(tin[i],angmin1,logwidth)*apod_upper(tin[i],angmax1,logwidth);
    }

    if (corrtype==1) { //ee
      if (logwidth<0) {   // no apodisation
	if ((tin[i]>angmin2)&&(tin[i]<angmax2)) {
	  weight_xim[i]=1.0;
	}
	else {
	  weight_xim[i]=0.0;
	}
      }
      else {
	weight_xim[i]=apod_lower(tin[i],angmin2,logwidth)*apod_upper(tin[i],angmax2,logwidth);
      }
    }

    tin[i]*=PI/10800.;   // convert from arcmin to radian
  }  // end of i loop
  fclose(dat);

  if ((tin[NR-1]<angmax1*PI/10800.*exp(logwidth/2.))||(corrtype==1 && tin[NR-1]<angmax2*PI/10800.*exp(logwidth/2.))) {
    printf("Error: correlation function not available out to max. range requested: %g < %g ; %g\n",tin[NR-1]/PI*10800.,angmax1*exp(logwidth/2.),angmax2*PI/10800.*exp(logwidth/2.));
    exit(-1);
  }
  if ((tin[0]>angmin1*PI/10800.*exp(-logwidth/2.))||(corrtype==1 && tin[0]>angmin2*PI/10800.*exp(-logwidth/2.))) {
    printf("Error: correlation function not available out to min. range requested: %g > %g ; %g\n",tin[0]/PI*10800.,angmin1*exp(-logwidth/2.),angmin2*PI/10800.*exp(-logwidth/2.));
    exit(-1);
  }

  sprintf(name,"%s/xi2bandpow_pmweights_%s.dat",path,ident);
  if ((dat=fopen(name,"r"))==NULL) {
    printf("No file %s - use pmweight of %g instead.\n",name,xi_pm_balance);
    for (i=0;i<NOUT;i++) {
      pfrac[i]=xi_pm_balance;
    }
  }
  else {
    printf("Using pmweights from file %s\n",name);
    for (i=0;i<NOUT;i++) {
      fscanf(dat,"%lf",&pfrac[i]);  // xi_+/- weight in P_E; one value per output ell bin
      printf("K_+ for Bin %i: %g\n",i+1,pfrac[i]);
    }
    fclose(dat);
  }

  // construct transformation matrix
  logbinwidth=log(tin[NR-1]/tin[0])/(1.*NR-1.);
  for (j=0;j<NR;j++) {
    dr=tin[j]*(exp(0.5*logbinwidth)-exp(-0.5*logbinwidth));  // input bin width
    for (i=0;i<NOUT;i++) {
      scale_terms=2.*PI*dr/(tin[j]*log(ell_bound[i+1]/ell_bound[i]));

      kernel=0.0;
      if (corrtype==1) {
	kernel=K_bandpow_ee(tin[j]*ell_bound[i+1],0)-K_bandpow_ee(tin[j]*ell_bound[i],0);
	gsl_matrix_set(trans,i,j,pfrac[i]*weight[j]*kernel*scale_terms);
	gsl_matrix_set(trans,NOUT+i,j,0.5*weight[j]*kernel*scale_terms);

	kernel=K_bandpow_ee(tin[j]*ell_bound[i+1],1)-K_bandpow_ee(tin[j]*ell_bound[i],1);
	gsl_matrix_set(trans,i,NR+j,(1.-pfrac[i])*weight_xim[j]*kernel*scale_terms);
	gsl_matrix_set(trans,NOUT+i,NR+j,-0.5*weight_xim[j]*kernel*scale_terms);
      }
      if (corrtype==2) {
	kernel=K_bandpow_ne(tin[j]*ell_bound[i+1])-K_bandpow_ne(tin[j]*ell_bound[i]);
	gsl_matrix_set(trans,i,j,weight[j]*kernel*scale_terms);
	gsl_matrix_set(trans,NOUT+i,NR+j,weight[j]*kernel*scale_terms);
      }
      if (corrtype==3) {
	kernel=K_bandpow_nn(tin[j]*ell_bound[i+1])-K_bandpow_nn(tin[j]*ell_bound[i]);
	gsl_matrix_set(trans,i,j,weight[j]*kernel*scale_terms);
      }
    }
  }

  // convert signals
  xi_to_derived2pt(xi,xierr,trans,derived2pt,derived2pterr,2*NR,2*NOUT);

  // output band powers
  sprintf(name,"%s/xi2bandpow_output_%s.dat",path,outident);
  dat=bj_fileopen("w",name);
  fprintf(dat,"#   ell         PEE             PEEerr          PBB                PBBerr\n");
  for (i=0;i<NOUT;i++) {
    if (corrtype==3) {
      fprintf(dat,"%15.10g\t%15.10g\t%15.10g\n",ell_centre[i],gsl_vector_get(derived2pt,i),sqrt(gsl_matrix_get(derived2pterr,i,i)));   // ell, bandpow, err
    }
    else {
      fprintf(dat,"%15.10g\t%15.10g\t%15.10g\t%15.10g\t%15.10g\n",ell_centre[i],gsl_vector_get(derived2pt,i),sqrt(gsl_matrix_get(derived2pterr,i,i)),gsl_vector_get(derived2pt,NOUT+i),sqrt(gsl_matrix_get(derived2pterr,NOUT+i,NOUT+i)));   // ell, E/+, err, B/x, err
    }
  }
  fclose(dat);

  sprintf(name,"%s/xi2bandpow_kernels_%s.dat",path,outident);  // write kernels
  dat=bj_fileopen("w",name);
  for (j=0;j<NR;j++) {
    fprintf(dat,"%15.10g\t",tin[j]/PI*10800.);
    for (i=0;i<NOUT;i++) {
      fprintf(dat,"%15.10g\t",gsl_matrix_get(trans,i,j));   // E-mode, xi_+
    }
    for (i=0;i<NOUT;i++) {
      fprintf(dat,"%15.10g\t",gsl_matrix_get(trans,i,NR+j));   // E-mode, xi_-
    }
    for (i=0;i<NOUT;i++) {
      fprintf(dat,"%15.10g\t",gsl_matrix_get(trans,NOUT+i,j));   // B-mode, xi_+
    }
    for (i=0;i<NOUT;i++) {
      fprintf(dat,"%15.10g\t",gsl_matrix_get(trans,NOUT+i,NR+j));   // B-mode, xi_-
    }
    fprintf(dat,"\n");
  }
  fclose(dat);

  // clean up
  gsl_vector_free(xi);
  gsl_vector_free(derived2pt);
  gsl_matrix_free(trans);
  gsl_matrix_free(xierr);
  gsl_matrix_free(derived2pterr);
  free(tin);
  free(ell_centre);
  free(ell_bound);
  free(weight);
  free(weight_xim);
  free(pfrac);
  return 0;
}

void xi_to_derived2pt(gsl_vector *xi,gsl_matrix *xierr,gsl_matrix *trans,gsl_vector *res,gsl_matrix *reserr,int nin,int nout)
{
  gsl_matrix *tmp=gsl_matrix_calloc(nout,nin);
  gsl_matrix *transt=gsl_matrix_calloc(nin,nout);

  // signal conversion
  mattimesvec_gen(trans,xi,res,nout,nin);

  // error conversion
  mattimesmat_gen(trans,xierr,tmp,nout,nin,nin);
  matrix_transpose(trans,transt,nout,nin);
  mattimesmat_gen(tmp,transt,reserr,nout,nin,nout);

  gsl_matrix_free(tmp);
  gsl_matrix_free(transt);
  return;
}

double K_bandpow_ee(double x,int pm)
{
  double res=0.0;

  if (pm==0) {
    res=x*gsl_sf_bessel_Jn(1,x);
  }
  else if (pm==1) {
    res=(x-8./x)*gsl_sf_bessel_Jn(1,x)-8.*gsl_sf_bessel_Jn(2,x);
  }
  else {
    printf("Error in routine K_bandpow_ee: invalid option for 'pm'.\n");
    exit(-1);
  }

  return(res);
}

double K_bandpow_ne(double x)
{
  return((-1.)*x*gsl_sf_bessel_Jn(1,x)-2.*gsl_sf_bessel_Jn(0,x));
}

double K_bandpow_nn(double x)
{
  return(x*gsl_sf_bessel_Jn(1,x));
}

double apod_upper(double x,double scale,double logwidth)
{
  double res=0.0;
  double logx=log(x);
  double logxmin=log(scale)-logwidth/2.;
  double logxmax=log(scale)+logwidth/2.;

  if (logx<=logxmin) {
    res=1.;
  }
  else if (logx>logxmax) {
    res=0.;
  }
  else {
    res=cos(PI/2.*(logx-logxmin)/(logxmax-logxmin));
    res=res*res;  // cos^2
  }
  return(res);
}

double apod_lower(double x,double scale,double logwidth)
{
  double res=0.0;
  double logx=log(x);
  double logxmin=log(scale)-logwidth/2.;
  double logxmax=log(scale)+logwidth/2.;

  if (logx<=logxmin) {
    res=0.;
  }
  else if (logx>logxmax) {
    res=1.;
  }
  else {
    res=cos(PI/2.*(logx-logxmax)/(logxmax-logxmin));
    res=res*res;  // cos^2
  }
  return(res);
}


/* specfunc/bessel_K1.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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.
 */

/* Author:  G. Jungman */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_exp.h>
#include <gsl/gsl_sf_bessel.h>

#include "error.h"

#include "chebyshev.h"
#include "cheb_eval.c"

/*-*-*-*-*-*-*-*-*-*-*-* Private Section *-*-*-*-*-*-*-*-*-*-*-*/

/* based on SLATEC besk1(), besk1e() */

/* chebyshev expansions

 series for bk1        on the interval  0.          to  4.00000d+00
                                        with weighted error   7.02e-18
                                         log weighted error  17.15
                               significant figures required  16.73
                                    decimal places required  17.67

 series for ak1        on the interval  1.25000d-01 to  5.00000d-01
                                        with weighted error   6.06e-17
                                         log weighted error  16.22
                               significant figures required  15.41
                                    decimal places required  16.83

 series for ak12       on the interval  0.          to  1.25000d-01
                                        with weighted error   2.58e-17
                                         log weighted error  16.59
                               significant figures required  15.22
                                    decimal places required  17.16
*/

static double bk1_data[11] = {
   0.0253002273389477705,
  -0.3531559607765448760,
  -0.1226111808226571480,
  -0.0069757238596398643,
  -0.0001730288957513052,
  -0.0000024334061415659,
  -0.0000000221338763073,
  -0.0000000001411488392,
  -0.0000000000006666901,
  -0.0000000000000024274,
  -0.0000000000000000070
};

static cheb_series bk1_cs = {
  bk1_data,
  10,
  -1, 1,
  8
};

static double ak1_data[17] = {
   0.27443134069738830,
   0.07571989953199368,
  -0.00144105155647540,
   0.00006650116955125,
  -0.00000436998470952,
   0.00000035402774997,
  -0.00000003311163779,
   0.00000000344597758,
  -0.00000000038989323,
   0.00000000004720819,
  -0.00000000000604783,
   0.00000000000081284,
  -0.00000000000011386,
   0.00000000000001654,
  -0.00000000000000248,
   0.00000000000000038,
  -0.00000000000000006
};
static cheb_series ak1_cs = {
  ak1_data,
  16,
  -1, 1,
  9
};

static double ak12_data[14] = {
   0.06379308343739001,
   0.02832887813049721,
  -0.00024753706739052,
   0.00000577197245160,
  -0.00000020689392195,
   0.00000000973998344,
  -0.00000000055853361,
   0.00000000003732996,
  -0.00000000000282505,
   0.00000000000023720,
  -0.00000000000002176,
   0.00000000000000215,
  -0.00000000000000022,
   0.00000000000000002
};
static cheb_series ak12_cs = {
  ak12_data,
  13,
  -1, 1,
  7
};

/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/

int gsl_sf_bessel_K1_scaled_e(const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(x <= 0.0) {
    DOMAIN_ERROR(result);
  }
  else if(x < 2.0*GSL_DBL_MIN) {
    OVERFLOW_ERROR(result);
  }
  else if(x <= 2.0) {
    const double lx = log(x);
    const double ex = exp(x);
    int stat_I1;
    gsl_sf_result I1;
    gsl_sf_result c;
    cheb_eval_e(&bk1_cs, 0.5*x*x-1.0, &c);
    stat_I1 = gsl_sf_bessel_I1_e(x, &I1);
    result->val  = ex * ((lx-M_LN2)*I1.val + (0.75 + c.val)/x);
    result->err  = ex * (c.err/x + fabs(lx)*I1.err);
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return stat_I1;
  }
  else if(x <= 8.0) {
    const double sx = sqrt(x);
    gsl_sf_result c;
    cheb_eval_e(&ak1_cs, (16.0/x-5.0)/3.0, &c);
    result->val  = (1.25 + c.val) / sx;
    result->err  = c.err / sx;
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return GSL_SUCCESS;
  }
  else {
    const double sx = sqrt(x);
    gsl_sf_result c;
    cheb_eval_e(&ak12_cs, 16.0/x-1.0, &c);
    result->val  = (1.25 + c.val) / sx;
    result->err  = c.err / sx;
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return GSL_SUCCESS;
  }
}

int gsl_sf_bessel_K1_e(const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(x <= 0.0) {
    DOMAIN_ERROR(result);
  }
  else if(x < 2.0*GSL_DBL_MIN) {
    OVERFLOW_ERROR(result);
  }
  else if(x <= 2.0) {
    const double lx = log(x);
    int stat_I1;
    gsl_sf_result I1;
    gsl_sf_result c;
    cheb_eval_e(&bk1_cs, 0.5*x*x-1.0, &c);
    stat_I1 = gsl_sf_bessel_I1_e(x, &I1);
    result->val  = (lx-M_LN2)*I1.val + (0.75 + c.val)/x;
    result->err  = c.err/x + fabs(lx)*I1.err;
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return stat_I1;
  }
  else {
    gsl_sf_result K1_scaled;
    int stat_K1 = gsl_sf_bessel_K1_scaled_e(x, &K1_scaled);
    int stat_e  = gsl_sf_exp_mult_err_e(-x, 0.0,
                                           K1_scaled.val, K1_scaled.err,
                                           result);
    result->err = fabs(result->val) * (GSL_DBL_EPSILON*fabs(x) + K1_scaled.err/K1_scaled.val);
    return GSL_ERROR_SELECT_2(stat_e, stat_K1);
  }
}

/*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/

#include "eval.h"

double gsl_sf_bessel_K1_scaled(const double x)
{
  EVAL_RESULT(gsl_sf_bessel_K1_scaled_e(x, &result));
}

double gsl_sf_bessel_K1(const double x)
{
  EVAL_RESULT(gsl_sf_bessel_K1_e(x, &result));
}


/* multifit/fsolver.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 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.
 */

#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_multifit_nlin.h>

gsl_multifit_fsolver *
gsl_multifit_fsolver_alloc (const gsl_multifit_fsolver_type * T,
                            size_t n, size_t p)
{
  int status;

  gsl_multifit_fsolver * s;

  if (n < p)
    {
      GSL_ERROR_VAL ("insufficient data points, n < p", GSL_EINVAL, 0);
    }

  s = (gsl_multifit_fsolver *) malloc (sizeof (gsl_multifit_fsolver));

  if (s == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for multifit solver struct",
                        GSL_ENOMEM, 0);
    }

  s->x = gsl_vector_calloc (p);

  if (s->x == 0)
    {
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for x", GSL_ENOMEM, 0);
    }

  s->f = gsl_vector_calloc (n);

  if (s->f == 0)
    {
      gsl_vector_free (s->x);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for f", GSL_ENOMEM, 0);
    }

  s->dx = gsl_vector_calloc (p);

  if (s->dx == 0)
    {
      gsl_vector_free (s->x);
      gsl_vector_free (s->f);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for dx", GSL_ENOMEM, 0);
    }

  s->state = malloc (T->size);

  if (s->state == 0)
    {
      gsl_vector_free (s->dx);
      gsl_vector_free (s->x);
      gsl_vector_free (s->f);
      free (s);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for multifit solver state",
                        GSL_ENOMEM, 0);
    }

  s->type = T ;

  status = (s->type->alloc)(s->state, n, p);

  if (status != GSL_SUCCESS)
    {
      (s->type->free)(s->state);
      free (s->state);
      gsl_vector_free (s->dx);
      gsl_vector_free (s->x);
      gsl_vector_free (s->f);
      free (s);         /* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to set solver", status, 0);
    }

  s->function = NULL;

  return s;
}

int
gsl_multifit_fsolver_set (gsl_multifit_fsolver * s,
                          gsl_multifit_function * f,
                          const gsl_vector * x)
{
  if (s->f->size != f->n)
    {
      GSL_ERROR ("function size does not match solver", GSL_EBADLEN);
    }

  if (s->x->size != x->size)
    {
      GSL_ERROR ("vector length does not match solver", GSL_EBADLEN);
    }

  s->function = f;
  gsl_vector_memcpy(s->x,x);

  return (s->type->set) (s->state, s->function, s->x, s->f, s->dx);
}

int
gsl_multifit_fsolver_iterate (gsl_multifit_fsolver * s)
{
  return (s->type->iterate) (s->state, s->function, s->x, s->f, s->dx);
}

void
gsl_multifit_fsolver_free (gsl_multifit_fsolver * s)
{
  RETURN_IF_NULL (s);
  (s->type->free) (s->state);
  free (s->state);
  gsl_vector_free (s->dx);
  gsl_vector_free (s->x);
  gsl_vector_free (s->f);
  free (s);
}

const char *
gsl_multifit_fsolver_name (const gsl_multifit_fsolver * s)
{
  return s->type->name;
}

gsl_vector *
gsl_multifit_fsolver_position (const gsl_multifit_fsolver * s)
{
  return s->x;
}