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

/* solve_quadratic.c - finds the real roots of a x^2 + b x + c = 0 */

#include <config.h>
#include <math.h>

#include <gsl/gsl_poly.h>

int
gsl_poly_solve_quadratic (double a, double b, double c,
                          double *x0, double *x1)
{
  double disc = b * b - 4 * a * c;

  if (a == 0) /* Handle linear case */
    {
      if (b == 0)
        {
          return 0;
        }
      else
        {
          *x0 = -c / b;
          return 1;
        };
    }

  if (disc > 0)
    {
      if (b == 0)
        {
          double r = fabs (0.5 * sqrt (disc) / a);
          *x0 = -r;
          *x1 =  r;
        }
      else
        {
          double sgnb = (b > 0 ? 1 : -1);
          double temp = -0.5 * (b + sgnb * sqrt (disc));
          double r1 = temp / a ;
          double r2 = c / temp ;

          if (r1 < r2)
            {
              *x0 = r1 ;
              *x1 = r2 ;
            }
          else
            {
              *x0 = r2 ;
              *x1 = r1 ;
            }
        }
      return 2;
    }
  else if (disc == 0)
    {
      *x0 = -0.5 * b / a ;
      *x1 = -0.5 * b / a ;
      return 2 ;
    }
  else
    {
      return 0;
    }
}


#include <stdio.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_eigen.h>

int
main (void)
{
  double data[] = { 1.0  , 1/2.0, 1/3.0, 1/4.0,
                    1/2.0, 1/3.0, 1/4.0, 1/5.0,
                    1/3.0, 1/4.0, 1/5.0, 1/6.0,
                    1/4.0, 1/5.0, 1/6.0, 1/7.0 };

  gsl_matrix_view m
    = gsl_matrix_view_array (data, 4, 4);

  gsl_vector *eval = gsl_vector_alloc (4);
  gsl_matrix *evec = gsl_matrix_alloc (4, 4);

  gsl_eigen_symmv_workspace * w =
    gsl_eigen_symmv_alloc (4);

  gsl_eigen_symmv (&m.matrix, eval, evec, w);

  gsl_eigen_symmv_free (w);

  gsl_eigen_symmv_sort (eval, evec,
                        GSL_EIGEN_SORT_ABS_ASC);

  {
    int i;

    for (i = 0; i < 4; i++)
      {
        double eval_i
           = gsl_vector_get (eval, i);
        gsl_vector_view evec_i
           = gsl_matrix_column (evec, i);

        printf ("eigenvalue = %g\n", eval_i);
        printf ("eigenvector = \n");
        gsl_vector_fprintf (stdout,
                            &evec_i.vector, "%g");
      }
  }

  gsl_vector_free (eval);
  gsl_matrix_free (evec);

  return 0;
}


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "sphere.h"
#include <petsc.h>
#undef __FUNCT__
#define __FUNCT__ "CartesianToSpherical"
PetscErrorCode CartesianToSpherical(Vec xyz, Vec sph)
{
  PetscErrorCode ierr;
  PetscScalar *xyzPt, *sphPt;
  PetscInt size;
  PetscReal x, y, z, r, theta, phi;
  PetscFunctionBegin;
  //get size of xyz vec
  ierr = VecGetSize(xyz, &size);CHKERRQ(ierr);

  ierr = VecGetArrayRead(xyz, &xyzPt);CHKERRQ(ierr);
  ierr = VecGetArray(sph, &sphPt);CHKERRQ(ierr);
  for(PetscInt k=0; k<size/3; ++k) {
    //get xyz points
    x = xyzPt[3*k+0];
    y = xyzPt[3*k+1];
    z = xyzPt[3*k+2];
    //compute transformation
    r = sqrt(x*x + y*y + z*z);
    theta = acos(z/r);
    phi = atan2(y,x);
    //set values in sph vec
    sphPt[3*k+0] = r;
    sphPt[3*k+1] = 0;
    sphPt[3*k+2] = phi;
    if(r>0) sphPt[3*k+1] = theta;

  }
  ierr = VecRestoreArray(sph, &sphPt);CHKERRQ(ierr);
  ierr = VecRestoreArrayRead(xyz, &xyzPt);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}
void cartesianToSpherical(SPoint *point) {

  double r, theta, phi;

  r = sqrt((point->x1)*(point->x1) + (point->x2)*(point->x2) + (point->x3)*(point->x3));
  theta = acos(point->x3/r);
  phi = atan2((point->x2),(point->x1));

  point->x1 = r;
  point->x2 = 0;
  point->x3 = phi;

  if(r > 0) point->x2 = theta;

  point->type = 's';
}

void sphericalToCartesian(SPoint *point) {
  double r = point->x1;
  double theta = point->x2;
  double phi = point->x3;

  double x = r*sin(theta)*cos(phi);
  double y = r*sin(theta)*sin(phi);
  double z = r*cos(theta);

  point->x1 = x;
  point->x2 = y;
  point->x3 = z;
}


#include <stdio.h>
#include <gsl/gsl_fit.h>

int main (void)
{
  int n = 11;
  double x[11] = {10.0, 8.0, 13.0, 9.0,11.0,14.0,6.0,4.0 ,12.0,7.0,5.0};
  double y[11] = {  8.04,  6.95,7.68, 8.81, 8.33,9.96,7.24,4.26,10.84,4.82,5.68 };

  double c0, c1, cov00, cov01, cov11, sumsq;

  gsl_fit_linear (x, 1, y, 1, n,
                   &c0, &c1, &cov00, &cov01, &cov11,
                   &sumsq);

  printf ("best fit: Y = %g + %g X\n", c0, c1);
  printf ("covariance matrix:\n");
  printf ("[ %g, %g\n  %g, %g]\n",
          cov00, cov01, cov01, cov11);
  printf ("sumsq = %g\n", sumsq);

  printf ("\n");

  return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <gsl/gsl_linalg.h>

int DEBUG = 1;
int DEBUG_SOLUTION = 1;
int STORE_TIMING = 0;

FILE* decompFile;
FILE* solveFile;

double* generateVector(int size)
{
    double* vec = calloc(sizeof(double), size);
    for (int i = 0; i < size; ++i){
        double val = (double)rand() / RAND_MAX; // double [0, 1]
        vec[i] = val;
    }
    return vec;
}

void printMatrix(double* m, int size)
{
    for (int i = 0; i < size; ++i){
        for (int j = 0; j < size; ++j){
            printf("%g    ", m[j + i * size]);
        }
        printf("\n");
    }
    printf("\n");
}

void printVector(double* v, int size)
{
    for (int i = 0; i < size; ++i){
        printf("%g", v[i]);
        printf("\n");
    }
    printf("\n");
}

double* copyVector(double* src, int size)
{
    double* vec = calloc(sizeof(double), size);
    for (int i = 0; i < size; ++i){
        vec[i] = src[i];
    }
    return vec;
}

void checkSolution(double* m, double* b, gsl_vector x, int size)
{
    const double epsilon = 0.0001;
    int failures = 0;

    for (int i = 0; i < size; ++i){
        double sum = 0;
        for (int j = 0; j < size; ++j){
            sum += m[j + i * size] * x.data[j];
            // printf("%g * %g = %g\n", m[j + i * size], x.data[j], m[j + i * size] * x.data[j]);
        }
        if (abs(sum - b[i]) > epsilon){
            failures++;
            if (DEBUG_SOLUTION){
                printf("Wrong solution was calculated: %g --- %g\n", sum, b[i]);
                printf("\n");
            }
        }
    }
    if (DEBUG_SOLUTION){
        if (failures == 0){
            printf("All calculated values were correct");
        } else {
            printf("Total %n errors in calculated value", failures);
        }
        printf("\n");
    }
}

void decomp(gsl_matrix *A, gsl_permutation* p, int *signum, int size)
{
    clock_t start = clock();
    gsl_linalg_LU_decomp(A, p, signum);
    clock_t end = clock();

    double t = ((double) (end - start)) / CLOCKS_PER_SEC;
    if (STORE_TIMING) fprintf (decompFile,"%d %g\n", size, t);
}

void solve(const gsl_matrix* LU, const gsl_permutation* p, const gsl_vector* b, gsl_vector* x, int size)
{
    clock_t start = clock();
    gsl_linalg_LU_solve(LU, p, b, x);
    clock_t end = clock();

    double t = ((double) (end - start)) / CLOCKS_PER_SEC;
    if (STORE_TIMING) fprintf (solveFile,"%d %g\n", size, t);
}

void calculate(int n)
{
    double* a_data = generateVector(n * n);
    double* b_data = generateVector(n);

    double* originalMatrix = copyVector(a_data, n * n);

    gsl_matrix_view m = gsl_matrix_view_array(a_data, n, n);
    gsl_vector_view b = gsl_vector_view_array(b_data, n);

    if (DEBUG){
        printf("Matrix M:\n");
        printMatrix(a_data, n);

        printf("Vector B:\n");
        printVector(b_data, n);
    }
    gsl_vector *x = gsl_vector_alloc(n);
    int s;

    gsl_permutation *p = gsl_permutation_alloc(n);

    decomp(&m.matrix, p, &s, n);
    solve(&m.matrix, p, &b.vector, x, n);

    if (DEBUG){
        printf("Solution X:\n");
        gsl_vector_fprintf(stdout, x, "%g");
        printf("\n");
    }
    checkSolution(originalMatrix, b_data, *x, n);

    gsl_permutation_free(p);
    gsl_vector_free(x);
}

int main(int argc, char* argv[])
{
    if (argc != 2){
        return -1;
    }
    char *pEnd;
    int n = strtol(argv[1], &pEnd, 0);

    srand(time(NULL));
    decompFile = fopen("decomp.txt", "w");
    solveFile = fopen("solve.txt", "w");

    if (n == 0){
        return -1;
    } else if (n < 0){
        DEBUG = 0;
        DEBUG_SOLUTION = 0;
        STORE_TIMING = 1;
        for (int i = 10; i <= 1000; i+=10){
            calculate(i);
        }
    } else {
        calculate(n);
    }

    return 0;
}


#ifndef ROUNDING_H_ZL4GHC5J
#define ROUNDING_H_ZL4GHC5J

#include <cmath>
#include <gsl/gsl>
#include <type_traits>

namespace sens_loc::math {

/// Round the value \c val to the nth \c digit.
///
/// \tparam Number arithmetic type that represents a number
/// \param val number to be rounded
/// \param digit The decimal digit the number shall be rounded to. Positive
/// values indicate the position behind the comma for rational numbers, a
/// negative number indicates a position before the comma.
/// \pre \c std::is_arithmetic_v<Number>
/// \post the result is rounded to the nth digit
/// \post \f$digit == 0 => result == val\f$
/// \post \f$!std::isfinite(val) => result == val\f$
/// \code
/// roundn(10.055, 2) == 10.06;
/// roundn(10.055, 0) == 10.055;
/// roundn(10.055, -1) == 10.00;
/// roundn(1052.4, -2) == 1100.0;
/// roundn(1052, -2) == 1100;
/// roundn(1052, 2) == 1052;
/// \endcode
template <typename Number>
Number roundn(Number val, int digit) noexcept {
    static_assert(std::is_arithmetic_v<Number>);

    if (digit == 0)
        return val;

    if constexpr (std::is_floating_point_v<Number>) {
        if (!std::isfinite(val))
            return val;
        Number potence_10 = std::pow(Number(10), digit);
        return std::round(val * potence_10) / potence_10;
    } else {
        double potence_10 = std::pow(10., digit);
        double rounded    = std::round(val * potence_10) / potence_10;
        return Number(std::round(rounded));
    }
}

}  // namespace sens_loc::math

#endif /* end of include guard: ROUNDING_H_ZL4GHC5J */


#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlinear.h>

/* number of data points to fit */
#define N 40

struct data {
  size_t n;
  double * y;
};

int
expb_f (const gsl_vector * x, void *data,
        gsl_vector * f)
{
  size_t n = ((struct data *)data)->n;
  double *y = ((struct data *)data)->y;

  double A = gsl_vector_get (x, 0);
  double lambda = gsl_vector_get (x, 1);
  double b = gsl_vector_get (x, 2);

  size_t i;

  for (i = 0; i < n; i++)
    {
      /* Model Yi = A * exp(-lambda * i) + b */
      double t = i;
      double Yi = A * exp (-lambda * t) + b;
      gsl_vector_set (f, i, Yi - y[i]);
    }

  return GSL_SUCCESS;
}

int
expb_df (const gsl_vector * x, void *data,
         gsl_matrix * J)
{
  size_t n = ((struct data *)data)->n;

  double A = gsl_vector_get (x, 0);
  double lambda = gsl_vector_get (x, 1);

  size_t i;

  for (i = 0; i < n; i++)
    {
      /* Jacobian matrix J(i,j) = dfi / dxj, */
      /* where fi = (Yi - yi)/sigma[i],      */
      /*       Yi = A * exp(-lambda * i) + b  */
      /* and the xj are the parameters (A,lambda,b) */
      double t = i;
      double e = exp(-lambda * t);
      gsl_matrix_set (J, i, 0, e);
      gsl_matrix_set (J, i, 1, -t * A * e);
      gsl_matrix_set (J, i, 2, 1.0);
    }
  return GSL_SUCCESS;
}

void
callback(const size_t iter, void *params,
         const gsl_multifit_nlinear_workspace *w)
{
  gsl_vector *f = gsl_multifit_nlinear_residual(w);
  gsl_vector *x = gsl_multifit_nlinear_position(w);
  double rcond;

  /* compute reciprocal condition number of J(x) */
  gsl_multifit_nlinear_rcond(&rcond, w);

  fprintf(stderr, "iter %2zu: A = %.4f, lambda = %.4f, b = %.4f, cond(J) = %8.4f, |f(x)| = %.4f\n",
          iter,
          gsl_vector_get(x, 0),
          gsl_vector_get(x, 1),
          gsl_vector_get(x, 2),
          1.0 / rcond,
          gsl_blas_dnrm2(f));
}

int
main (void)
{
  const gsl_multifit_nlinear_type *T = gsl_multifit_nlinear_trust;
  gsl_multifit_nlinear_workspace *w;
  gsl_multifit_nlinear_fdf fdf;
  gsl_multifit_nlinear_parameters fdf_params =
    gsl_multifit_nlinear_default_parameters();
  const size_t n = N;
  const size_t p = 3;

  gsl_vector *f;
  gsl_matrix *J;
  gsl_matrix *covar = gsl_matrix_alloc (p, p);
  double y[N], weights[N];
  struct data d = { n, y };
  double x_init[3] = { 1.0, 1.0, 0.0 }; /* starting values */
  gsl_vector_view x = gsl_vector_view_array (x_init, p);
  gsl_vector_view wts = gsl_vector_view_array(weights, n);
  gsl_rng * r;
  double chisq, chisq0;
  int status, info;
  size_t i;

  const double xtol = 1e-8;
  const double gtol = 1e-8;
  const double ftol = 0.0;

  gsl_rng_env_setup();
  r = gsl_rng_alloc(gsl_rng_default);

  /* define the function to be minimized */
  fdf.f = expb_f;
  fdf.df = expb_df;   /* set to NULL for finite-difference Jacobian */
  fdf.fvv = NULL;     /* not using geodesic acceleration */
  fdf.n = n;
  fdf.p = p;
  fdf.params = &d;

  /* this is the data to be fitted */
  for (i = 0; i < n; i++)
    {
      double t = i;
      double yi = 1.0 + 5 * exp (-0.1 * t);
      double si = 0.1 * yi;
      double dy = gsl_ran_gaussian(r, si);

      weights[i] = 1.0 / (si * si);
      y[i] = yi + dy;
      printf ("data: %zu %g %g\n", i, y[i], si);
    };

  /* allocate workspace with default parameters */
  w = gsl_multifit_nlinear_alloc (T, &fdf_params, n, p);

  /* initialize solver with starting point and weights */
  gsl_multifit_nlinear_winit (&x.vector, &wts.vector, &fdf, w);

  /* compute initial cost function */
  f = gsl_multifit_nlinear_residual(w);
  gsl_blas_ddot(f, f, &chisq0);

  /* solve the system with a maximum of 20 iterations */
  status = gsl_multifit_nlinear_driver(20, xtol, gtol, ftol,
                                       callback, NULL, &info, w);

  /* compute covariance of best fit parameters */
  J = gsl_multifit_nlinear_jac(w);
  gsl_multifit_nlinear_covar (J, 0.0, covar);

  /* compute final cost */
  gsl_blas_ddot(f, f, &chisq);

#define FIT(i) gsl_vector_get(w->x, i)
#define ERR(i) sqrt(gsl_matrix_get(covar,i,i))

  fprintf(stderr, "summary from method '%s/%s'\n",
          gsl_multifit_nlinear_name(w),
          gsl_multifit_nlinear_trs_name(w));
  fprintf(stderr, "number of iterations: %zu\n",
          gsl_multifit_nlinear_niter(w));
  fprintf(stderr, "function evaluations: %zu\n", fdf.nevalf);
  fprintf(stderr, "Jacobian evaluations: %zu\n", fdf.nevaldf);
  fprintf(stderr, "reason for stopping: %s\n",
          (info == 1) ? "small step size" : "small gradient");
  fprintf(stderr, "initial |f(x)| = %f\n", sqrt(chisq0));
  fprintf(stderr, "final   |f(x)| = %f\n", sqrt(chisq));

  {
    double dof = n - p;
    double c = GSL_MAX_DBL(1, sqrt(chisq / dof));

    fprintf(stderr, "chisq/dof = %g\n", chisq / dof);

    fprintf (stderr, "A      = %.5f +/- %.5f\n", FIT(0), c*ERR(0));
    fprintf (stderr, "lambda = %.5f +/- %.5f\n", FIT(1), c*ERR(1));
    fprintf (stderr, "b      = %.5f +/- %.5f\n", FIT(2), c*ERR(2));
  }

  fprintf (stderr, "status = %s\n", gsl_strerror (status));

  gsl_multifit_nlinear_free (w);
  gsl_matrix_free (covar);
  gsl_rng_free (r);

  return 0;
}


#include <stdio.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <time.h>

/*
  gsl_matrix_printf prints a matrix as a column vector.  This function
  prints a matrix in block form.
*/
void pretty_print(const gsl_matrix * M)
{
  // Get the dimension of the matrix.
  int rows = M->size1;
  int cols = M->size2;
  // Now print out the data in a square format.
  for(int i = 0; i < rows; i++){
    for(int j = 0; j < cols; j++){
      printf("%f ", gsl_matrix_get(M, i, j));
    }
    printf("\n");
  }
  printf("\n");
}

int run_svd(const gsl_matrix * a) {
  // Need to transpose the input
  gsl_matrix *a_transpose = gsl_matrix_alloc(a->size2, a->size1);
  gsl_matrix_transpose_memcpy(a_transpose, a);
  printf("a_matrix'\n");
  pretty_print(a_transpose);

  int m = a->size1;
  gsl_matrix * V = gsl_matrix_alloc(m, m);
  gsl_vector * S = gsl_vector_alloc(m);
  gsl_vector * work = gsl_vector_alloc(m);

  gsl_linalg_SV_decomp(a_transpose, V, S, work);
  printf("U\n");
  pretty_print(V); printf("\n");
  printf("V\n");
  pretty_print(a_transpose); printf("\n");
  printf("S\n");
  gsl_vector_fprintf(stdout, S, "%g");

  gsl_matrix_free(V);
  gsl_vector_free(S);
  gsl_vector_free(work);

  return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h> // high level interface to blas
#include <gsl/gsl_linalg.h> // lapack

void print_matrix(gsl_matrix* m, int nx, int ny);

int main (void) {
	int i, j;
	int signum = 0;
	double determinant_m = 0.0;
	gsl_permutation  *m_LU_permutation = gsl_permutation_alloc(8);
	gsl_matrix *m_test = gsl_matrix_alloc(8, 8);
	gsl_matrix *m_original = gsl_matrix_alloc(8,8);
	gsl_matrix *m_inverted = gsl_matrix_alloc(8,8);

	/*for (i = 0; i < 8; i++){
		for(j = 0; j < 8; j++){
			gsl_matrix_set(m_test, i, j, 1);
		}
	}*/

	gsl_matrix_set(m_test, 0, 7, 1);
	gsl_matrix_set(m_test, 7,0, 1);
	gsl_matrix_set(m_test, 3,2, 5);
	gsl_matrix_set(m_test, 5,2, 5);

	for( i = 0; i < 8; i++){
		gsl_matrix_set(m_test, i, i, 3);
	}

	// set the oritinal one too
	gsl_matrix_memcpy(m_original, m_test);
	// now print the matrix out
	//print_matrix(m_test, 8, 8);

	printf("\n");
	print_matrix(m_original, 8,8);

	// simple matrix operations
	//gsl_matrix_add(m_test, m_test);
	//gsl_matrix_add_constant(m_test, 1.05);

	//print_matrix(m_test, 8,8);

	// creates the lu decomp of the matrix
	// need to allocate the permutation
	gsl_linalg_LU_decomp(m_test, m_LU_permutation, &signum);

	// calculate the determinant of m_test from the lu
	determinant_m = gsl_linalg_LU_det(m_test, signum);
	printf("det_m = %g\n", determinant_m);
	// invert the matrix
	gsl_linalg_LU_invert(m_test, m_LU_permutation, m_inverted);

	// multiply the inverse and the matrix together  (can't use the LU one bc it's fucked up)
	// m_test should be I
	gsl_blas_dgemm( CblasNoTrans,CblasNoTrans , 1.0, m_original, m_inverted, 0.0, m_test);

	print_matrix(m_test, 8,8);

	gsl_permutation_free(m_LU_permutation);
	gsl_matrix_free(m_inverted);
	gsl_matrix_free(m_test);
	return(0);
}

void print_matrix(gsl_matrix* m, int nx, int ny){
	int i,j;
	for(i = 0; i < nx; i++){
		for(j= 0; j < ny; j++){
			printf("%g ", gsl_matrix_get(m, i, j));
		}
		printf("\n");
 	}
}


// SPDX-License-Identifier: Unlicense

/*
 * libinput calibration calculation
 */

#include <stdio.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>

/*
 * Compute the (Moore-Penrose) pseudo-inverse of a matrix.
 *
 * PUBLIC DOMAIN by Charl Linssen <charl@itfromb.it>
 *
 * If the singular value decomposition (SVD) of A = UΣVᵀ then the
 * pseudoinverse A⁻¹ = VΣ⁻¹Uᵀ, where ᵀ indicates transpose and
 * Σ⁻¹ is obtained by taking the reciprocal of each nonzero element on
 * the diagonal, leaving zeros in place. Elements on the diagonal smaller
 * than ``rcond`` times the largest singular value are considered zero.
 *
 * @parameter A		Input matrix. **WARNING**: the input matrix ``A`` is
 * 			destroyed. However, it is still the responsibility of
 * 			the caller to free it.
 *
 * @parameter rcond	A real number specifying the singular value threshold
 * 			for inclusion. NumPy default for ``rcond`` is 1E-15.
 *
 * @returns A_pinv	Matrix containing the result. ``A_pinv`` is allocated
 * 			in this function and it is the responsibility of the
 * 			caller to free it.
 *
 *
 */
gsl_matrix* moore_penrose_pinv(gsl_matrix *A, const double rcond) {

	gsl_matrix *V, *Sigma_pinv, *U, *A_pinv;
	gsl_matrix *_tmp_mat = NULL;
	gsl_vector *_tmp_vec;
	gsl_vector *u;
	double x, cutoff;
	size_t i, j;
	unsigned int n = A->size1;
	unsigned int m = A->size2;
	unsigned int was_swapped = 0;

	if (m > n) {
		/* libgsl SVD can only handle the case m <= n - transpose matrix */
		was_swapped = 1;
		_tmp_mat = gsl_matrix_alloc(m, n);
		gsl_matrix_transpose_memcpy(_tmp_mat, A);
		A = _tmp_mat;
		i = m;
		m = n;
		n = i;
	}

	/* do SVD */
	V = gsl_matrix_alloc(m, m);
	u = gsl_vector_alloc(m);
	_tmp_vec = gsl_vector_alloc(m);
	gsl_linalg_SV_decomp(A, V, u, _tmp_vec);
	gsl_vector_free(_tmp_vec);

	/* compute Σ⁻¹ */
	Sigma_pinv = gsl_matrix_alloc(m, n);
	gsl_matrix_set_zero(Sigma_pinv);
	cutoff = rcond * gsl_vector_max(u);

	for (i = 0; i < m; ++i) {
		if (gsl_vector_get(u, i) > cutoff) {
			x = 1. / gsl_vector_get(u, i);
		}
		else {
			x = 0.;
		}
		gsl_matrix_set(Sigma_pinv, i, i, x);
	}

	/* libgsl SVD yields "thin" SVD - pad to full matrix by adding zeros */
	U = gsl_matrix_alloc(n, n);
	gsl_matrix_set_zero(U);

	for (i = 0; i < n; ++i) {
		for (j = 0; j < m; ++j) {
			gsl_matrix_set(U, i, j, gsl_matrix_get(A, i, j));
		}
	}

	if (_tmp_mat != NULL) {
		gsl_matrix_free(_tmp_mat);
	}

	/* two dot products to obtain pseudoinverse */
	_tmp_mat = gsl_matrix_alloc(m, n);
	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1., V, Sigma_pinv, 0., _tmp_mat);

	if (was_swapped) {
		A_pinv = gsl_matrix_alloc(n, m);
		gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1., U, _tmp_mat, 0., A_pinv);
	}
	else {
		A_pinv = gsl_matrix_alloc(m, n);
		gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1., _tmp_mat, U, 0., A_pinv);
	}

	gsl_matrix_free(_tmp_mat);
	gsl_matrix_free(U);
	gsl_matrix_free(Sigma_pinv);
	gsl_vector_free(u);
	gsl_matrix_free(V);

	return A_pinv;
}

int main(int argc, char **argv) {
	const unsigned int col = 4;
	const unsigned int row = 3;
	const double rcond = 1E-15;

	const double oneEigth = 1.0/8.0;
	const double sevenEight = 7.0/8.0;

	if (argc < 11) {
		printf("usage: %s x-res y-res x0 y0 x1 y1 x2 y2 x3 y3 [swap-x-y]\n", argv[0]);
		return EXIT_FAILURE;
	}

        int xres = atoi(argv[1]);
        int yres = atoi(argv[2]);
        int x0in = atoi(argv[3]);
        int y0in = atoi(argv[4]);
        int x1in = atoi(argv[5]);
        int y1in = atoi(argv[6]);
        int x2in = atoi(argv[7]);
        int y2in = atoi(argv[8]);
        int x3in = atoi(argv[9]);
        int y3in = atoi(argv[10]);

	int swapxy = 0;
	if (argc > 11)
		swapxy = 1;

	double x0 = (double)x0in / (double)xres;
	double y0 = (double)y0in / (double)yres;
	double x1 = (double)x1in / (double)xres;
	double y1 = (double)y1in / (double)yres;
	double x2 = (double)x2in / (double)xres;
	double y2 = (double)y2in / (double)yres;
	double x3 = (double)x3in / (double)xres;
	double y3 = (double)y3in / (double)yres;

	gsl_matrix *A = gsl_matrix_alloc(row, col);
	gsl_matrix *C = gsl_matrix_alloc(row, col);
	gsl_matrix *R = gsl_matrix_alloc(3, 3);
	gsl_matrix *S;
	gsl_matrix *T;
	gsl_matrix *A_pinv;

	gsl_matrix_set(A, 0, 0, x0);
	gsl_matrix_set(A, 0, 1, x1);
	gsl_matrix_set(A, 0, 2, x2);
	gsl_matrix_set(A, 0, 3, x3);
	gsl_matrix_set(A, 1, 0, y0);
	gsl_matrix_set(A, 1, 1, y1);
	gsl_matrix_set(A, 1, 2, y2);
	gsl_matrix_set(A, 1, 3, y3);
	gsl_matrix_set(A, 2, 0, 1.0);
	gsl_matrix_set(A, 2, 1, 1.0);
	gsl_matrix_set(A, 2, 2, 1.0);
	gsl_matrix_set(A, 2, 3, 1.0);

	gsl_matrix_set(C, 0, 0, oneEigth);
	gsl_matrix_set(C, 0, 1, sevenEight);
	gsl_matrix_set(C, 0, 2, oneEigth);
	gsl_matrix_set(C, 0, 3, sevenEight);
	gsl_matrix_set(C, 1, 0, oneEigth);
	gsl_matrix_set(C, 1, 1, oneEigth);
	gsl_matrix_set(C, 1, 2, sevenEight);
	gsl_matrix_set(C, 1, 3, sevenEight);
	gsl_matrix_set(C, 2, 0, 1.0);
	gsl_matrix_set(C, 2, 1, 1.0);
	gsl_matrix_set(C, 2, 2, 1.0);
	gsl_matrix_set(C, 2, 3, 1.0);

	A_pinv = moore_penrose_pinv(A, rcond);
	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, C, A_pinv, 0.0, R);

	if (swapxy) {
		S = gsl_matrix_alloc(3, 3);

		/* extra swapping x-y axes as requested */
		gsl_matrix_set(S, 0, 0,  0.0);
		gsl_matrix_set(S, 0, 1, -1.0);
		gsl_matrix_set(S, 0, 2,  1.0);
		gsl_matrix_set(S, 1, 0, -1.0);
		gsl_matrix_set(S, 1, 1,  0.0);
		gsl_matrix_set(S, 1, 2,  1.0);
		gsl_matrix_set(S, 2, 0,  0.0);
		gsl_matrix_set(S, 2, 1,  0.0);
		gsl_matrix_set(S, 2, 2,  1.0);

		T = R;
		R = gsl_matrix_alloc(3, 3);
		gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, T, S, 0.0, R);
	}

        printf("%f, %f, %f, %f, %f, %f, %f, %f, %f\n",
                gsl_matrix_get((const gsl_matrix *)R, 0, 0), gsl_matrix_get((const gsl_matrix *)R, 0, 1), gsl_matrix_get((const gsl_matrix *)R, 0, 2),
                gsl_matrix_get((const gsl_matrix *)R, 1, 0), gsl_matrix_get((const gsl_matrix *)R, 1, 1), gsl_matrix_get((const gsl_matrix *)R, 1, 2),
                gsl_matrix_get((const gsl_matrix *)R, 2, 0), gsl_matrix_get((const gsl_matrix *)R, 2, 1), gsl_matrix_get((const gsl_matrix *)R, 2, 2));

	gsl_matrix_free(A);
	gsl_matrix_free(C);
	gsl_matrix_free(R);
	gsl_matrix_free(A_pinv);

	if (swapxy) {
		gsl_matrix_free(S);
		gsl_matrix_free(T);
	}
	return EXIT_SUCCESS;
}


//
// Created by Harold on 2020/9/16.
//

#ifndef M_MATH_M_CURVEFIT_H
#define M_MATH_M_CURVEFIT_H

#include <map>
#include <gsl/gsl_fit.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <numeric>
#include <vector>
#include <cmath>

namespace M_MATH {
    class CurveFit{
    public:
        enum robust_type {
            DEFAULT,
            BISQUARE,
            CAUCHY,
            FAIR,
            HUBER,
            OLS,
            WELSCH
        };
    public:
        bool is_valid() const { return !m_coefficient.empty(); }
        bool linearfit(double const* x, double const* y, size_t n);
        bool polyfit(double const* x, double const* y, size_t length, unsigned poly_n);
        bool robustfit(double const* x, double const* y, size_t length, robust_type type, unsigned poly_n);

        double getY(double x) const;

        template<typename IT_X, typename IT_Y>
        void getYs(IT_X x_begin, IT_X x_end, IT_Y y_begin) const;

        double get_coefficient(size_t n) const;
        void set_coefficient(size_t n, double c) { m_coefficient[n] = c; };
        size_t get_coefficient_size() const { return m_coefficient.size(); };

        double get_slope() { return m_coefficient[1]; };
        double get_intercept() { return m_coefficient[0]; };
        double get_SSR() const { return m_ssr; };
        double get_SSE() const { return m_sse; };
        double get_SST() const { return m_sst; };
        double get_RMSE() const { return m_rmse; };
        double get_RSquare() const { return m_RSquare; };
        double get_Goodness() const { return m_goodness; };

        void clear() {
            m_coefficient.clear();
            m_err.clear();
        }

    private:
        std::map<size_t, double> m_coefficient;  // <index, fitting coefficient>, [0]: slope, [1] intercept
        std::map<size_t, double> m_err;
        double m_cov;  // covariance
        double m_ssr;  // sum of squares due to regression
        double m_sse;  // sum of squares error
        double m_sst;  // m_sst = m_ssr + m_sse
        double m_rmse;  // RMS error
        double m_RSquare;  // R-Square

        double m_sumsq;  // sum of squares
        double m_goodness;  // fit goodness

        static void get_determinate_parameters(double const* y, double const* yf,
                                               size_t length,
                                               double &ssr,
                                               double &sse,
                                               double &sst,
                                               double &rmse,
                                               double &RSquare) {
            double y_mean = std::accumulate(y, y+length, 0.0);
            ssr = 0.0;
            for (auto i = 0; i < length; ++i) {
                ssr += (yf[i] - y_mean) * (yf[i] - y_mean);
                sse += (y[i] - yf[i]) * (y[i] - yf[i]);
            }
            sst = ssr + sse;
            rmse = std::sqrt(sse/(double)length);
            RSquare = 1.0 - ssr/sst;
        }

        static gsl_multifit_robust_type const* get_robust_type(robust_type type) {
            switch (type) {
                case DEFAULT:
                    return gsl_multifit_robust_default;
                case BISQUARE:
                    return gsl_multifit_robust_bisquare;
                case CAUCHY:
                    return gsl_multifit_robust_cauchy;
                case FAIR:
                    return gsl_multifit_robust_fair;
                case HUBER:
                    return gsl_multifit_robust_huber;
                case OLS:
                    return gsl_multifit_robust_ols;
                case WELSCH:
                    return gsl_multifit_robust_welsch;
            }
            return nullptr;
        }
    };

    inline bool CurveFit::linearfit(const double *x, const double *y, size_t n) {
        clear();
        m_coefficient[0] = 0;
        m_coefficient[1] = 1;
        m_err[0] = 0;
        m_err[0] = 0;
        // double const* x, size_t const x_stride,
        // double const* y, size_t const y_stride,
        // size_t n,
        // double &intercept,
        // double &slope,
        // double &intercept_err,
        // double &slope_err,
        // double &cov,
        // double &wssr
        int ret = gsl_fit_linear(x, 1, y, 1, n,
                                 &m_coefficient[0], &m_coefficient[1],
                                 &m_err[0], &m_err[1],
                                 &m_cov, &m_sumsq);
        if (ret != 0)
            return false;
        // gammaQ function
        m_goodness = gsl_cdf_chisq_Q(m_sumsq / 2.0, double(n - 2) / 2.0);
        {
            std::vector<double> yf(n, 0);
            getYs(x, x+n, yf.begin());
            get_determinate_parameters(y, &yf[0], n, m_ssr, m_sse, m_sst, m_rmse, m_RSquare);
        }
        return true;
    }

    inline bool CurveFit::polyfit(const double *x, const double *y, size_t length, unsigned poly_n) {
        gsl_matrix *Xf = gsl_matrix_alloc(length, poly_n+1);
        gsl_vector *Yf = gsl_vector_alloc(length);
        gsl_vector *c = gsl_vector_alloc(poly_n+1);
        gsl_matrix *cov = gsl_matrix_alloc(poly_n+1, poly_n+1);

        for (auto i = 0; i < length; i++) {
            gsl_matrix_set(Xf, i, 0, 1.0);
            gsl_vector_set(Yf, i, y[i]);
            for (auto j = 1; j < poly_n + 1; j++)
                gsl_matrix_set(Xf, i, j, std::pow(x[i], int(j)));
        }

        auto *workspace = gsl_multifit_linear_alloc(length, poly_n+1);
        double chisq;
        // temporary vec to hold computed coefficients
        std::vector<double> coe(poly_n+1, 0);

        int ret = gsl_multifit_linear(Xf, Yf, c, cov, &chisq, workspace);
        gsl_multifit_linear_free(workspace);
        for (auto i = 0; i < c->size; i++)
            coe[i] = gsl_vector_get(c, i);

        gsl_matrix_free(Xf);
        gsl_vector_free(Yf);
        gsl_vector_free(c);
        gsl_matrix_free(cov);

        // if not successfully computed, not change former results
        if (ret != 0)
            return false;

        // update results
        m_goodness = gsl_cdf_chisq_Q(chisq/2.0, double(length-2)/2.0);
        clear();
        for (auto i = 0; i < poly_n+1; i++)
            m_coefficient[i] = coe[i];
        {
            std::vector<double> yf(length, 0);
            getYs(x, x+length, yf.begin());
            get_determinate_parameters(y, &yf[0], length, m_ssr, m_sse, m_sst, m_rmse, m_RSquare);
        }
        return true;
    }

    inline double CurveFit::getY(double x) const {
        double ret = 0;
        for (auto const& it : m_coefficient)
            ret += (it.second) * std::pow(x, it.first);
        return ret;
    }

    template<typename IT_X, typename IT_Y>
    inline void CurveFit::getYs(IT_X x_begin, IT_X x_end, IT_Y y_begin) const {
        for (; x_begin != x_end; ++x_begin, ++y_begin)
            *y_begin = getY(*x_begin);
    }

    inline double CurveFit::get_coefficient(size_t n) const {
        auto it = m_coefficient.find(n);
        if (it != m_coefficient.end())
            return it->second;
        return 0.0;
    }

    // poly_n >= 2
    // poly_n = 2: linear fit
    bool CurveFit::robustfit(const double *x, const double *y, size_t length, robust_type type, unsigned int poly_n) {
        gsl_matrix *Xf = gsl_matrix_alloc(length, poly_n);
        gsl_vector *Yf = gsl_vector_alloc(length);
        gsl_vector *c = gsl_vector_alloc(poly_n);
        gsl_matrix *cov = gsl_matrix_alloc(poly_n, poly_n);

        for (auto i = 0; i < length; i++) {
            gsl_matrix_set(Xf, i, 0, 1.0);
            gsl_vector_set(Yf, i, y[i]);
            for (auto j = 1; j < poly_n; j++)
                gsl_matrix_set(Xf, i, j, std::pow(x[i], int(j)));
        }

        auto *workspace = gsl_multifit_robust_alloc(get_robust_type(type), length, poly_n);
        // temporary vec to hold computed coefficients
        std::vector<double> coe(poly_n, 0);

        int ret = gsl_multifit_robust(Xf, Yf, c, cov, workspace);
        gsl_multifit_robust_free(workspace);
        for (auto i = 0; i < c->size; i++)
            coe[i] = gsl_vector_get(c, i);

        gsl_matrix_free(Xf);
        gsl_vector_free(Yf);
        gsl_vector_free(c);
        gsl_matrix_free(cov);

        // if not successfully computed, not change former results
        if (ret != 0)
            return false;

        // update results
        clear();
        for (auto i = 0; i < poly_n; i++)
            m_coefficient[i] = coe[i];
        {
            std::vector<double> yf(length, 0);
            getYs(x, x+length, yf.begin());
            get_determinate_parameters(y, &yf[0], length, m_ssr, m_sse, m_sst, m_rmse, m_RSquare);
        }
        return true;
    }
}

#endif //M_MATH_M_CURVEFIT_H


#include <stdio.h>
#include <math.h>
#include <gsl/gsl_poly.h>
#include <gsl/gsl_errno.h>
#include <time.h>

int main (int argc, char* argv[])
{
  /* Parse the command line arguments. */
  const char* usage = "Usage: zeroes <xmin> <xmax> <ymin> <ymax> <xres> <coeff> ... <coeff> - <degree> ... <degree>\n";

  if (argc < 8) {
    fprintf (stderr, "%s", usage);
    exit(EXIT_FAILURE);
  }

  /* The window in the plane. */
  const double xmin = atof(argv[1]);
  const double xmax = atof(argv[2]);
  const double ymin = atof(argv[3]);
  const double ymax = atof(argv[4]);

  /* Image size, width is given, height is computed. */
  const int xres = atoi(argv[5]);
  const int yres = (int)((xres * (ymax-ymin)) / (xmax-xmin));

  /* The coefficients */
  int ci_max = 0 ;
  while (6 + ci_max < argc && (argv[6+ci_max][0] != '-' || argv[6+ci_max][1] != 0)) { ci_max++; }
  if (ci_max == 0) {
    fprintf (stderr, "%s\nError: specify at least one coefficient.", usage) ;
    exit(EXIT_FAILURE);
  }
  double coeff[ci_max];
  for (int ci = 0; ci < ci_max; ci++) { coeff[ci] = atof(argv[6+ci]); }

  /* The degrees */
  const int di_max = argc - 7 - ci_max;
  if (di_max <= 0) {
    fprintf (stderr, "%s\nError: specify at least one degree.", usage) ;
    exit(EXIT_FAILURE);
  }
  int degree[di_max];
  for (int di = 0; di < di_max; di++) { degree[di] = atoi(argv[7+ci_max+di]); }

  /* Allocate the image and initialize it */
  unsigned int *image = (unsigned int *)calloc(sizeof(unsigned int), yres * xres) ;
  const unsigned int count_bound = 0xffffffff;
  unsigned int max_count = 0 ;

  /* Turn off the GSL error handler that aborts on error (some polynomials have tricky zeroes) */
  gsl_set_error_handler_off ();

  /* We're going to skip zeroes whose imaginary part is almost zero. */
  const double epsilon = 1.0e-20 ;

  time_t start = time(NULL);

  /* Run through degrees and compute the zeroes for each one. */
  for (int di = 0; di < di_max; di++) {
    const int d = degree[di]; /* The degree we're working on. */
    time_t now = time(NULL);
    fprintf (stderr, "Degree %d (%.2lf seconds)\n", d, difftime(now, start));
    /* Initialize the polynomial. */
    double poly[d+1];
    for (int j = 0; j <= d; j++) { poly[j] = coeff[0]; }
    /* Initialize the counters. */
    int counter[d+1];
    for (int j = 0; j <= d; j++) { counter[j] = 0; }
    int j = 0;
    /* Allocate the workspace. */
    gsl_poly_complex_workspace *w = gsl_poly_complex_workspace_alloc(d+1);
    do {
      /* Compute the zeroes if head coefficient is non-zero. */
      if (poly[d] > epsilon || poly[d] < -epsilon) {
        double z[2*d]; /* the zeroes are stored here */
        int status = gsl_poly_complex_solve (poly, d+1, w, z);
        /* Only use zeroes if GSL reported success */
        if (status == 0) {
          /* Draw zeroes, skipping the real ones that are away from the origin */
          for (int i=0; i < d; i++) {
            if (-epsilon < z[2*i+1] && z[2*i+1] < epsilon && (z[2*i] < -0.5 || z[2*i] > 0.5)) {
              continue;
            }
            int x = (int)((xres * (z[2*i] - xmin)) / (xmax - xmin));
            int y = yres - (int)((yres * (z[2*i+1] - ymin)) / (ymax - ymin));
            if (0 <= x && x < xres && 0 <= y && y < yres && image[xres * y + x] < count_bound) {
              int c = ++image[xres * y + x];
              if (max_count < c) { max_count = c; }
            }
          }
        }
      }
      /* calculate the next polynomial */
      for (j = 0; j <= d && counter[j] == ci_max-1; j++) { counter[j] = 0; poly[j] = coeff[0]; }
      if (j <= d) { counter[j]++; poly[j] = coeff[counter[j]]; }
    } while (j <= d);
    /* Deallocate workspace. */
    gsl_poly_complex_workspace_free (w);
  }

  /* Output the computed result. */
  /* ASCII header to the file*/
  printf("P6\n# Zeroes, xmin=%lf, xmax=%lf, ymin=%lf, ymax=%lf, max_count=%d, coeffs=[", xmin, xmax, ymin, ymax, max_count);
  for (int ci=0; ci < ci_max; ci++) { printf("%lf%s", coeff[ci], (ci < ci_max-1 ? ", " : "],")); }
  printf("degrees = [");
  for (int di=0; di < di_max; di++) { printf("%d%s", degree[di], (di < di_max-1 ? ", " : "],")); }
  printf("\n");
  printf("%d\n%d\n%d\n", xres, yres, 0xffff);
  /* Image */
  double r = 65535.0 / log((double)max_count) ;
  for (int y = 0; y < yres; y++) {
    for (int x = 0; x < xres; x++) {
        unsigned char color[6];
        unsigned int k = (unsigned int)(r * log((float)image[xres * y + x])) ;
        color[0] = k >> 8;
        color[1] = k & 255;
        color[2] = k >> 8;
        color[3] = k & 255;
        color[4] = k >> 8;
        color[5] = k & 255;
        fwrite(color, 6, 1, stdout);
    }
  }
  free(image);
  return(0);
}


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>

struct color {
	double value[3];
};

struct color cartesian_to_polar(struct color source_color) {
	struct color target_color;
	target_color.value[0] = source_color.value[0];
	target_color.value[1] = hypot(source_color.value[1], source_color.value[2]);
	target_color.value[2] = atan2(source_color.value[2], source_color.value[1])*180.0/M_PI;

	if (target_color.value[2] < 0) {
		target_color.value[2] = target_color.value[2] + 360;
	}

	return target_color;
}

struct color polar_to_cartesian(struct color source_color) {
	struct color target_color;
	double angle;
	angle = source_color.value[2]/180*(M_PI);
	target_color.value[0] = source_color.value[0];
	target_color.value[1] = cos(angle)*source_color.value[1];
	target_color.value[2] = sin(angle)*source_color.value[1];
	return target_color;
}

/* XYZ = CIE 1931 XYZ */

struct color XYZ_values_to_OSA74(double X, double Y, double Z) {
	struct color target_color;
	double x = X/(X+Y+Z);
	double y = Y/(X+Y+Z);
	double Y0 = (4.4934*x*x + 4.3034*y*y - 4.276*x*y - 1.3744*x - 2.5643*y + 1.8103)*Y;
	double Lambda = 5.9*(cbrt(Y0) - 2.0/3 + 0.042*cbrt(Y0-30));
	double C = Lambda/(5.9*(cbrt(Y0) - 2.0/3));
	double R = 0.7990*X + 0.4194*Y - 0.1648*Z;
	double G = -0.4493*X + 1.3265*Y + 0.0927*Z;
	double B = -0.1149*X + 0.3394*Y + 0.7170*Z;
	/* target_color.value[0] = (Lambda - 14.3993)/sqrt(2); */
	target_color.value[0] = (Lambda - 14.4)/sqrt(2);
	target_color.value[1] = C*(1.7*cbrt(R) + 8*cbrt(G) - 9.7*cbrt(B));
	target_color.value[2] = C*(-13.7*cbrt(R) + 17.7*cbrt(G) - 4*cbrt(B));
	return target_color;
}

struct color XYZ_values_to_OSA90(double X, double Y, double Z) {
	struct color target_color;
	double x = X/(X+Y+Z);
	double y = Y/(X+Y+Z);
	double Y0 = (4.4934*x*x + 4.3034*y*y - 4.276*x*y - 1.3744*x - 2.5643*y + 1.8103)*Y;
	double Lambda = 5.9*(cbrt(Y0) - 2.0/3 + 0.06*cbrt(Y0-30));
	double C = Lambda/(5.9*(cbrt(Y0) - 2.0/3));
	double R = 0.9285*X + 0.3251*Y - 0.1915*Z;
	double G = -0.4493*X + 1.3265*Y + 0.0927*Z;
	double B = -0.2032*X + 0.6*Y + 0.5523*Z;
	/* target_color.value[0] = (Lambda - 14.3993)/sqrt(2); */
	target_color.value[0] = (Lambda - 14.4)/sqrt(2);
	target_color.value[1] = C*(-1.3*cbrt(R) + 17*cbrt(G) - 15.7*cbrt(B));
	target_color.value[2] = C*(-12.7*cbrt(R) + 19*cbrt(G) - 6.3*cbrt(B));
	return target_color;
}

struct color XYZ_to_OSA74(struct color source_color) {
	double X, Y, Z;
	X = source_color.value[0];
	Y = source_color.value[1];
	Z = source_color.value[2];
	return XYZ_values_to_OSA74(X, Y, Z);
}

struct color XYZ_to_OSA90(struct color source_color) {
	double X, Y, Z;
	X = source_color.value[0];
	Y = source_color.value[1];
	Z = source_color.value[2];
	return XYZ_values_to_OSA90(X, Y, Z);
}

int print_matrix(gsl_matrix *matrix) {
	int row, column;
	for(row = 0; row < matrix->size1; row++) {
		for(column = 0; column < matrix->size2; column++) {
			printf("%8.8f", gsl_matrix_get(matrix, row, column));
		}
		putchar('\n');
	}
	return 0;
}

struct color OSA74_to_XYZ(struct color source_color) {
	double L, j, g;
	L = source_color.value[0];
	j = source_color.value[1];
	g = source_color.value[2];
	struct color target_color;
	int max_iterations = 600;
	double X, Y, Z, previous_X, previous_Y, previous_Z, Delta;
	double L0, L1, L2, L3, j0, j1, j2, j3, g0, g1, g2, g3;
	struct color color0, color1, color2, color3;

	gsl_matrix *matrix0 = gsl_matrix_alloc(3, 3);
	gsl_matrix *inverse_matrix0 = gsl_matrix_alloc(3, 3);
	gsl_matrix *matrix1 = gsl_matrix_alloc(3, 1);
	gsl_matrix *matrix2 = gsl_matrix_alloc(3, 1);
	gsl_permutation *permutation = gsl_permutation_alloc(3);
	int sign = 0;

	/* start values */
	/*X = 28.4; Y = 30.0; Z = 32.2; Delta = 0.5;*/
	X = 28.4; Y = 30.0; Z = 32.2; Delta = 0.005;
	gsl_matrix_set(matrix2, 0, 0, X);
	gsl_matrix_set(matrix2, 1, 0, Y);
	gsl_matrix_set(matrix2, 2, 0, Z);

	int i = 0; do {
		/* calculate colors for influence matrix */
		color0 = XYZ_values_to_OSA74(X, Y, Z);
		color1 = XYZ_values_to_OSA74(X+Delta, Y, Z);
		color2 = XYZ_values_to_OSA74(X, Y+Delta, Z);
		color3 = XYZ_values_to_OSA74(X, Y, Z+Delta);
		L0 = color0.value[0]; j0 = color0.value[1]; g0 = color0.value[2];
		L1 = color1.value[0]; j1 = color1.value[1]; g1 = color1.value[2];
		L2 = color2.value[0]; j2 = color2.value[1]; g2 = color2.value[2];
		L3 = color3.value[0]; j3 = color3.value[1]; g3 = color3.value[2];

		gsl_matrix_set(matrix0, 0, 0, (L1-L0)/Delta);
		gsl_matrix_set(matrix0, 0, 1, (L2-L0)/Delta);
		gsl_matrix_set(matrix0, 0, 2, (L3-L0)/Delta);
		gsl_matrix_set(matrix0, 1, 0, (j1-j0)/Delta);
		gsl_matrix_set(matrix0, 1, 1, (j2-j0)/Delta);
		gsl_matrix_set(matrix0, 1, 2, (j3-j0)/Delta);
		gsl_matrix_set(matrix0, 2, 0, (g1-g0)/Delta);
		gsl_matrix_set(matrix0, 2, 1, (g2-g0)/Delta);
		gsl_matrix_set(matrix0, 2, 2, (g3-g0)/Delta);

		gsl_matrix_set(matrix1, 0, 0, L-L0);
		gsl_matrix_set(matrix1, 1, 0, j-j0);
		gsl_matrix_set(matrix1, 2, 0, g-g0);

		/* update matrix2:  matrix2 = inverse_matrix0  matrix1 + matrix2 */
		/* incremental term (inverse_matrix0 × matrix1) is divided by (5+cbrt(i)) */
		gsl_linalg_LU_decomp(matrix0, permutation, &sign);
		gsl_linalg_LU_invert(matrix0, permutation, inverse_matrix0);
		gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1/(5+cbrt(i)), inverse_matrix0, matrix1, 1.0, matrix2);

		previous_X = X; previous_Y = Y; previous_Z = Z;
		X = gsl_matrix_get(matrix2, 0, 0);
		Y = gsl_matrix_get(matrix2, 1, 0);
		Z = gsl_matrix_get(matrix2, 2, 0);

		/* printf("\n%d\n", i);
		print_matrix(matrix2); */

		i++;
	} while ((i < max_iterations) && ((fabs(X - previous_X) > 0.0001) || (fabs(Y - previous_Y) > 0.0001) || (fabs(Z - previous_Z) > 0.0001)));

	/*printf("Iterations: %i\n", i);*/

	/*free memory*/
	gsl_matrix_free(matrix0);
	gsl_matrix_free(inverse_matrix0);
	gsl_matrix_free(matrix1);
	gsl_matrix_free(matrix2);
	/*gsl_matrix_free(matrix_product);*/
	gsl_permutation_free(permutation);

	if (i == max_iterations) {
		fprintf(stderr, "Error: Maximum iterations reached.\n");
	}

	target_color.value[0] = X;
	target_color.value[1] = Y;
	target_color.value[2] = Z;
	return target_color;
}

struct color OSA90_to_XYZ(struct color source_color) {
	double L, j, g;
	L = source_color.value[0];
	j = source_color.value[1];
	g = source_color.value[2];
	struct color target_color;
	int max_iterations = 600;
	double X, Y, Z, previous_X, previous_Y, previous_Z, Delta;
	double L0, L1, L2, L3, j0, j1, j2, j3, g0, g1, g2, g3;
	struct color color0, color1, color2, color3;

	gsl_matrix *matrix0 = gsl_matrix_alloc(3, 3);
	gsl_matrix *inverse_matrix0 = gsl_matrix_alloc(3, 3);
	gsl_matrix *matrix1 = gsl_matrix_alloc(3, 1);
	gsl_matrix *matrix2 = gsl_matrix_alloc(3, 1);
	gsl_permutation *permutation = gsl_permutation_alloc(3);
	int sign = 0;

	/* start values */
	/*X = 28.4; Y = 30.0; Z = 32.2; Delta = 0.5;*/
	X = 28.4; Y = 30.0; Z = 32.2; Delta = 0.005;
	gsl_matrix_set(matrix2, 0, 0, X);
	gsl_matrix_set(matrix2, 1, 0, Y);
	gsl_matrix_set(matrix2, 2, 0, Z);

	int i = 0; do {
		/* calculate colors for influence matrix */
		color0 = XYZ_values_to_OSA90(X, Y, Z);
		color1 = XYZ_values_to_OSA90(X+Delta, Y, Z);
		color2 = XYZ_values_to_OSA90(X, Y+Delta, Z);
		color3 = XYZ_values_to_OSA90(X, Y, Z+Delta);
		L0 = color0.value[0]; j0 = color0.value[1]; g0 = color0.value[2];
		L1 = color1.value[0]; j1 = color1.value[1]; g1 = color1.value[2];
		L2 = color2.value[0]; j2 = color2.value[1]; g2 = color2.value[2];
		L3 = color3.value[0]; j3 = color3.value[1]; g3 = color3.value[2];

		gsl_matrix_set(matrix0, 0, 0, (L1-L0)/Delta);
		gsl_matrix_set(matrix0, 0, 1, (L2-L0)/Delta);
		gsl_matrix_set(matrix0, 0, 2, (L3-L0)/Delta);
		gsl_matrix_set(matrix0, 1, 0, (j1-j0)/Delta);
		gsl_matrix_set(matrix0, 1, 1, (j2-j0)/Delta);
		gsl_matrix_set(matrix0, 1, 2, (j3-j0)/Delta);
		gsl_matrix_set(matrix0, 2, 0, (g1-g0)/Delta);
		gsl_matrix_set(matrix0, 2, 1, (g2-g0)/Delta);
		gsl_matrix_set(matrix0, 2, 2, (g3-g0)/Delta);

		gsl_matrix_set(matrix1, 0, 0, L-L0);
		gsl_matrix_set(matrix1, 1, 0, j-j0);
		gsl_matrix_set(matrix1, 2, 0, g-g0);

		/* update matrix2:  matrix2 = inverse_matrix0  matrix1 + matrix2 */
		/* incremental term (inverse_matrix0 × matrix1) is divided by (5+cbrt(i)) */
		gsl_linalg_LU_decomp(matrix0, permutation, &sign);
		gsl_linalg_LU_invert(matrix0, permutation, inverse_matrix0);
		gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1/(5+cbrt(i)), inverse_matrix0, matrix1, 1.0, matrix2);

		previous_X = X; previous_Y = Y; previous_Z = Z;
		X = gsl_matrix_get(matrix2, 0, 0);
		Y = gsl_matrix_get(matrix2, 1, 0);
		Z = gsl_matrix_get(matrix2, 2, 0);

		/* printf("\n%d\n", i);
		print_matrix(matrix2); */

		i++;
	} while ((i < max_iterations) && ((fabs(X - previous_X) > 0.0001) || (fabs(Y - previous_Y) > 0.0001) || (fabs(Z - previous_Z) > 0.0001)));

	/*printf("Iterations: %i\n", i);*/

	/*free memory*/
	gsl_matrix_free(matrix0);
	gsl_matrix_free(inverse_matrix0);
	gsl_matrix_free(matrix1);
	gsl_matrix_free(matrix2);
	/*gsl_matrix_free(matrix_product);*/
	gsl_permutation_free(permutation);

	if (i == max_iterations) {
		fprintf(stderr, "Error: Maximum iterations reached.\n");
	}

	target_color.value[0] = X;
	target_color.value[1] = Y;
	target_color.value[2] = Z;
	return target_color;
}

double npow(double base, double exponent) {
	if (base < 0) {
		return -pow(fabs(base), exponent);
	} else {
		return pow(base, exponent);
	}
}

const double c1 = 3424/pow(2, 12);
const double c2 = 2413/pow(2, 7);
const double c3 = 2392/pow(2, 7);
const double n = 2610/pow(2, 14);
const double p = 1.7*2523/pow(2, 5);

double perceptual_quantizer(double cone_response) {
	return npow((c1 + c2*npow(cone_response/10000, n)) / (1 + c3*npow(cone_response/10000, n)), p);
}

double invert_perceptual_quantizer(double cone_response) {
	/*printf("input: %f\n", cone_response);*/
	/*printf("return: %f\n", 10000*npow((c1 - npow(cone_response, 1/p))/(c3*npow(cone_response, 1/p) - c2), 1/n));*/

	return 10000*npow((c1 - npow(cone_response, 1/p))/(c3*npow(cone_response, 1/p) - c2), 1/n);
}

const double d = -0.56;
const double d0 = 1.6295499532821566E-11;

struct color XYZ_to_Jab(struct color source_color) {
	double X = source_color.value[0];
	double Y = source_color.value[1];
	double Z = source_color.value[2];
	double adjusted_X, adjusted_Y, L, M, S, adjusted_L, adjusted_M, adjusted_S, I, J, a, b, C, h;
	struct color target_color;

	adjusted_X = 1.15*X - 0.15*Z;
	adjusted_Y = 0.66*Y + 0.34*X;

	L = 0.41478972*adjusted_X + 0.579999*adjusted_Y + 0.0146480*Z;
	M = -0.2015100*adjusted_X + 1.120649*adjusted_Y + 0.0531008*Z;
	S = -0.0166008*adjusted_X + 0.264800*adjusted_Y + 0.6684799*Z;

	adjusted_L = perceptual_quantizer(L);
	adjusted_M = perceptual_quantizer(M);
	adjusted_S = perceptual_quantizer(S);

	I = 0.5*adjusted_L + 0.5*adjusted_M;
	J = ((1+d)*I)/(1+d*I) - d0;

	a = 3.524000*adjusted_L - 4.066708*adjusted_M + 0.542708*adjusted_S;
	b = 0.199076*adjusted_L + 1.096799*adjusted_M - 1.295875*adjusted_S;

	target_color.value[0] = J;
	target_color.value[1] = a;
	target_color.value[2] = b;
	return target_color;
}

struct color Jab_to_XYZ(struct color source_color) {
	double J = source_color.value[0];
	double a = source_color.value[1];
	double b = source_color.value[2];
	double I, adjusted_L, adjusted_M, adjusted_S, L, M, S, adjusted_X, adjusted_Y, X, Y, Z;
	struct color target_color;

	I = ((J + d0)/(1 + d - d*(J + d0)));

	adjusted_L = I + 0.138605*a + 0.0580473*b;
	adjusted_M = I - 0.138605*a - 0.0580473*b;
	adjusted_S = I - 0.0960192*a - 0.811892*b;

	L = invert_perceptual_quantizer(adjusted_L);
	M = invert_perceptual_quantizer(adjusted_M);
	S = invert_perceptual_quantizer(adjusted_S);

	adjusted_X = 1.92423*L - 1.00479*M + 0.0376514*S;
	adjusted_Y = 0.350317*L + 0.726481*M - 0.0653844*S;
	Z = -0.0909828*L - 0.312728*M + 1.52277*S;

	X = (adjusted_X + 0.15*Z)/1.15;
	Y = (adjusted_Y - 0.34*X)/0.66;

	target_color.value[0] = X;
	target_color.value[1] = Y;
	target_color.value[2] = Z;
	return target_color;
}

const double J_scale = 0.167174631034780;

struct color scale_Jab(struct color source_color) {
	struct color target_color;

	target_color.value[0] = source_color.value[0]/J_scale*100;
	target_color.value[1] = source_color.value[1]/J_scale*100;
	target_color.value[2] = source_color.value[2];
	return target_color;
}

struct color unscale_Jab(struct color source_color) {
	struct color target_color;

	target_color.value[0] = source_color.value[0]*J_scale/100;
	target_color.value[1] = source_color.value[1]*J_scale/100;
	target_color.value[2] = source_color.value[2];
	return target_color;
}

double apply_gamma_correction(double value) {
	if (value > 0.0031308) {
		return 1.055*pow(value, (1.0/2.4)) - 0.055;
	} else {
		return 12.92*value;
	}
}

double remove_gamma_correction(double value) {
	if (value > 0.04045) {
		return pow(((value+0.055)/1.055), 2.4);
	} else {
		return value/12.92;
	}
}

struct color XYZ_to_sRGB(struct color source_color) {
	double X, Y, Z;
	X = source_color.value[0];
	Y = source_color.value[1];
	Z = source_color.value[2];
	struct color target_color;
	double linear_R, linear_G, linear_B, R, G, B;

	/* XYZ source values range = [0, 100] */
	X = X/100.0; Y = Y/100.0; Z = Z/100.0;

	/* 2° observer, D65 illuminant */
	linear_R = X* 3.2406 + Y*-1.5372 + Z*-0.4986;
	linear_G = X*-0.9689 + Y* 1.8758 + Z* 0.0415;
	linear_B = X* 0.0557 + Y*-0.2040 + Z* 1.0570;

	R = apply_gamma_correction(linear_R);
	G = apply_gamma_correction(linear_G);
	B = apply_gamma_correction(linear_B);

	if ((R<0.0) || (R>1.0) || (G<0.0) || (G>1.0) || (B<0.0) || (B>1.0)) {
		errno = ERANGE;
	}

	/* RGB target values range = [0, 255] */
	target_color.value[0] = R*255;
	target_color.value[1] = G*255;
	target_color.value[2] = B*255;

	return target_color;
}

struct color sRGB_to_XYZ(struct color source_color) {
	double R, G, B;
	R = source_color.value[0];
	G = source_color.value[1];
	B = source_color.value[2];
	struct color target_color;
	double linear_R, linear_G, linear_B, X, Y, Z;

	/* RGB source values range = [0, 255] */
	R = R/255.0; G = G/255.0; B = B/255.0;

	linear_R = remove_gamma_correction(R);
	linear_G = remove_gamma_correction(G);
	linear_B = remove_gamma_correction(B);

	/* 2° observer, D65 illuminant */
	X = linear_R*0.4124 + linear_G*0.3576 + linear_B*0.1805;
	Y = linear_R*0.2126 + linear_G*0.7152 + linear_B*0.0722;
	Z = linear_R*0.0193 + linear_G*0.1192 + linear_B*0.9505;

	/* XYZ target values range = [0, 100] */
	target_color.value[0] = 100*X;
	target_color.value[1] = 100*Y;
	target_color.value[2] = 100*Z;
	return target_color;
}

const double Y_threshold = pow((6/29.0), 3);
const double D65_X = 95.0156;
const double D65_Y = 100;
const double D65_Z = 108.8199;

/*const double D65_X = 97.31273;*/
/*const double D65_Y = 100.00;*/
/*const double D65_Z = 138.59590;*/

struct color XYZ_to_Luv(struct color source_color) {
	/* L, u, v stand for L*, u*, v* */
	double X, Y, Z, prime_u, prime_v, relative_Y, white_prime_u, white_prime_v, L, u, v;
	struct color target_color;

	X = source_color.value[0];
	Y = source_color.value[1];
	Z = source_color.value[2];

	prime_u = (4*X) / (X + 15*Y + 3*Z);
	prime_v = (9*Y) / (X + 15*Y + 3*Z);

	white_prime_u = (4*D65_X) / (D65_X + 15*D65_Y + 3*D65_Z);
	white_prime_v = (9*D65_Y) / (D65_X + 15*D65_Y + 3*D65_Z);

	relative_Y = Y/D65_Y;

	if (relative_Y > pow((6/29.0), 3)) {
		L = cbrt(relative_Y)*116 - 16;
	} else {
		L = relative_Y*pow((29/3.0), 3);
	}

	u = 13*L*(prime_u - white_prime_u);
	v = 13*L*(prime_v - white_prime_v);

	target_color.value[0] = L;
	target_color.value[1] = u;
	target_color.value[2] = v;
	return target_color;
}

struct color Luv_to_XYZ(struct color source_color) {
	/* L, u, v stand for L*, u*, v* */
	double L, u, v, white_prime_u, white_prime_v, prime_u, prime_v, X, Y, Z;
	struct color target_color;

	L = source_color.value[0];
	u = source_color.value[1];
	v = source_color.value[2];

	white_prime_u = (4*D65_X) / (D65_X + 15*D65_Y + 3*D65_Z);
	white_prime_v = (9*D65_Y) / (D65_X + 15*D65_Y + 3*D65_Z);

	prime_u = u/(13*L) + white_prime_u;
	prime_v = v/(13*L) + white_prime_v;

	if (L > 8.0) {
		Y = D65_Y*pow((L + 16)/116.0, 3);
	} else {
		Y = D65_Y*L*pow(3/29.0, 3);
	}

	X = Y * (9*prime_u)/(4.0*prime_v);
	Z = Y * (12 - 3*prime_u - 20*prime_v)/(4.0*prime_v);

	target_color.value[0] = X;
	target_color.value[1] = Y;
	target_color.value[2] = Z;
	return target_color;
}

/* OSA74 range: -19.1032--10.0938 (29.1970) */

struct color OSA74_to_pOSA74(struct color source_color) {
	double L, j, g, new_L, chroma, hue;
	struct color target_color;

	L = source_color.value[0];
	j = source_color.value[1];
	g = source_color.value[2];

	new_L = (L*sqrt(2) + 19.1032)*100/29.1970;
	chroma = hypot(j, g)*100/29.1970;
	hue = atan2(g, j)*180.0/M_PI;

	if (hue < 0) {
		hue = hue + 360;
	}

	target_color.value[0] = new_L;
	target_color.value[1] = chroma;
	target_color.value[2] = hue;
	return target_color;
}

struct color pOSA74_to_OSA74(struct color source_color) {
	double new_L, chroma, hue, L, angle, j, g;
	struct color target_color;

	new_L = source_color.value[0];
	chroma = source_color.value[1];
	hue = source_color.value[2];

	L = (new_L*29.1970/100 - 19.1032)/sqrt(2);

	angle = hue/180*(M_PI);
	j = cos(angle)*chroma*29.1970/100;
	g = sin(angle)*chroma*29.1970/100;

	target_color.value[0] = L;
	target_color.value[1] = j;
	target_color.value[2] = g;
	return target_color;
}

int RGB_to_hex(struct color RGB_color) {
	int R = (int)(RGB_color.value[0] + 0.5);
	int G = (int)(RGB_color.value[1] + 0.5);
	int B = (int)(RGB_color.value[2] + 0.5);
	return (R<<16) | (G<<8) | B;
}

struct color hex_to_RGB(int hex_value) {
	struct color RGB_color;
	RGB_color.value[0] = ((hex_value>>16) & 0xFF)/255.0;
	RGB_color.value[1] = ((hex_value>>8) & 0xFF)/255.0;
	RGB_color.value[2] = (hex_value & 0xFF)/255.0;
	return RGB_color;
}

int main(int argc, char *argv[]) {
	int i;
	struct color source_color;
	struct color intermediate_color;
	struct color target_color;
	char next_char;
	char *source_format = argv[1];
	char *target_format = argv[2];

	/* check arguments */

	/* check argument count */
	if (argc != 6) {
		fprintf(stderr, "Usage: %s source_format target_format value1 value2 value3\n", argv[0]);
		return EXIT_FAILURE;
	}

	if (strcmp(source_format, target_format) == 0) {
		fprintf(stderr, "Source and target formats are identical.\n");
		return EXIT_FAILURE;
	}

	/* check if color values are numbers */
	for (i = 3; i < argc; i++) {
		if (sscanf(argv[i], "%lf%c", &source_color.value[i-3], &next_char) != 1) {
			fprintf(stderr, "Invalid color values, values must be numbers.\n");
			return EXIT_FAILURE;
		}
	}

	/* OSA/polar conversions do not need intermediate conversion to XYZ */
	if ((strcmp(source_format, "pOSA74") == 0) && (strcmp(target_format, "OSA74") == 0)) {
		target_color = pOSA74_to_OSA74(source_color);
		printf("%.2f %.2f %.2f\n", target_color.value[0], target_color.value[1], target_color.value[2]);
		return EXIT_SUCCESS;
	} else if ((strcmp(source_format, "OSA74") == 0) && (strcmp(target_format, "pOSA74") == 0)) {
		target_color = OSA74_to_pOSA74(source_color);
		printf("%.2f %.2f %.2f\n", target_color.value[0], target_color.value[1], target_color.value[2]);
		return EXIT_SUCCESS;
	}

	/* convert from source format to XYZ */
	if (strcmp(source_format, "Jab") == 0) {
		intermediate_color = Jab_to_XYZ(polar_to_cartesian(unscale_Jab(source_color)));
		/*intermediate_color = Jab_to_XYZ(polar_to_cartesian(source_color));*/
		/*intermediate_color = Jab_to_XYZ(source_color);*/
	} else if (strcmp(source_format, "LCh") == 0) {
		intermediate_color = Luv_to_XYZ(polar_to_cartesian(source_color));
	} else if (strcmp(source_format, "Luv") == 0) {
		intermediate_color = Luv_to_XYZ(source_color);
	} else if (strcmp(source_format, "OSA74") == 0) {
		intermediate_color = OSA74_to_XYZ(source_color);
	} else if (strcmp(source_format, "OSA90") == 0) {
		intermediate_color = OSA90_to_XYZ(source_color);
	} else if (strcmp(source_format, "pOSA74") == 0) {
		/*intermediate_color = OSA90_to_XYZ(polar_to_OSA(source_color));*/
		intermediate_color = OSA74_to_XYZ(pOSA74_to_OSA74(source_color));
	} else if (strcmp(source_format, "sRGB") == 0) {
		intermediate_color = sRGB_to_XYZ(source_color);
	} else if (strcmp(source_format, "XYZ") == 0) {
		intermediate_color = source_color;
	} else {
		fprintf(stderr, "Invalid source format.");
		return EXIT_FAILURE;
	}

	/* convert from XYZ to target format */
	if (strcmp(target_format, "Jab") == 0) {
		target_color = scale_Jab(cartesian_to_polar(XYZ_to_Jab(intermediate_color)));
		/*target_color = cartesian_to_polar(XYZ_to_Jab(intermediate_color));*/
		/*target_color = XYZ_to_Jab(intermediate_color);*/
	} else if (strcmp(target_format, "LCh") == 0) {
		target_color = cartesian_to_polar(XYZ_to_Luv(intermediate_color));
	} else if (strcmp(target_format, "Luv") == 0) {
		target_color = XYZ_to_Luv(intermediate_color);
	} else if (strcmp(target_format, "OSA74") == 0) {
		target_color = XYZ_to_OSA74(intermediate_color);
	} else if (strcmp(target_format, "OSA90") == 0) {
		target_color = XYZ_to_OSA90(intermediate_color);
	} else if (strcmp(target_format, "pOSA74") == 0) {
		target_color = OSA74_to_pOSA74(XYZ_to_OSA74(intermediate_color));
	} else if (strcmp(target_format, "polarOSA74") == 0) {
		target_color = cartesian_to_polar(XYZ_to_OSA74(intermediate_color));
	} else if (strcmp(target_format, "sRGB") == 0) {
		target_color = XYZ_to_sRGB(intermediate_color);
	} else if (strcmp(target_format, "XYZ") == 0) {
		target_color = intermediate_color;
	} else {
		fprintf(stderr, "Invalid target format.");
		return EXIT_FAILURE;
	}
	if (errno == ERANGE) {
		fprintf(stderr, "The requested color lies outside of the %s gamut.\n", target_format);
		/*return EXIT_FAILURE;*/
		/*printf("%d %d %d\n", 149, 149, 148);*/
		return EXIT_SUCCESS;
	}

	/* round RGB values to integers */
	if (strcmp(target_format, "sRGB") == 0) {
		/*printf("%.0f %.0f %.0f\n", target_color.value[0], target_color.value[1], target_color.value[2]);*/
		printf("#%06x\n", RGB_to_hex(target_color));
	} else {
		printf("%.4f %.4f %.4f\n", target_color.value[0], target_color.value[1], target_color.value[2]);
	}

	return EXIT_SUCCESS;
}


#include <gsl/gsl_sf_erf.h>
#include <gsl/gsl_cdf.h>
#include <stdio.h>

void evaluate(double x) {
  double probability = gsl_sf_erf(x);

  printf(
    "The probability that Normal(0, 1) random variable has a value "
    "between %g and %g is: %g\n", -x, x, probability
  );

  double area = 1 - 2 * gsl_cdf_gaussian_P(-x, 1);

  printf(
    "The integral of a Normal(0, 1) distribution "
    "between %g and %g is: %g\n", -x, x, area
  );
}

int main() {
  evaluate(0.25);
  evaluate(1.96);
}


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

int main(int argc, char *argv[]) {
    double mu = 1.0;
    if (argc > 1)
        mu = atof(argv[1]);
    int n = 1;
    if (argc > 2)
        n = atoi(argv[2]);
    unsigned long seed;
    if (argc > 3)
        seed = atol(argv[3]);
    else
        seed = time(NULL);
    gsl_rng_env_setup();
    gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
    gsl_rng_set(rng, seed);
    for (int i = 0; i < n; i++) {
        double x = gsl_ran_exponential(rng, mu);
        printf("%.12f\n", x);
    }
    gsl_rng_free(rng);
    return EXIT_SUCCESS;
}


/**
 * Finite element method solver. This solver solves laplace(u) = 6 on square
 * [0, 1]x[0, 1]. Boundary conditions are set to be 1 + x^2 + 2y^2.
 *
 * Exact solution of this problem is 1 + x^2 + 2y^2. We will test against this
 * solution in the end.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#include <lapacke.h>
#include <triangle.h>

#define F -6.0

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

static void compute_local_a(uint *point_id, REAL *pointlist, REAL *matrix);
static void compute_local_f(uint *point_id, REAL *pointlist, REAL *matrix);
static void generate_mesh(struct triangulateio *io, struct triangulateio *out);
static REAL boundary_condition(uint point_id, struct triangulateio *out);
static REAL exact_solution(uint point_id, struct triangulateio *out);

int main(int argc, char **argv)
{
    struct triangulateio in, out;
    uint num_of_points;
    uint num_of_triangles;
    REAL *local_A, *local_F, *global_A, *global_F;

    /* generate geometry */
    generate_mesh(&in, &out);

    num_of_points = out.numberofpoints;
    num_of_triangles = out.numberoftriangles;

    /**
     * Allocate all matrix data structures
     */
    global_A = malloc(num_of_points * num_of_points * sizeof(REAL));
    global_F = malloc(num_of_points * sizeof(REAL));
    local_A = malloc(3 * 3 * sizeof(REAL));
    local_F = malloc(3 * 3 * sizeof(REAL));

    assert(global_A != NULL);
    assert(global_F != NULL);
    assert(local_A != NULL);
    assert(local_F != NULL);

    memset(global_A, 0, num_of_points * num_of_points * sizeof(REAL));
    memset(global_F, 0, num_of_points * sizeof(REAL));

    /**
     * Matrix assembly without boundary conditions
     */
    for (size_t n = 0; n < num_of_triangles; n++) {
        // three points that describe triangle
        uint point_id[3];
        memcpy(point_id, &out.trianglelist[n * 3], 3 * sizeof(int));
        compute_local_a(point_id, out.pointlist, local_A);
        compute_local_f(point_id, out.pointlist, local_F);

        for (size_t i = 0; i < 3; i++) {
            int i1 = point_id[i];
            for (int j = 0; j < 3; j++) {
                int j1 = point_id[j];
                global_A[i1 * num_of_points + j1] += local_A[i * 3 + j];
                global_F[i1] += local_F[i * 3 + j] * F;
            }
        }
    }

    for (size_t i = 0; i < num_of_points; i++) {
        if (out.pointmarkerlist[i] == 1) {
            for (size_t j = 0; j < num_of_points; j++) {
                global_F[j] -= global_A[j * num_of_points + i]
                    * boundary_condition(i, &out);
                global_A[i * num_of_points + j] = 0;
                global_A[j * num_of_points + i] = 0;
            }
            global_A[i * num_of_points + i] = 1.0;
            global_F[i] = boundary_condition(i, &out);
        }
    }

    /*************************************************************************
     ************************ Solve Ax = b ***********************************
     *************************************************************************/
    int ipiv[num_of_points];
    int info = LAPACKE_dgesv(LAPACK_ROW_MAJOR,
                             num_of_points,
                             1,
                             global_A,
                             num_of_points,
                             ipiv,
                             global_F,
                             1);
    if (info > 0) {
        printf("The factorization has been completed, but the factor U is\n");
        printf("exactly singular, so the solution could not be computed.");
        goto mem_free;
    } else if (info < 0) {
        printf("%d-th argument had an illegal value\n", -info);
        goto mem_free;
    }

    //TODO: implement max error
    printf("\nSolution in point vs exact solution\n");
    for (size_t i = 0; i < num_of_points; i++) {
        double temp = exact_solution(i, &out);
        printf(
            "%lf  \t  %lf  \t  %lf\n", temp, global_F[i], temp - global_F[i]);
    }

mem_free:
    /* free all Triangle data structures */
    free(out.edgelist);
    free(out.edgemarkerlist);
    free(out.neighborlist);
    free(out.pointlist);
    free(out.trianglelist);
    free(out.segmentlist);
    free(out.segmentmarkerlist);
    free(out.pointmarkerlist);

    /* free all input data structures */
    free(in.regionlist);
    free(in.pointlist);

    /* free all matrix arrays */
    free(global_A);
    free(local_A);
    free(global_F);
    free(local_F);

    return info;
}

static void generate_mesh(struct triangulateio *in, struct triangulateio *out)
{
    in->numberofpoints = 4;
    in->numberofpointattributes = 0;
    in->pointattributelist = (REAL *) NULL;
    in->pointlist = malloc(in->numberofpoints * 2 * sizeof(REAL));

    in->pointlist[0] = 0.0;
    in->pointlist[1] = 0.0;

    in->pointlist[2] = 0.0;
    in->pointlist[3] = 1.0;

    in->pointlist[4] = 1.0;
    in->pointlist[5] = 1.0;

    in->pointlist[6] = 1.0;
    in->pointlist[7] = 0.0;

    in->pointmarkerlist = (int *) NULL;

    in->numberofsegments = 0;
    in->numberofholes = 0;
    in->numberofregions = 1;
    in->regionlist = malloc(in->numberofregions * 4 * sizeof(REAL));
    in->regionlist[0] = 0.5;
    in->regionlist[1] = 0.5;
    in->regionlist[2] = 7.0; /* Regional attribute (for whole mesh). */
    in->regionlist[3] = 0.1; /* Area constraint. */

    out->pointlist = (REAL *) NULL;
    out->pointmarkerlist = (int *) NULL;
    out->trianglelist = (int *) NULL;
    out->triangleattributelist = (REAL *) NULL;
    out->neighborlist = (int *) NULL;
    out->segmentlist = (int *) NULL;
    out->segmentmarkerlist = (int *) NULL;
    out->edgelist = (int *) NULL;
    out->edgemarkerlist = (int *) NULL;

    /* Triangulate the points. read and write a PSLG (p), preserve the
     * convex hull (c), zero index (z), produce edge list (e), produce
     * neighbor list (n), be quiet (Q), area should be less than 0.1 (a.1)
     * and generate triangle for FEM (q).
     */
    triangulate("pzceQna.1q", in, out, (struct triangulateio *) NULL);
}

static void compute_local_a(uint *point_id, REAL *pointlist, REAL *matrix)
{
        REAL dx23 = pointlist[2 * point_id[1]] - pointlist[2 * point_id[2]];
        REAL dx31 = pointlist[2 * point_id[2]] - pointlist[2 * point_id[0]];
        REAL dx12 = pointlist[2 * point_id[0]] - pointlist[2 * point_id[1]];
        REAL dy23 = pointlist[2 * point_id[1] + 1] - pointlist[2 * point_id[2] + 1];
        REAL dy31 = pointlist[2 * point_id[2] + 1] - pointlist[2 * point_id[0] + 1];
        REAL dy12 = pointlist[2 * point_id[0] + 1] - pointlist[2 * point_id[1] + 1];;

        REAL area = 0.5 * (dx31 * dy12 - dy31 * dx12);
        REAL _tmp_mult = 0.25 / area;
        matrix[0] = _tmp_mult * (dx23 * dx23 + dy23 * dy23);
        matrix[1] = _tmp_mult * (dx23 * dx31 + dy23 * dy31);
        matrix[2] = _tmp_mult * (dx23 * dx12 + dy23 * dy12);
        matrix[3] = _tmp_mult * (dx31 * dx23 + dy31 * dy23);
        matrix[4] = _tmp_mult * (dx31 * dx31 + dy31 * dy31);
        matrix[5] = _tmp_mult * (dx31 * dx12 + dy31 * dy12);
        matrix[6] = _tmp_mult * (dx12 * dx23 + dy12 * dy23);
        matrix[7] = _tmp_mult * (dx12 * dx31 + dy12 * dy31);
        matrix[8] = _tmp_mult * (dx12 * dx12 + dy12 * dy12);
}

static inline void compute_local_f(uint *point_id, REAL *pointlist, REAL *matrix)
{
        REAL dx31 = pointlist[2 * point_id[2]] - pointlist[2 * point_id[0]];
        REAL dx12 = pointlist[2 * point_id[0]] - pointlist[2 * point_id[1]];
        REAL dy31 = pointlist[2 * point_id[2] + 1] - pointlist[2 * point_id[0] + 1];
        REAL dy12 = pointlist[2 * point_id[0] + 1] - pointlist[2 * point_id[1] + 1];

        REAL area = 0.5 * (dx31 * dy12 - dy31 * dx12);
        REAL c_diag = area / 6.0;
        REAL c_off = area / 12.0;

        matrix[0] = c_diag; matrix[1] = c_off;  matrix[2] = c_off;
        matrix[3] = c_off;  matrix[4] = c_diag; matrix[5] = c_off;
        matrix[6] = c_off;  matrix[7] = c_off;  matrix[8] = c_diag;
}

static inline REAL exact_solution(uint point_id, struct triangulateio *out)
{
    REAL x = out->pointlist[2 * point_id];
    REAL y = out->pointlist[2 * point_id + 1];
    return (1.0 + x * x + 2.0 * y * y);
}

static inline REAL boundary_condition(uint point_id, struct triangulateio *out)
{
    REAL x = out->pointlist[2 * point_id];
    REAL y = out->pointlist[2 * point_id + 1];
    return (1.0 + x * x + 2.0 * y * y);
}


#include <math.h>
#include <stdio.h>

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

#include <argtable2.h>

#include "qdm.h"

void
qdm_mspline_vector(
    gsl_vector *result,
    const double tau,
    const size_t spline_df,
    const gsl_vector *knots
)
{
  size_t bin = qdm_vector_search(knots, tau);

  gsl_vector_set(result, 0, 0);

  for (size_t m = 0; m < result->size - 1; m++) {
    double v;

    if (bin < m) {
      v = 0;
    } else if (bin - spline_df + 1 > m) {
      v = 0;
    } else if (bin == m) {
      double n = 3 * gsl_sf_pow_int(tau - gsl_vector_get(knots, m), 2);
      double d1 = gsl_vector_get(knots, m + 1) - gsl_vector_get(knots, m);
      double d2 = gsl_vector_get(knots, m + 2) - gsl_vector_get(knots, m);
      double d3 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m);

      v = n / (d1 * d2 * d3);
    } else if (bin == m + 1) {
      double i1 = 1 / (gsl_vector_get(knots, m + 2) - gsl_vector_get(knots, m + 1));

      double i2n = gsl_sf_pow_int(tau - gsl_vector_get(knots, m), 2);
      double i2d1 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m);
      double i2d2 = gsl_vector_get(knots, m + 2) - gsl_vector_get(knots, m);
      double i2d3 = gsl_vector_get(knots, m + 2) - gsl_vector_get(knots, m + 1);
      double i2 = i2n / (i2d1 * i2d2 * i2d3);

      double i3n = gsl_sf_pow_int(gsl_vector_get(knots, m + 3) - tau, 2);
      double i3d1 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m);
      double i3d2 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m + 1);
      double i3d3 = gsl_vector_get(knots, m + 2) - gsl_vector_get(knots, m + 1);
      double i3 = i3n / (i3d1 * i3d2 * i3d3);

      v = 3 * (i1 - i2 - i3);
    } else {
      double n = 3 * gsl_sf_pow_int(gsl_vector_get(knots, m + 3) - tau, 2);
      double d1 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m);
      double d2 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m + 1);
      double d3 = gsl_vector_get(knots, m + 3) - gsl_vector_get(knots, m + 2);
      double d = d1 * d2 * d3;

      v = n / d;
    }

    gsl_vector_set(result, m + 1, v);
  }
}

void
qdm_mspline_matrix(gsl_matrix *result, const size_t spline_df, const gsl_vector *x, const gsl_vector *knots)
{
  for (size_t i = 0; i < result->size1; i++) {
    gsl_vector_view row = gsl_matrix_row(result, i);
    qdm_mspline_vector(&row.vector, gsl_vector_get(x, i), spline_df, knots);
  }
}


/*
 *  mixmax.c
 *  MIXMAX, A Pseudo-Random Number Generator
 *
 *  Copyright Konstantin Savvidy.
 *
 *  Free to use, academic or commercial. Do not redistribute without permission.
 *
 *	G.K.Savvidy and N.G.Ter-Arutyunian,
 *  On the Monte Carlo simulation of physical systems,
 *	J.Comput.Phys. 97, 566 (1991);
 *  Preprint EPI-865-16-86, Yerevan, Jan. 1986
 *
 *  K.Savvidy
 *  The MIXMAX random number generator
 *  Comp. Phys. Commun. 196 (2015), pp 161–165
 *  http://dx.doi.org/10.1016/j.cpc.2015.06.003
 *
 */

#include <stdio.h>
#include <stdint.h>

#ifndef MIXMAX_H_
#define MIXMAX_H_

#define USE_INLINE_ASM

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _N
#define N 240
/* The currently recommended generator is the three-parameter MIXMAX with
N=240, s=487013230256099140, m=2^51+1

   Other newly recommended N are N=8, 17 and 240,
   as well as the ordinary MIXMAX with N=256 and s=487013230256099064

   Since the algorithm is linear in N, the cost per number is almost independent of N.
 */
#else
#define N _N
#endif

#ifndef __LP64__
typedef uint64_t myuint;
//#warning but no problem,  'myuint' is 'uint64_t'
#else
typedef unsigned long long int myuint;
//#warning but no problem,  'myuint' is 'unsigned long long int'
#endif

struct rng_state_st
{
    myuint V[N];
    myuint sumtot;
    int counter;
    FILE* fh;
};

typedef struct rng_state_st rng_state_t; // C struct alias

int  rng_get_N(void); // get the N programmatically, useful for checking the value for which the library was compiled

rng_state_t  *rng_alloc();                 /* allocate the state */
int           rng_free(rng_state_t* X);    /* free memory occupied by the state */
rng_state_t  *rng_copy(myuint *Y);         /* init from vector, takes the vector Y,
                                               returns pointer to the newly allocated and initialized state */
void read_state(rng_state_t* X, const char filename[] );
void print_state(rng_state_t* X);
    int iterate(rng_state_t* X);
    myuint iterate_raw_vec(myuint* Y, myuint sumtotOld);

//   FUNCTIONS FOR SEEDING

typedef uint32_t myID_t;

void seed_uniquestream(rng_state_t* X, myID_t clusterID, myID_t machineID, myID_t runID, myID_t  streamID );
/*
 best choice: will make a state vector from which you can get at least 10^100 numbers
 guaranteed mathematically to be non-colliding with any other stream prepared from another set of 32bit IDs,
 so long as it is different by at least one bit in at least one of the four IDs
			-- useful if you are running a parallel simulation with many clusters, many CPUs each
 */

void seed_spbox(rng_state_t* X, myuint seed);    // non-linear method, makes certified unique vectors,  probability for streams to collide is < 1/10^4600

void seed_vielbein(rng_state_t* X, unsigned int i); // seeds with the i-th unit vector, i = 0..N-1,  for testing only

//   FUNCTIONS FOR GETTING RANDOM NUMBERS

#ifdef __MIXMAX_C
	myuint get_next(rng_state_t* X);         // returns 64-bit int, which is between 1 and 2^61-1 inclusive
	double get_next_float(rng_state_t* X);   // returns double precision floating point number in (0,1]
#endif  //__MIXMAX_C

void fill_array(rng_state_t* X, unsigned int n, double *array); // fastest method: set n to a multiple of N (e.g. n=256)

void iterate_and_fill_array(rng_state_t* X, double *array); // fills the array with N numbers

myuint precalc(rng_state_t* X);
/* needed if the state has been changed by something other than  iterate, but no worries, seeding functions call this for you when necessary */
myuint apply_bigskip(myuint* Vout, myuint* Vin, myID_t clusterID, myID_t machineID, myID_t runID, myID_t  streamID );
// applies a skip of some number of steps calculated from the four IDs
void branch_inplace( rng_state_t* Xin, myID_t* ID ); // almost the same as apply_bigskip, but in-place and from a vector of IDs

#define BITS  61

/* magic with Mersenne Numbers */

#define M61   2305843009213693951ULL

    myuint modadd(myuint foo, myuint bar);
    myuint modmulM61(myuint s, myuint a);
    myuint fmodmulM61(myuint cum, myuint s, myuint a);

#define MERSBASE M61 //xSUFF(M61)
#define MOD_PAYNE(k) ((((k)) & MERSBASE) + (((k)) >> BITS) )  // slightly faster than my old way, ok for addition
#define MOD_REM(k) ((k) % MERSBASE )  // latest Intel CPU is supposed to do this in one CPU cycle, but on my machines it seems to be 20% slower than the best tricks
#define MOD_MERSENNE(k) MOD_PAYNE(k)

//#define INV_MERSBASE (0x1p-61)
#define INV_MERSBASE (0.4336808689942017736029811203479766845703E-18)
//const double INV_MERSBASE=(0.4336808689942017736029811203479766845703E-18); // gives "duplicate symbol" error

// the charpoly is irreducible for the combinations of N and SPECIAL

#if (N==256)
#define SPECIALMUL 0
#define SPECIAL 487013230256099064 // s=487013230256099064, m=1 -- good old MIXMAX
#define MOD_MULSPEC(k) fmodmulM61( 0, SPECIAL , (k) )

#elif (N==8)
#define SPECIALMUL 53 // m=2^53+1
#define SPECIAL 0

#elif (N==17)
#define SPECIALMUL 36 // m=2^36+1, other valid possibilities are m=2^13+1, m=2^19+1, m=2^24+1
#define SPECIAL 0

#elif (N==40)
#define SPECIALMUL 42 // m=2^42+1
#define SPECIAL 0

#elif (N==60)
#define SPECIALMUL 52 // m=2^52+1
#define SPECIAL 0

#elif (N==96)
#define SPECIALMUL 55 // m=2^55+1
#define SPECIAL 0

#elif (N==120)
#define SPECIALMUL 51   // m=2^51+1 and a SPECIAL=+1 (!!!)
#define SPECIAL 1
#define MOD_MULSPEC(k) (k)

#elif (N==240)
#define SPECIALMUL 51   // m=2^51+1 and a SPECIAL=487013230256099140
#define SPECIAL 487013230256099140ULL
#define MOD_MULSPEC(k) fmodmulM61( 0, SPECIAL , (k) )

#elif (N==44851)
#define SPECIALMUL 0
#define SPECIAL -3
#define MOD_MULSPEC(k) MOD_MERSENNE(3*(MERSBASE-(k)))

#else
#warning Not a verified N, you are on your own!
#define SPECIALMUL 58
#define SPECIAL 0

#endif // list of interesting N for modulus M61 ends here

        inline 	myuint get_next_inlined(rng_state_t* X) { // forward output
            int i;
            i=X->counter;

            if (i<=(N-1) ){
                X->counter++;
                return X->V[i];
            }else{
                X->sumtot = iterate_raw_vec(X->V, X->sumtot);
                X->counter=2;
                return X->V[1];
            }
        }

//    inline 	myuint get_next_inlined(rng_state_t* X) { // backward output, slightly faster
//        int i;
//        i=X->counter;
//
//        if ( i!=0 ){
//            X->counter--;
//            return X->V[i];
//        }else{
//            X->sumtot = iterate_raw_vec(X->V, X->sumtot);
//            X->counter=N-2;
//            return X->V[N-1];
//        }
//    }

    inline double get_next_float_inlined(rng_state_t* X){
        /* cast to signed int trick suggested by Andrzej Görlich     */
        int64_t Z=(int64_t)get_next_inlined(X);
        double F;
#if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__ICC)) && defined(__x86_64__) && defined(__SSE2_MATH__) && defined(USE_INLINE_ASM)
#warning Using the inline assembler
        /* using SSE inline assemly to zero the xmm register, just before int64 -> double conversion,
         not really necessary in GCC-5 or better, but huge penalty on earlier compilers
         */
        __asm__  __volatile__("pxor %0, %0; "
                              :"=x"(F)
                              );
#endif
        F=Z;
        return F*INV_MERSBASE;
    }

#ifndef __MIXMAX_C // c++ can put code into header files, why cant we? (with the inline declaration, should be safe from duplicate-symbol error)

#define get_next(X) get_next_inlined(X)
#define get_next_float(X) get_next_float_inlined(X) // get_next_float_packbits(X) //

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
    inline double convert1double(uint64_t u){
        const double one = 1;
        const uint64_t onemask = *(uint64_t*)&one;
        uint64_t tmp = (u>>9) | onemask; // must not have bits higher than 52 !
        double d = *(double*)&tmp;
        return d-1.0;
    }

    inline double get_next_float_packbits(rng_state_t* X){
        uint64_t Z=get_next_inlined(X); // must not have bits higher than 52 !
        return convert1double(Z);
    }
#pragma GCC diagnostic pop

#endif // __MIXMAX_C

// ERROR CODES - exit() is called with these
#define ARRAY_INDEX_OUT_OF_BOUNDS   0xFF01
#define SEED_WAS_ZERO               0xFF02
#define ERROR_READING_STATE_FILE    0xFF03
#define ERROR_READING_STATE_COUNTER       0xFF04
#define ERROR_READING_STATE_CHECKSUM      0xFF05

#ifdef __cplusplus
}
#endif

//#define HOOKUP_GSL 1

#ifdef HOOKUP_GSL // if you need to use mixmax through GSL, pass -DHOOKUP_GSL=1 to the compiler
#ifndef __MIXMAX_C

#include <gsl/gsl_rng.h>
unsigned long gsl_get_next(void *vstate);
double gsl_get_next_float(void *vstate);
void seed_for_gsl(void *vstate, unsigned long seed);

static const gsl_rng_type mixmax_type =
{"MIXMAX",                        /* name */
    MERSBASE,                         /* RAND_MAX */
    1,                                /* RAND_MIN */
    sizeof (rng_state_t),
    &seed_for_gsl,
    &gsl_get_next,
    &gsl_get_next_float
};

unsigned long gsl_get_next(void *vstate) {
    rng_state_t* X= (rng_state_t*)vstate;
    return (unsigned long)get_next(X);
}

double gsl_get_next_float(void *vstate) {
    rng_state_t* X= (rng_state_t*)vstate;
    return ( (double)get_next(X)) * INV_MERSBASE;
}

void seed_for_gsl(void *vstate, unsigned long seed){
    rng_state_t* X= (rng_state_t*)vstate;
    seed_spbox(X,(myuint)seed);
}

const gsl_rng_type *gsl_rng_mixmax = &mixmax_type;

#endif // HOOKUP_GSL
#endif // not inside __MIXMAX_C
#endif // closing MIXMAX_H_


#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
#define NMAX 100

double f (double x, void *params);
double f (double x, void *params)
{
    double n = *(double *) params;
    double f = exp (-x) * pow (x, n);

    return f;
}
void integralGen (int nmin, int nmax, double vals[]);
void integralGen (int nmin, int nmax, double vals[])
{
    double result, error;
    double a = 0., b = 1.;
    double abserr = 1.e-9, relerr = 1.e-9;
    double n;
    size_t np = 1000;
    gsl_integration_workspace *w = gsl_integration_workspace_alloc (np);

    gsl_function F;

    F.function = &f;

    for (int i = nmin; i <= nmax; i++)
    {
        n = (double) i;
        F.params = &n;

        gsl_integration_qag (&F, a, b, abserr, relerr, np, GSL_INTEG_GAUSS15,
            w, &result, &error);
        vals[i] = result;

    }

    gsl_integration_workspace_free (w);
}