#ifdef newc
#include <assert.h>
#include <ctype.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <strstream>
#include <cmath>
#include <cstdio>
#include <complex>
using namespace std;
#else
#include <assert.h>
#include <ctype.h>
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <complex.h>
#endif

#include "TwoPunctures.h"

TwoPunctures::TwoPunctures(double mp, double mm, double b,
                           double P_plusx, double P_plusy, double P_plusz,
                           double S_plusx, double S_plusy, double S_plusz,
                           double P_minusx, double P_minusy, double P_minusz,
                           double S_minusx, double S_minusy, double S_minusz,
                           int nA, int nB, int nphi,
                           double Mp, double Mm, double admtol, double Newtontol,
                           int Newtonmaxit) : par_m_plus(mp), par_m_minus(mm), par_b(b), npoints_A(nA),
                                              npoints_B(nB), npoints_phi(nphi), target_M_plus(Mp), target_M_minus(Mm),
                                              adm_tol(admtol), Newton_tol(Newtontol), Newton_maxit(Newtonmaxit)
{
  par_P_plus[0] = P_plusx;
  par_P_plus[1] = P_plusy;
  par_P_plus[2] = P_plusz;
  par_P_minus[0] = P_minusx;
  par_P_minus[1] = P_minusy;
  par_P_minus[2] = P_minusz;
  par_S_plus[0] = S_plusx;
  par_S_plus[1] = S_plusy;
  par_S_plus[2] = S_plusz;
  par_S_minus[0] = S_minusx;
  par_S_minus[1] = S_minusy;
  par_S_minus[2] = S_minusz;

  int const nvar = 1, n1 = npoints_A, n2 = npoints_B, n3 = npoints_phi;

  ntotal = n1 * n2 * n3 * nvar;

  F = dvector(0, ntotal - 1);
  allocate_derivs(&u, ntotal);
  allocate_derivs(&v, ntotal);
}

TwoPunctures::~TwoPunctures()
{
  free_dvector(F, 0, ntotal - 1);
  free_derivs(&u, ntotal);
  free_derivs(&v, ntotal);
}

void TwoPunctures::Solve()
{

  double mp = par_m_plus;
  double mm = par_m_minus;

  enum GRID_SETUP_METHOD
  {
    GSM_Taylor_expansion,
    GSM_evaluation
  };
  enum GRID_SETUP_METHOD gsm;

  int antisymmetric_lapse, averaged_lapse, pmn_lapse, brownsville_lapse;

  int const nvar = 1, n1 = npoints_A, n2 = npoints_B, n3 = npoints_phi;

  int imin[3], imax[3];
  int const ntotal = n1 * n2 * n3 * nvar;

  // double admMass;

  /* initialise to 0 */
  for (int j = 0; j < ntotal; j++)
  {
    v.d0[j] = 0.0;
    v.d1[j] = 0.0;
    v.d2[j] = 0.0;
    v.d3[j] = 0.0;
    v.d11[j] = 0.0;
    v.d12[j] = 0.0;
    v.d13[j] = 0.0;
    v.d22[j] = 0.0;
    v.d23[j] = 0.0;
    v.d33[j] = 0.0;
  }

  double tmp, Mp_adm, Mm_adm, Mp_adm_err, Mm_adm_err, up, um;

  double M_p = target_M_plus;
  double M_m = target_M_minus;
  /* If bare masses are not given, iteratively solve for them given the
     target ADM masses target_M_plus and target_M_minus and with initial
     guesses given by par_m_plus and par_m_minus. */
  if (par_m_plus < 0 || par_m_minus < 0)
  {

    par_m_plus = target_M_plus;
    par_m_minus = target_M_minus;
    cout << "Attempting to find bare masses." << endl;
    cout << "Target ADM masses: M_p=" << M_p << " and M_m=" << M_m << endl;
    cout << "ADM mass tolerance: " << adm_tol << endl;

    /* Loop until both ADM masses are within adm_tol of their target */
    do
    {
      cout << "Bare masses: mp=" << mp << ", mm=" << mm << endl;
      Newton(nvar, n1, n2, n3, v, Newton_tol, 1);

      F_of_v(nvar, n1, n2, n3, v, F, u);

      up = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v, par_b, 0., 0.);
      um = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v, -par_b, 0., 0.);

      /* Calculate the ADM masses from the current bare mass guess PRD 70, 064011 (2004) Eq.(83)*/
      Mp_adm = (1 + up) * mp + mp * mm / (4. * par_b);
      Mm_adm = (1 + um) * mm + mp * mm / (4. * par_b);

      /* Check how far the current ADM masses are from the target */
      Mp_adm_err = fabs(M_p - Mp_adm);
      Mm_adm_err = fabs(M_m - Mm_adm);
      cout << "ADM mass error: M_p_err=" << Mp_adm_err << ", M_m_err=" << Mm_adm_err << endl;

      /* Invert the ADM mass equation and update the bare mass guess so that
         it gives the correct target ADM masses */
      tmp = -4 * par_b * (1 + um + up + um * up) +
            sqrt(16 * par_b * M_m * (1 + um) * (1 + up) +
                 pow(-M_m + M_p + 4 * par_b * (1 + um) * (1 + up), 2));
      par_m_plus = mp = (tmp + M_p - M_m) / (2. * (1 + up));
      par_m_minus = mm = (tmp - M_p + M_m) / (2. * (1 + um));

    } while ((Mp_adm_err > adm_tol) ||
             (Mm_adm_err > adm_tol));

    cout << "Found bare masses resulted Mp = " << Mp_adm << " and Mm = " << Mm_adm << endl;
  }

  Newton(nvar, n1, n2, n3, v, Newton_tol, Newton_maxit);

  F_of_v(nvar, n1, n2, n3, v, F, u);

  up = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v, par_b, 0., 0.);
  um = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v, -par_b, 0., 0.);

  /* Calculate the ADM masses from the current bare mass guess PRD 70, 064011 (2004) Eq.(83)*/
  Mp_adm = (1 + up) * mp + mp * mm / (4. * par_b);
  Mm_adm = (1 + um) * mm + mp * mm / (4. * par_b);

  cout << "The two puncture masses are mp = " << mp << " and mm = " << mm << endl;
  cout << "                   resulted Mp = " << Mp_adm << " and Mm = " << Mm_adm << endl;

  /* print out ADM mass, eq.: \Delta M_ADM=2*r*u=4*b*V for A=1,B=0,phi=0 PRD 70, 064011 (2004) Eq.(81)*/
  admMass = (mp + mm - 4 * par_b * PunctEvalAtArbitPosition(v.d0, 0, 1, 0, 0, nvar, n1, n2, n3));
  cout << "The total ADM mass is " << admMass << endl;

  target_M_plus = Mp_adm;
  target_M_minus = Mm_adm;
}
void TwoPunctures::Save(char *fname)
{
  ofstream outfile;
  outfile.open(fname, ios::trunc);

  time_t tnow;
  time(&tnow);
  struct tm *loc_time;
  loc_time = localtime(&tnow);
  outfile << "#File created on " << asctime(loc_time);
  outfile << "#Newton_tol = " << Newton_tol << endl;
  outfile << "#Mp         = " << target_M_plus << endl;
  outfile << "#Mm         = " << target_M_minus << endl;
  double D = 2 * par_b, x1, x2;
  x1 = D * target_M_minus / (target_M_plus + target_M_minus);
  x2 = -D * target_M_plus / (target_M_plus + target_M_minus);
  // in order to relate Brugmann's convention, rotate xy
  outfile << "bhmass1      = " << par_m_plus << endl;
  outfile << "bhx1         = " << 0 << endl;
  outfile << "bhy1         = " << x1 << endl;
  outfile << "bhz1         = " << 0 << endl;
  outfile << "bhpx1        = " << -par_P_plus[1] << endl;
  outfile << "bhpy1        = " << par_P_plus[0] << endl;
  outfile << "bhpz1        = " << par_P_plus[2] << endl;
  outfile << "bhsx1        = " << -par_S_plus[1] << endl;
  outfile << "bhsy1        = " << par_S_plus[0] << endl;
  outfile << "bhsz1        = " << par_S_plus[2] << endl;
  outfile << "bhmass2      = " << par_m_minus << endl;
  outfile << "bhx2         = " << 0 << endl;
  outfile << "bhy2         = " << x2 << endl;
  outfile << "bhz2         = " << 0 << endl;
  outfile << "bhpx2        = " << -par_P_minus[1] << endl;
  outfile << "bhpy2        = " << par_P_minus[0] << endl;
  outfile << "bhpz2        = " << par_P_minus[2] << endl;
  outfile << "bhsx2        = " << -par_S_minus[1] << endl;
  outfile << "bhsy2        = " << par_S_minus[0] << endl;
  outfile << "bhsz2        = " << par_S_minus[2] << endl;
  int const n1 = npoints_A, n2 = npoints_B, n3 = npoints_phi;
  outfile << "data " << n1 << " " << n2 << " " << n3 << endl;
  int ntotal = n1 * n2 * n3;

  outfile.setf(ios::scientific, ios::floatfield);
  outfile.precision(16);
  for (int i = 0; i < ntotal; i++)
    outfile << v.d0[i] << endl;

  outfile.close();

  // 增加输出来方便 python 读取 puncture 数据
  // 小曲  2024/12/04

  ofstream outfile2;
  outfile2.open("puncture_parameters_new.txt", ios::trunc);

  // 注意在这个程序中 xy 平面发生了偏转
  outfile2 << setw(18) << setprecision(10) << par_m_plus
           << setw(18) << setprecision(10) << target_M_plus
           << setw(18) << setprecision(10) << admMass << " # bare mass 1   mass 1   ADM mass" << endl;
  outfile2 << setw(18) << setprecision(10) << 0.0
           << setw(18) << setprecision(10) << x1
           << setw(18) << setprecision(10) << 0.0 << " # position 1" << endl;
  outfile2 << setw(18) << setprecision(10) << -par_P_plus[1]
           << setw(18) << setprecision(10) << par_P_plus[0]
           << setw(18) << setprecision(10) << par_P_plus[2] << " # momentum 1" << endl;
  outfile2 << setw(18) << setprecision(10) << -par_S_plus[1]
           << setw(18) << setprecision(10) << par_S_plus[0]
           << setw(18) << setprecision(10) << par_S_plus[2] << " # angular mumentum 1" << endl;
  outfile2 << setw(18) << setprecision(10) << par_m_minus
           << setw(18) << setprecision(10) << target_M_minus
           << setw(18) << setprecision(10) << admMass << " # bare mass 2   mass 2   ADM mass" << endl;
  outfile2 << setw(18) << setprecision(10) << 0.0
           << setw(18) << setprecision(10) << x2
           << setw(18) << setprecision(10) << 0.0 << " # position 2" << endl;
  outfile2 << setw(18) << setprecision(10) << -par_P_minus[1]
           << setw(18) << setprecision(10) << par_P_minus[0]
           << setw(18) << setprecision(10) << par_P_minus[2] << " # momentum 2" << endl;
  outfile2 << setw(18) << setprecision(10) << -par_S_minus[1]
           << setw(18) << setprecision(10) << par_S_minus[0]
           << setw(18) << setprecision(10) << par_S_minus[2] << " # angular mumentum 2" << endl;

  outfile2.close();
}

void TwoPunctures::set_initial_guess(derivs v)
{

  int nvar = 1, n1 = npoints_A, n2 = npoints_B, n3 = npoints_phi;

  double *s_x, *s_y, *s_z; // Cartesian x,y,z
  double al, A, Am1, be, B, phi, R, r, X;
  int ivar, i, j, k, i3D, indx;
  derivs U;
  FILE *debug_file;

  s_x = (double *)calloc(n1 * n2 * n3, sizeof(double));
  s_y = (double *)calloc(n1 * n2 * n3, sizeof(double));
  s_z = (double *)calloc(n1 * n2 * n3, sizeof(double));
  allocate_derivs(&U, nvar);
  for (ivar = 0; ivar < nvar; ivar++)
    for (i = 0; i < n1; i++)
      for (j = 0; j < n2; j++)
        for (k = 0; k < n3; k++)
        {
          i3D = Index(ivar, i, j, k, 1, n1, n2, n3);

          al = Pih * (2 * i + 1) / n1;
          A = -cos(al);
          be = Pih * (2 * j + 1) / n2;
          B = -cos(be);
          phi = 2. * Pi * k / n3;

          /* Calculation of (X,R)*/
          AB_To_XR(nvar, A, B, &X, &R, U);
          /* Calculation of (x,r)*/
          C_To_c(nvar, X, R, &(s_x[i3D]), &r, U);
          /* Calculation of (y,z)*/
          rx3_To_xyz(nvar, s_x[i3D], r, phi, &(s_y[i3D]), &(s_z[i3D]), U);
        }
  //  Set_Initial_Guess_for_u(n1*n2*n3, v.d0, s_x, s_y, s_z);  //extern fortran code to set initial guess
  for (ivar = 0; ivar < nvar; ivar++)
    for (i = 0; i < n1; i++)
      for (j = 0; j < n2; j++)
        for (k = 0; k < n3; k++)
        {
          indx = Index(ivar, i, j, k, 1, n1, n2, n3);
          v.d0[indx] = 0;                                     // set initial guess 0
          v.d0[indx] /= (-cos(Pih * (2 * i + 1) / n1) - 1.0); // PRD 70, 064011 (2004) Eq.(5), from u to U
        }
  Derivatives_AB3(nvar, n1, n2, n3, v);
  if (0)
  {
    debug_file = fopen("initial.dat", "w");
    assert(debug_file);
    for (ivar = 0; ivar < nvar; ivar++)
      for (i = 0; i < n1; i++)
        for (j = 0; j < n2; j++)
        {
          al = Pih * (2 * i + 1) / n1;
          A = -cos(al);
          Am1 = A - 1.0;
          be = Pih * (2 * j + 1) / n2;
          B = -cos(be);
          phi = 0.0;
          indx = Index(ivar, i, j, 0, 1, n1, n2, n3);
          U.d0[0] = Am1 * v.d0[indx];                    /* U*/
          U.d1[0] = v.d0[indx] + Am1 * v.d1[indx];       /* U_A*/
          U.d2[0] = Am1 * v.d2[indx];                    /* U_B*/
          U.d3[0] = Am1 * v.d3[indx];                    /* U_3*/
          U.d11[0] = 2 * v.d1[indx] + Am1 * v.d11[indx]; /* U_AA*/
          U.d12[0] = v.d2[indx] + Am1 * v.d12[indx];     /* U_AB*/
          U.d13[0] = v.d3[indx] + Am1 * v.d13[indx];     /* U_AB*/
          U.d22[0] = Am1 * v.d22[indx];                  /* U_BB*/
          U.d23[0] = Am1 * v.d23[indx];                  /* U_B3*/
          U.d33[0] = Am1 * v.d33[indx];                  /* U_33*/
          /* Calculation of (X,R)*/
          AB_To_XR(nvar, A, B, &X, &R, U);
          /* Calculation of (x,r)*/
          C_To_c(nvar, X, R, &(s_x[indx]), &r, U);
          /* Calculation of (y,z)*/
          rx3_To_xyz(nvar, s_x[i3D], r, phi, &(s_y[indx]), &(s_z[indx]), U);
          fprintf(debug_file,
                  "%.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g "
                  "%.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g\n",
                  (double)s_x[indx], (double)s_y[indx],
                  (double)A, (double)B,
                  (double)U.d0[0],
                  (double)(-cos(Pih * (2 * i + 1) / n1) - 1.0),
                  (double)U.d1[0],
                  (double)U.d2[0],
                  (double)U.d3[0],
                  (double)U.d11[0],
                  (double)U.d22[0],
                  (double)U.d33[0],
                  (double)v.d0[indx],
                  (double)v.d1[indx],
                  (double)v.d2[indx],
                  (double)v.d3[indx],
                  (double)v.d11[indx],
                  (double)v.d22[indx],
                  (double)v.d33[indx]);
        }
    fprintf(debug_file, "\n\n");
    for (i = n2 - 10; i < n2; i++)
    {
      double d;
      indx = Index(0, 0, i, 0, 1, n1, n2, n3);
      d = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v,
                                     s_x[indx], 0.0, 0.0);
      fprintf(debug_file, "%.16g %.16g\n",
              (double)s_x[indx], (double)d);
    }
    fprintf(debug_file, "\n\n");
    for (i = n2 - 10; i < n2 - 1; i++)
    {
      double d;
      int ip = Index(0, 0, i + 1, 0, 1, n1, n2, n3);
      indx = Index(0, 0, i, 0, 1, n1, n2, n3);
      for (j = -10; j < 10; j++)
      {
        d = PunctIntPolAtArbitPosition(0, nvar, n1, n2, n3, v,
                                       s_x[indx] + (s_x[ip] - s_x[indx]) * j / 10,
                                       0.0, 0.0);
        fprintf(debug_file, "%.16g %.16g\n",
                (double)(s_x[indx] + (s_x[ip] - s_x[indx]) * j / 10), (double)d);
      }
    }
    fprintf(debug_file, "\n\n");
    for (i = 0; i < n1; i++)
      for (j = 0; j < n2; j++)
      {
        X = 2 * (2.0 * i / n1 - 1.0);
        R = 2 * (1.0 * j / n2);
        if (X * X + R * R > 1.0)
        {
          C_To_c(nvar, X, R, &(s_x[indx]), &r, U);
          rx3_To_xyz(nvar, s_x[i3D], r, phi, &(s_y[indx]), &(s_z[indx]), U);
          *U.d0 = s_x[indx] * s_x[indx];
          *U.d1 = 2 * s_x[indx];
          *U.d2 = 0.0;
          *U.d3 = 0.0;
          *U.d11 = 2.0;
          *U.d22 = 0.0;
          *U.d33 = *U.d12 = *U.d23 = *U.d13 = 0.0;
          C_To_c(nvar, X, R, &(s_x[indx]), &r, U);
          fprintf(debug_file,
                  "%.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g\n",
                  (double)s_x[indx], (double)r, (double)X, (double)R, (double)U.d0[0],
                  (double)U.d1[0],
                  (double)U.d2[0],
                  (double)U.d3[0],
                  (double)U.d11[0],
                  (double)U.d22[0],
                  (double)U.d33[0]);
        }
      }
    fclose(debug_file);
  }
  free(s_z);
  free(s_y);
  free(s_x);
  free_derivs(&U, nvar);
}

// some tools
/*---------------------------------------------------------------------------*/
int TwoPunctures::index(int i, int j, int k, int l, int a, int b, int c, int d)
{
  int rr = 0;
  rr = l + k * d + j * d * c + i * d * c * b;
  return rr;
}
/*---------------------------------------------------------------------------*/
int *TwoPunctures::ivector(long nl, long nh)
/* allocate an int vector with subscript range v[nl..nh] */
{
  int *retval;

  retval = (int *)malloc(sizeof(int) * (nh - nl + 1));
  if (retval == NULL)
    cout << "allocation failure in ivector()" << endl;

  return retval - nl;
}

/*---------------------------------------------------------------------------*/
double *TwoPunctures::dvector(long nl, long nh)
/* allocate a double vector with subscript range v[nl..nh] */
{
  double *retval;

  retval = (double *)malloc(sizeof(double) * (nh - nl + 1));
  if (retval == NULL)
    cout << "allocation failure in dvector()" << endl;

  return retval - nl;
}

/*---------------------------------------------------------------------------*/
int **TwoPunctures::imatrix(long nrl, long nrh, long ncl, long nch)
/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
{
  int **retval;

  retval = (int **)malloc(sizeof(int *) * (nrh - nrl + 1));
  if (retval == NULL)
    cout << "allocation failure (1) in imatrix()" << endl;

  /* get all memory for the matrix in on chunk */
  retval[0] = (int *)malloc(sizeof(int) * (nrh - nrl + 1) * (nch - ncl + 1));
  if (retval[0] == NULL)
    cout << "allocation failure (2) in imatrix()" << endl;

  /* apply column and row offsets */
  retval[0] -= ncl;
  retval -= nrl;

  /* slice chunk into rows */
  long width = (nch - ncl + 1);
  for (long i = nrl + 1; i <= nrh; i++)
    retval[i] = retval[i - 1] + width;
  assert(retval[nrh] - retval[nrl] == (nrh - nrl) * width);

  return retval;
}

/*---------------------------------------------------------------------------*/
double **TwoPunctures::dmatrix(long nrl, long nrh, long ncl, long nch)
/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
{
  double **retval;

  retval = (double **)malloc(sizeof(double *) * (nrh - nrl + 1));
  if (retval == NULL)
    cout << "allocation failure (1) in dmatrix()" << endl;

  /* get all memory for the matrix in on chunk */
  retval[0] = (double *)malloc(sizeof(double) * (nrh - nrl + 1) * (nch - ncl + 1));
  if (retval[0] == NULL)
    cout << "allocation failure (2) in dmatrix()" << endl;

  /* apply column and row offsets */
  retval[0] -= ncl;
  retval -= nrl;

  /* slice chunk into rows */
  long width = (nch - ncl + 1);
  for (long i = nrl + 1; i <= nrh; i++)
    retval[i] = retval[i - 1] + width;
  assert(retval[nrh] - retval[nrl] == (nrh - nrl) * width);

  return retval;
}

/*---------------------------------------------------------------------------*/
double ***TwoPunctures::d3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh)
/* allocate a double 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
{
  double ***retval;

  /* get memory for index structures */
  retval = (double ***)malloc(sizeof(double **) * (nrh - nrl + 1));
  if (retval == NULL)
    cout << "allocation failure (1) in dmatrix()" << endl;

  retval[0] = (double **)malloc(sizeof(double *) * (nrh - nrl + 1) * (nch - ncl + 1));
  if (retval[0] == NULL)
    cout << "allocation failure (2) in dmatrix()" << endl;

  /* get all memory for the tensor in on chunk */
  retval[0][0] = (double *)malloc(sizeof(double) * (nrh - nrl + 1) * (nch - ncl + 1) * (nrh - nrl + 1));
  if (retval[0][0] == NULL)
    cout << "allocation failure (3) in dmatrix()" << endl;

  /* apply all offsets */
  retval[0][0] -= ndl;
  retval[0] -= ncl;
  retval -= nrl;

  /* slice chunk into rows and columns */
  long width = (nch - ncl + 1);
  long depth = (ndh - ndl + 1);
  for (long j = ncl + 1; j <= nch; j++)
  { /* first row of columns */
    retval[nrl][j] = retval[nrl][j - 1] + depth;
  }
  assert(retval[nrl][nch] - retval[nrl][ncl] == (nch - ncl) * depth);
  for (long i = nrl + 1; i <= nrh; i++)
  {
    retval[i] = retval[i - 1] + width;
    retval[i][ncl] = retval[i - 1][ncl] + width * depth; /* first cell in column */
    for (long j = ncl + 1; j <= nch; j++)
    {
      retval[i][j] = retval[i][j - 1] + depth;
    }
    assert(retval[i][nch] - retval[i][ncl] == (nch - ncl) * depth);
  }
  assert(retval[nrh] - retval[nrl] == (nrh - nrl) * width);
  assert(&retval[nrh][nch][ndh] - &retval[nrl][ncl][ndl] == (nrh - nrl + 1) * (nch - ncl + 1) * (ndh - ndl + 1) - 1);

  return retval;
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::free_ivector(int *v, long nl, long nh)
/* free an int vector allocated with ivector() */
{
  free(v + nl);
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::free_dvector(double *v, long nl, long nh)
/* free an double vector allocated with dvector() */
{
  free(v + nl);
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::free_imatrix(int **m, long nrl, long nrh, long ncl, long nch)
/* free an int matrix allocated by imatrix() */
{
  free(m[nrl] + ncl);
  free(m + nrl);
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch)
/* free a double matrix allocated by dmatrix() */
{
  free(m[nrl] + ncl);
  free(m + nrl);
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::free_d3tensor(double ***t, long nrl, long nrh, long ncl, long nch,
                                 long ndl, long ndh)
/* free a double f3tensor allocated by f3tensor() */
{
  free(t[nrl][ncl] + ndl);
  free(t[nrl] + ncl);
  free(t + nrl);
}

/*--------------------------------------------------------------------------*/
int TwoPunctures::minimum2(int i, int j)
{
  int result = i;
  if (j < result)
    result = j;
  return result;
}

/*-------------------------------------------------------------------------*/
int TwoPunctures::minimum3(int i, int j, int k)
{
  int result = i;
  if (j < result)
    result = j;
  if (k < result)
    result = k;
  return result;
}

/*--------------------------------------------------------------------------*/
int TwoPunctures::maximum2(int i, int j)
{
  int result = i;
  if (j > result)
    result = j;
  return result;
}

/*--------------------------------------------------------------------------*/
int TwoPunctures::maximum3(int i, int j, int k)
{
  int result = i;
  if (j > result)
    result = j;
  if (k > result)
    result = k;
  return result;
}

/*--------------------------------------------------------------------------*/
int TwoPunctures::pow_int(int mantisse, int exponent)
{
  int i, result = 1;

  for (i = 1; i <= exponent; i++)
    result *= mantisse;

  return result;
}

/*--------------------------------------------------------------------------*/
void TwoPunctures::chebft_Zeros(double u[], int n, int inv)
/* eq. 5.8.7 and 5.8.8 at x = (5.8.4) of 2nd edition C++ NR */
{
  int k, j, isignum;
  double fac, sum, Pion, *c;

  c = dvector(0, n);
  Pion = Pi / n;
  if (inv == 0)
  {
    fac = 2.0 / n;
    isignum = 1;
    for (j = 0; j < n; j++)
    {
      sum = 0.0;
      for (k = 0; k < n; k++)
        sum += u[k] * cos(Pion * j * (k + 0.5));
      c[j] = fac * sum * isignum;
      isignum = -isignum;
    }
  }
  else
  {
    for (j = 0; j < n; j++)
    {
      sum = -0.5 * u[0];
      isignum = 1;
      for (k = 0; k < n; k++)
      {
        sum += u[k] * cos(Pion * (j + 0.5) * k) * isignum;
        isignum = -isignum;
      }
      c[j] = sum;
    }
  }
  for (j = 0; j < n; j++)
    u[j] = c[j];
  free_dvector(c, 0, n);
}

/* --------------------------------------------------------------------------*/
void TwoPunctures::chebft_Extremes(double u[], int n, int inv)
/* eq. 5.8.7 and 5.8.8 at x = (5.8.5) of 2nd edition C++ NR */
{
  int k, j, isignum, N = n - 1;
  double fac, sum, PioN, *c;

  c = dvector(0, N);
  PioN = Pi / N;
  if (inv == 0)
  {
    fac = 2.0 / N;
    isignum = 1;
    for (j = 0; j < n; j++)
    {
      sum = 0.5 * (u[0] + u[N] * isignum);
      for (k = 1; k < N; k++)
        sum += u[k] * cos(PioN * j * k);
      c[j] = fac * sum * isignum;
      isignum = -isignum;
    }
    c[N] = 0.5 * c[N];
  }
  else
  {
    for (j = 0; j < n; j++)
    {
      sum = -0.5 * u[0];
      isignum = 1;
      for (k = 0; k < n; k++)
      {
        sum += u[k] * cos(PioN * j * k) * isignum;
        isignum = -isignum;
      }
      c[j] = sum;
    }
  }
  for (j = 0; j < n; j++)
    u[j] = c[j];
  free_dvector(c, 0, N);
}

/* --------------------------------------------------------------------------*/

void TwoPunctures::chder(double *c, double *cder, int n)
{
  int j;

  cder[n] = 0.0;
  cder[n - 1] = 0.0;
  for (j = n - 2; j >= 0; j--)
    cder[j] = cder[j + 2] + 2 * (j + 1) * c[j + 1];
}

/* --------------------------------------------------------------------------*/
double TwoPunctures::chebev(double a, double b, double c[], int m, double x)
/* eq. 5.8.11 of C++ NR (2nd ed) */
{
  int j;
  double djp2, djp1, dj; /* d_{j+2}, d_{j+1} and d_j */
  double y;

  /* rescale input to lie within [-1,1] */
  y = 2 * (x - 0.5 * (b + a)) / (b - a);

  dj = djp1 = 0;
  for (j = m - 1; j >= 1; j--)
  {
    /* advance the coefficients */
    djp2 = djp1;
    djp1 = dj;
    dj = 2 * y * djp1 - djp2 + c[j];
  }

  return y * dj - djp1 + 0.5 * c[0];
}

/* --------------------------------------------------------------------------*/
void TwoPunctures::fourft(double *u, int N, int inv)
/* a (slow) Fourier transform, seems to be just eq. 12.1.6 and 12.1.9 of C++ NR (2nd ed) */
{
  int l, k, iy, M;
  double x, x1, fac, Pi_fac, *a, *b;

  M = N / 2;
  a = dvector(0, M);
  b = dvector(1, M); /* Actually: b=vector(1,M-1) but this is problematic if M=1*/
  fac = 1. / M;
  Pi_fac = Pi * fac;
  if (inv == 0)
  {
    for (l = 0; l <= M; l++)
    {
      a[l] = 0;
      if (l > 0 && l < M)
        b[l] = 0;
      x1 = Pi_fac * l;
      for (k = 0; k < N; k++)
      {
        x = x1 * k;
        a[l] += fac * u[k] * cos(x);
        if (l > 0 && l < M)
          b[l] += fac * u[k] * sin(x);
      }
    }
    u[0] = a[0];
    u[M] = a[M];
    for (l = 1; l < M; l++)
    {
      u[l] = a[l];
      u[l + M] = b[l];
    }
  }
  else
  {
    a[0] = u[0];
    a[M] = u[M];
    for (l = 1; l < M; l++)
    {
      a[l] = u[l];
      b[l] = u[M + l];
    }
    iy = 1;
    for (k = 0; k < N; k++)
    {
      u[k] = 0.5 * (a[0] + a[M] * iy);
      x1 = Pi_fac * k;
      for (l = 1; l < M; l++)
      {
        x = x1 * l;
        u[k] += a[l] * cos(x) + b[l] * sin(x);
      }
      iy = -iy;
    }
  }
  free_dvector(a, 0, M);
  free_dvector(b, 1, M);
}

/* -----------------------------------------*/
void TwoPunctures::fourder(double u[], double du[], int N)
{
  int l, M, lpM;

  M = N / 2;
  du[0] = 0.;
  du[M] = 0.;
  for (l = 1; l < M; l++)
  {
    lpM = l + M;
    du[l] = u[lpM] * l;
    du[lpM] = -u[l] * l;
  }
}

/* -----------------------------------------*/
void TwoPunctures::fourder2(double u[], double d2u[], int N)
{
  int l, l2, M, lpM;

  d2u[0] = 0.;
  M = N / 2;
  for (l = 1; l <= M; l++)
  {
    l2 = l * l;
    lpM = l + M;
    d2u[l] = -u[l] * l2;
    if (l < M)
      d2u[lpM] = -u[lpM] * l2;
  }
}

/* ----------------------------------------- */
double TwoPunctures::fourev(double *u, int N, double x)
{
  int l, M = N / 2;
  double xl, result;

  result = 0.5 * (u[0] + u[M] * cos(x * M));
  for (l = 1; l < M; l++)
  {
    xl = x * l;
    result += u[l] * cos(xl) + u[M + l] * sin(xl);
  }
  return result;
}

/* ------------------------------------------------------------------------*/
double TwoPunctures::norm1(double *v, int n)
{
  int i;
  double result = -1;

  for (i = 0; i < n; i++)
    if (fabs(v[i]) > result)
      result = fabs(v[i]);

  return result;
}

/* -------------------------------------------------------------------------*/
double TwoPunctures::norm2(double *v, int n)
{
  int i;
  double result = 0;

  for (i = 0; i < n; i++)
    result += v[i] * v[i];

  return sqrt(result);
}

/* -------------------------------------------------------------------------*/
double TwoPunctures::scalarproduct(double *v, double *w, int n)
{
  int i;
  double result = 0;

  for (i = 0; i < n; i++)
    result += v[i] * w[i];

  return result;
}

/* -------------------------------------------------------------------------*/
/* Calculates the value of v at an arbitrary position (x,y,z)*/
double TwoPunctures::PunctIntPolAtArbitPosition(int ivar, int nvar, int n1,
                                                int n2, int n3, derivs v, double x, double y,
                                                double z)
{
  double xs, ys, zs, rs2, phi, X, R, A, B, aux1, aux2, result, Ui;

  xs = x / par_b;
  ys = y / par_b;
  zs = z / par_b;
  rs2 = ys * ys + zs * zs;
  phi = atan2(z, y);
  if (phi < 0)
    phi += 2 * Pi;

  aux1 = 0.5 * (xs * xs + rs2 - 1);
  aux2 = sqrt(aux1 * aux1 + rs2);
  X = asinh(sqrt(aux1 + aux2));
  R = asin(min(1.0, sqrt(-aux1 + aux2)));
  if (x < 0)
    R = Pi - R;

  A = 2 * tanh(0.5 * X) - 1;
  B = tan(0.5 * R - Piq);

  result = PunctEvalAtArbitPosition(v.d0, ivar, A, B, phi, nvar, n1, n2, n3);

  Ui = (A - 1) * result;

  return Ui;
}
/* Calculates the value of v at an arbitrary position (A,B,phi)*/
double TwoPunctures::PunctEvalAtArbitPosition(double *v, int ivar, double A, double B, double phi,
                                              int nvar, int n1, int n2, int n3)
{
  int i, j, k, N;
  double *p, *values1, **values2, result;

  N = maximum3(n1, n2, n3);
  p = dvector(0, N);
  values1 = dvector(0, N);
  values2 = dmatrix(0, N, 0, N);

  for (k = 0; k < n3; k++)
  {
    for (j = 0; j < n2; j++)
    {
      for (i = 0; i < n1; i++)
        p[i] = v[ivar + nvar * (i + n1 * (j + n2 * k))];
      chebft_Zeros(p, n1, 0);
      values2[j][k] = chebev(-1, 1, p, n1, A);
    }
  }

  for (k = 0; k < n3; k++)
  {
    for (j = 0; j < n2; j++)
      p[j] = values2[j][k];
    chebft_Zeros(p, n2, 0);
    values1[k] = chebev(-1, 1, p, n2, B);
  }

  fourft(values1, n3, 0);
  result = fourev(values1, n3, phi);

  free_dvector(p, 0, N);
  free_dvector(values1, 0, N);
  free_dmatrix(values2, 0, N, 0, N);

  return result;
}
/*-----------------------------------------------------------*/
void TwoPunctures::AB_To_XR(int nvar, double A, double B, double *X, double *R,
                            derivs U)
/* On Entrance: U.d0[]=U[]; U.d1[] =U[]_A;  U.d2[] =U[]_B;  U.d3[] =U[]_3;  */
/*                          U.d11[]=U[]_AA; U.d12[]=U[]_AB; U.d13[]=U[]_A3; */
/*                          U.d22[]=U[]_BB; U.d23[]=U[]_B3; U.d33[]=U[]_33; */
/* At Exit:     U.d0[]=U[]; U.d1[] =U[]_X;  U.d2[] =U[]_R;  U.d3[] =U[]_3;  */
/*                          U.d11[]=U[]_XX; U.d12[]=U[]_XR; U.d13[]=U[]_X3; */
/*                          U.d22[]=U[]_RR; U.d23[]=U[]_R3; U.d33[]=U[]_33; */
{
  double At = 0.5 * (A + 1), A_X, A_XX, B_R, B_RR;
  int ivar;

  *X = 2 * atanh(At);
  *R = Pih + 2 * atan(B);

  A_X = 1 - At * At;
  A_XX = -At * A_X;
  B_R = 0.5 * (1 + B * B);
  B_RR = B * B_R;

  for (ivar = 0; ivar < nvar; ivar++)
  {
    U.d11[ivar] = A_X * A_X * U.d11[ivar] + A_XX * U.d1[ivar];
    U.d12[ivar] = A_X * B_R * U.d12[ivar];
    U.d13[ivar] = A_X * U.d13[ivar];
    U.d22[ivar] = B_R * B_R * U.d22[ivar] + B_RR * U.d2[ivar];
    U.d23[ivar] = B_R * U.d23[ivar];
    U.d1[ivar] = A_X * U.d1[ivar];
    U.d2[ivar] = B_R * U.d2[ivar];
  }
}
/*-----------------------------------------------------------*/
void TwoPunctures::C_To_c(int nvar, double X, double R, double *x, double *r,
                          derivs U)
/* On Entrance: U.d0[]=U[]; U.d1[] =U[]_X;  U.d2[] =U[]_R;  U.d3[] =U[]_3;  */
/*                          U.d11[]=U[]_XX; U.d12[]=U[]_XR; U.d13[]=U[]_X3; */
/*                          U.d22[]=U[]_RR; U.d23[]=U[]_R3; U.d33[]=U[]_33; */
/* At Exit:     U.d0[]=U[]; U.d1[] =U[]_x;  U.d2[] =U[]_r;  U.d3[] =U[]_3;  */
/*                          U.d11[]=U[]_xx; U.d12[]=U[]_xr; U.d13[]=U[]_x3; */
/*                          U.d22[]=U[]_rr; U.d23[]=U[]_r3; U.d33[]=U[]_33; */
{
  double C_c2, U_cb, U_CB;
  complex<double> C, C_c, C_cc, c, c_C, c_CC, U_c, U_cc, U_C, U_CC;
  int ivar;

  C = complex<double>(X, R);

  c = cosh(C) * par_b; /* c=b*cosh(C)*/
  c_C = sinh(C) * par_b;
  c_CC = c;

  C_c = complex<double>(1, 0) / c_C;
  C_cc = -C_c * C_c * C_c * c_CC;
  C_c2 = abs(C_c);
  C_c2 = C_c2 * C_c2;

  for (ivar = 0; ivar < nvar; ivar++)
  {
    /* U_C = 0.5*(U_X3-i*U_R3)*/
    /* U_c = U_C*C_c = 0.5*(U_x3-i*U_r3)*/
    U_C = complex<double>(0.5 * U.d13[ivar], -0.5 * U.d23[ivar]);
    U_c = U_C * C_c;
    U.d13[ivar] = 2. * real(U_c);
    U.d23[ivar] = -2. * imag(U_c);

    /* U_C = 0.5*(U_X-i*U_R)*/
    /* U_c = U_C*C_c = 0.5*(U_x-i*U_r)*/
    U_C = complex<double>(0.5 * U.d1[ivar], -0.5 * U.d2[ivar]);
    U_c = U_C * C_c;
    U.d1[ivar] = 2. * real(U_c);
    U.d2[ivar] = -2. * imag(U_c);

    /* U_CC = 0.25*(U_XX-U_RR-2*i*U_XR)*/
    /* U_CB = d^2(U)/(dC*d\bar{C}) = 0.25*(U_XX+U_RR)*/
    U_CC = complex<double>(0.25 * (U.d11[ivar] - U.d22[ivar]), -0.5 * U.d12[ivar]);
    U_CB = 0.25 * (U.d11[ivar] + U.d22[ivar]);

    /* U_cc = C_cc*U_C+(C_c)^2*U_CC*/
    U_cb = U_CB * C_c2;
    U_cc = C_cc * U_C + C_c * C_c * U_CC;

    /* U_xx = 2*(U_cb+Re[U_cc])*/
    /* U_rr = 2*(U_cb-Re[U_cc])*/
    /* U_rx = -2*Im[U_cc]*/
    U.d11[ivar] = 2 * (U_cb + real(U_cc));
    U.d22[ivar] = 2 * (U_cb - real(U_cc));
    U.d12[ivar] = -2 * imag(U_cc);
  }

  *x = real(c);
  *r = imag(c);
}
/*-----------------------------------------------------------*/
void TwoPunctures::rx3_To_xyz(int nvar, double x, double r, double phi,
                              double *y, double *z, derivs U)
/* On Entrance: U.d0[]=U[]; U.d1[] =U[]_x;  U.d2[] =U[]_r;  U.d3[] =U[]_3;  */
/*                          U.d11[]=U[]_xx; U.d12[]=U[]_xr; U.d13[]=U[]_x3; */
/*                          U.d22[]=U[]_rr; U.d23[]=U[]_r3; U.d33[]=U[]_33; */
/* At Exit:     U.d0[]=U[]; U.d1[] =U[]_x;  U.d2[] =U[]_y;  U.dz[] =U[]_z;  */
/*                          U.d11[]=U[]_xx; U.d12[]=U[]_xy; U.d1z[]=U[]_xz; */
/*                          U.d22[]=U[]_yy; U.d2z[]=U[]_yz; U.dzz[]=U[]_zz; */
{
  int jvar;
  double
      sin_phi = sin(phi),
      cos_phi = cos(phi),
      sin2_phi = sin_phi * sin_phi,
      cos2_phi = cos_phi * cos_phi,
      sin_2phi = 2 * sin_phi * cos_phi,
      cos_2phi = cos2_phi - sin2_phi, r_inv = 1 / r, r_inv2 = r_inv * r_inv;

  *y = r * cos_phi;
  *z = r * sin_phi;

  for (jvar = 0; jvar < nvar; jvar++)
  {
    double U_x = U.d1[jvar], U_r = U.d2[jvar], U_3 = U.d3[jvar],
           U_xx = U.d11[jvar], U_xr = U.d12[jvar], U_x3 = U.d13[jvar],
           U_rr = U.d22[jvar], U_r3 = U.d23[jvar], U_33 = U.d33[jvar];
    U.d1[jvar] = U_x;                                                    /* U_x*/
    U.d2[jvar] = U_r * cos_phi - U_3 * r_inv * sin_phi;                  /* U_y*/
    U.d3[jvar] = U_r * sin_phi + U_3 * r_inv * cos_phi;                  /* U_z*/
    U.d11[jvar] = U_xx;                                                  /* U_xx*/
    U.d12[jvar] = U_xr * cos_phi - U_x3 * r_inv * sin_phi;               /* U_xy*/
    U.d13[jvar] = U_xr * sin_phi + U_x3 * r_inv * cos_phi;               /* U_xz*/
    U.d22[jvar] = U_rr * cos2_phi + r_inv2 * sin2_phi * (U_33 + r * U_r) /* U_yy*/
                  + sin_2phi * r_inv2 * (U_3 - r * U_r3);
    U.d23[jvar] = 0.5 * sin_2phi * (U_rr - r_inv * U_r - r_inv2 * U_33) /* U_yz*/
                  - cos_2phi * r_inv2 * (U_3 - r * U_r3);
    U.d33[jvar] = U_rr * sin2_phi + r_inv2 * cos2_phi * (U_33 + r * U_r) /* U_zz*/
                  - sin_2phi * r_inv2 * (U_3 - r * U_r3);
  }
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::Derivatives_AB3(int nvar, int n1, int n2, int n3, derivs v)
{
  int i, j, k, ivar, N, *indx;
  double *p, *dp, *d2p, *q, *dq, *r, *dr;

  N = maximum3(n1, n2, n3);
  p = dvector(0, N);
  dp = dvector(0, N);
  d2p = dvector(0, N);
  q = dvector(0, N);
  dq = dvector(0, N);
  r = dvector(0, N);
  dr = dvector(0, N);
  indx = ivector(0, N);

  for (ivar = 0; ivar < nvar; ivar++)
  {
    for (k = 0; k < n3; k++)
    { /* Calculation of Derivatives w.r.t. A-Dir. */
      for (j = 0; j < n2; j++)
      { /* (Chebyshev_Zeros)*/
        for (i = 0; i < n1; i++)
        {
          indx[i] = Index(ivar, i, j, k, nvar, n1, n2, n3);
          p[i] = v.d0[indx[i]];
        }
        chebft_Zeros(p, n1, 0);
        chder(p, dp, n1);
        chder(dp, d2p, n1);
        chebft_Zeros(dp, n1, 1);
        chebft_Zeros(d2p, n1, 1);
        for (i = 0; i < n1; i++)
        {
          v.d1[indx[i]] = dp[i];
          v.d11[indx[i]] = d2p[i];
        }
      }
    }
    for (k = 0; k < n3; k++)
    { /* Calculation of Derivatives w.r.t. B-Dir. */
      for (i = 0; i < n1; i++)
      { /* (Chebyshev_Zeros)*/
        for (j = 0; j < n2; j++)
        {
          indx[j] = Index(ivar, i, j, k, nvar, n1, n2, n3);
          p[j] = v.d0[indx[j]];
          q[j] = v.d1[indx[j]];
        }
        chebft_Zeros(p, n2, 0);
        chebft_Zeros(q, n2, 0);
        chder(p, dp, n2);
        chder(dp, d2p, n2);
        chder(q, dq, n2);
        chebft_Zeros(dp, n2, 1);
        chebft_Zeros(d2p, n2, 1);
        chebft_Zeros(dq, n2, 1);
        for (j = 0; j < n2; j++)
        {
          v.d2[indx[j]] = dp[j];
          v.d22[indx[j]] = d2p[j];
          v.d12[indx[j]] = dq[j];
        }
      }
    }
    for (i = 0; i < n1; i++)
    { /* Calculation of Derivatives w.r.t. phi-Dir. (Fourier)*/
      for (j = 0; j < n2; j++)
      {
        for (k = 0; k < n3; k++)
        {
          indx[k] = Index(ivar, i, j, k, nvar, n1, n2, n3);
          p[k] = v.d0[indx[k]];
          q[k] = v.d1[indx[k]];
          r[k] = v.d2[indx[k]];
        }
        fourft(p, n3, 0);
        fourder(p, dp, n3);
        fourder2(p, d2p, n3);
        fourft(dp, n3, 1);
        fourft(d2p, n3, 1);
        fourft(q, n3, 0);
        fourder(q, dq, n3);
        fourft(dq, n3, 1);
        fourft(r, n3, 0);
        fourder(r, dr, n3);
        fourft(dr, n3, 1);
        for (k = 0; k < n3; k++)
        {
          v.d3[indx[k]] = dp[k];
          v.d33[indx[k]] = d2p[k];
          v.d13[indx[k]] = dq[k];
          v.d23[indx[k]] = dr[k];
        }
      }
    }
  }
  free_dvector(p, 0, N);
  free_dvector(dp, 0, N);
  free_dvector(d2p, 0, N);
  free_dvector(q, 0, N);
  free_dvector(dq, 0, N);
  free_dvector(r, 0, N);
  free_dvector(dr, 0, N);
  free_ivector(indx, 0, N);
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::Newton(int const nvar, int const n1, int const n2, int const n3,
                          derivs v, double const tol, int const itmax)
{
  int ntotal = n1 * n2 * n3 * nvar, ii, it;
  double *F, dmax, normres;
  derivs u, dv;

  F = dvector(0, ntotal - 1);
  allocate_derivs(&dv, ntotal);
  allocate_derivs(&u, ntotal);

  it = 0;
  dmax = 1;
  while (dmax > tol && it < itmax)
  {
    if (it == 0)
    {
      F_of_v(nvar, n1, n2, n3, v, F, u);
      dmax = norm_inf(F, ntotal);
    }
    for (int j = 0; j < ntotal; j++)
      dv.d0[j] = 0;

    {
      printf("Newton: it=%d \t |F|=%e\n", it, (double)dmax);
      printf("bare mass: mp=%g \t mm=%g\n", (double)par_m_plus, (double)par_m_minus);
    }

    fflush(stdout);
    ii = bicgstab(nvar, n1, n2, n3, v, dv, 100, dmax * 1.e-3, &normres);

    for (int j = 0; j < ntotal; j++)
      v.d0[j] -= dv.d0[j];
    F_of_v(nvar, n1, n2, n3, v, F, u);
    dmax = norm_inf(F, ntotal);
    it += 1;
  }
  if (itmax == 0)
  {
    F_of_v(nvar, n1, n2, n3, v, F, u);
    dmax = norm_inf(F, ntotal);
  }

  printf("Newton: it=%d \t |F|=%e \n", it, (double)dmax);

  fflush(stdout);

  free_dvector(F, 0, ntotal - 1);
  free_derivs(&dv, ntotal);
  free_derivs(&u, ntotal);
}
#define FAC sin(al) * sin(be) * sin(al) * sin(be) * sin(al) * sin(be)
/* --------------------------------------------------------------------------*/
void TwoPunctures::F_of_v(int nvar, int n1, int n2, int n3, derivs v, double *F,
                          derivs u)
{
  /*      Calculates the left hand sides of the non-linear equations F_m(v_n)=0*/
  /*      and the function u (u.d0[]) as well as its derivatives*/
  /*      (u.d1[], u.d2[], u.d3[], u.d11[], u.d12[], u.d13[], u.d22[], u.d23[], u.d33[])*/
  /*      at interior points and at the boundaries "+/-"*/

  int i, j, k, ivar, indx;
  double al, be, A, B, X, R, x, r, phi, y, z, Am1, *values;
  derivs U;
  double *sources;

  values = dvector(0, nvar - 1);
  allocate_derivs(&U, nvar);

  sources = (double *)calloc(n1 * n2 * n3, sizeof(double));
  if (0)
  {
    double *s_x, *s_y, *s_z;
    int i3D;
    s_x = (double *)calloc(n1 * n2 * n3, sizeof(double));
    s_y = (double *)calloc(n1 * n2 * n3, sizeof(double));
    s_z = (double *)calloc(n1 * n2 * n3, sizeof(double));
    for (i = 0; i < n1; i++)
      for (j = 0; j < n2; j++)
        for (k = 0; k < n3; k++)
        {
          i3D = Index(0, i, j, k, 1, n1, n2, n3);

          al = Pih * (2 * i + 1) / n1;
          A = -cos(al);
          be = Pih * (2 * j + 1) / n2;
          B = -cos(be);
          phi = 2. * Pi * k / n3;

          Am1 = A - 1;
          for (ivar = 0; ivar < nvar; ivar++)
          {
            indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
            U.d0[ivar] = Am1 * v.d0[indx];                    /* U*/
            U.d1[ivar] = v.d0[indx] + Am1 * v.d1[indx];       /* U_A*/
            U.d2[ivar] = Am1 * v.d2[indx];                    /* U_B*/
            U.d3[ivar] = Am1 * v.d3[indx];                    /* U_3*/
            U.d11[ivar] = 2 * v.d1[indx] + Am1 * v.d11[indx]; /* U_AA*/
            U.d12[ivar] = v.d2[indx] + Am1 * v.d12[indx];     /* U_AB*/
            U.d13[ivar] = v.d3[indx] + Am1 * v.d13[indx];     /* U_AB*/
            U.d22[ivar] = Am1 * v.d22[indx];                  /* U_BB*/
            U.d23[ivar] = Am1 * v.d23[indx];                  /* U_B3*/
            U.d33[ivar] = Am1 * v.d33[indx];                  /* U_33*/
          }
          /* Calculation of (X,R) and*/
          /* (U_X, U_R, U_3, U_XX, U_XR, U_X3, U_RR, U_R3, U_33)*/
          AB_To_XR(nvar, A, B, &X, &R, U);
          /* Calculation of (x,r) and*/
          /* (U, U_x, U_r, U_3, U_xx, U_xr, U_x3, U_rr, U_r3, U_33)*/
          C_To_c(nvar, X, R, &(s_x[i3D]), &r, U);
          /* Calculation of (y,z) and*/
          /* (U, U_x, U_y, U_z, U_xx, U_xy, U_xz, U_yy, U_yz, U_zz)*/
          rx3_To_xyz(nvar, s_x[i3D], r, phi, &(s_y[i3D]), &(s_z[i3D]), U);
        }
    //    Set_Rho_ADM(cctkGH, n1*n2*n3, sources, s_x, s_y, s_z);  //external fortran code
    free(s_z);
    free(s_y);
    free(s_x);
  }
  else
    for (i = 0; i < n1; i++)
      for (j = 0; j < n2; j++)
        for (k = 0; k < n3; k++)
          sources[Index(0, i, j, k, 1, n1, n2, n3)] = 0.0;

  Derivatives_AB3(nvar, n1, n2, n3, v);
  double psi, psi2, psi4, psi7, r_plus, r_minus;
  FILE *debugfile = NULL;
  if (0)
  {
    debugfile = fopen("res.dat", "w");
    assert(debugfile);
  }
  for (i = 0; i < n1; i++)
  {
    for (j = 0; j < n2; j++)
    {
      for (k = 0; k < n3; k++)
      {

        al = Pih * (2 * i + 1) / n1;
        A = -cos(al);
        be = Pih * (2 * j + 1) / n2;
        B = -cos(be);
        phi = 2. * Pi * k / n3;

        Am1 = A - 1;
        for (ivar = 0; ivar < nvar; ivar++)
        {
          indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
          U.d0[ivar] = Am1 * v.d0[indx];                    /* U*/
          U.d1[ivar] = v.d0[indx] + Am1 * v.d1[indx];       /* U_A*/
          U.d2[ivar] = Am1 * v.d2[indx];                    /* U_B*/
          U.d3[ivar] = Am1 * v.d3[indx];                    /* U_3*/
          U.d11[ivar] = 2 * v.d1[indx] + Am1 * v.d11[indx]; /* U_AA*/
          U.d12[ivar] = v.d2[indx] + Am1 * v.d12[indx];     /* U_AB*/
          U.d13[ivar] = v.d3[indx] + Am1 * v.d13[indx];     /* U_AB*/
          U.d22[ivar] = Am1 * v.d22[indx];                  /* U_BB*/
          U.d23[ivar] = Am1 * v.d23[indx];                  /* U_B3*/
          U.d33[ivar] = Am1 * v.d33[indx];                  /* U_33*/
        }
        /* Calculation of (X,R) and*/
        /* (U_X, U_R, U_3, U_XX, U_XR, U_X3, U_RR, U_R3, U_33)*/
        AB_To_XR(nvar, A, B, &X, &R, U);
        /* Calculation of (x,r) and*/
        /* (U, U_x, U_r, U_3, U_xx, U_xr, U_x3, U_rr, U_r3, U_33)*/
        C_To_c(nvar, X, R, &x, &r, U);
        /* Calculation of (y,z) and*/
        /* (U, U_x, U_y, U_z, U_xx, U_xy, U_xz, U_yy, U_yz, U_zz)*/
        rx3_To_xyz(nvar, x, r, phi, &y, &z, U);
        NonLinEquations(sources[Index(0, i, j, k, 1, n1, n2, n3)],
                        A, B, X, R, x, r, phi, y, z, U, values);
        for (ivar = 0; ivar < nvar; ivar++)
        {
          indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
          F[indx] = values[ivar] * FAC;
          /* if ((i<5) && ((j<5) || (j>n2-5)))*/
          /*     F[indx] = 0.0;*/
          u.d0[indx] = U.d0[ivar];   /*  U*/
          u.d1[indx] = U.d1[ivar];   /*      U_x*/
          u.d2[indx] = U.d2[ivar];   /*      U_y*/
          u.d3[indx] = U.d3[ivar];   /*      U_z*/
          u.d11[indx] = U.d11[ivar]; /*      U_xx*/
          u.d12[indx] = U.d12[ivar]; /*      U_xy*/
          u.d13[indx] = U.d13[ivar]; /*      U_xz*/
          u.d22[indx] = U.d22[ivar]; /*      U_yy*/
          u.d23[indx] = U.d23[ivar]; /*      U_yz*/
          u.d33[indx] = U.d33[ivar]; /*      U_zz*/
        }
        if (debugfile && (k == 0))
        {
          r_plus = sqrt((x - par_b) * (x - par_b) + y * y + z * z);
          r_minus = sqrt((x + par_b) * (x + par_b) + y * y + z * z);
          psi = 1. +
                0.5 * par_m_plus / r_plus +
                0.5 * par_m_minus / r_minus +
                U.d0[0];
          psi2 = psi * psi;
          psi4 = psi2 * psi2;
          psi7 = psi * psi2 * psi4;
          fprintf(debugfile,
                  "%.16g %.16g %.16g %.16g %.16g %.16g %.16g %.16g\n",
                  (double)x, (double)y, (double)A, (double)B,
                  (double)(U.d11[0] +
                           U.d22[0] +
                           U.d33[0] +
                           /*                      0.125 * BY_KKofxyz (x, y, z) / psi7 +*/
                           (2.0 * Pi / psi2 / psi * sources[indx]) * FAC),
                  (double)((U.d11[0] +
                            U.d22[0] +
                            U.d33[0]) *
                           FAC),
                  (double)(-(2.0 * Pi / psi2 / psi * sources[indx]) * FAC),
                  (double)sources[indx]
                  /*(double)F[indx]*/
          );
        }
      }
    }
  }
  if (debugfile)
  {
    fclose(debugfile);
  }
  free(sources);
  free_dvector(values, 0, nvar - 1);
  free_derivs(&U, nvar);
}
/* --------------------------------------------------------------------------*/
double TwoPunctures::norm_inf(double const *F, int const ntotal)
{
  double dmax = -1;
  {
    double dmax1 = -1;
    for (int j = 0; j < ntotal; j++)
      if (fabs(F[j]) > dmax1)
        dmax1 = fabs(F[j]);
    if (dmax1 > dmax)
      dmax = dmax1;
  }
  return dmax;
}
/* --------------------------------------------------------------------------*/
int TwoPunctures::bicgstab(int const nvar, int const n1, int const n2, int const n3,
                           derivs v, derivs dv, int const itmax, double const tol,
                           double *normres)
{
  int const output = 1;
  int ntotal = n1 * n2 * n3 * nvar, ii;
  double alpha = 0, beta = 0;
  double rho = 0, rho1 = 1, rhotol = 1e-50;
  double omega = 0, omegatol = 1e-50;
  double *p, *rt, *s, *t, *r, *vv;
  double **JFD;
  int **cols, *ncols, maxcol = StencilSize * nvar;
  double *F;
  derivs u, ph, sh;

  F = dvector(0, ntotal - 1);
  allocate_derivs(&u, ntotal);

  JFD = dmatrix(0, ntotal - 1, 0, maxcol - 1);
  cols = imatrix(0, ntotal - 1, 0, maxcol - 1);
  ncols = ivector(0, ntotal - 1);

  F_of_v(nvar, n1, n2, n3, v, F, u);
  SetMatrix_JFD(nvar, n1, n2, n3, u, ncols, cols, JFD);

  /* temporary storage */
  r = dvector(0, ntotal - 1);
  p = dvector(0, ntotal - 1);
  allocate_derivs(&ph, ntotal);
  /*      ph  = dvector(0, ntotal-1);*/
  rt = dvector(0, ntotal - 1);
  s = dvector(0, ntotal - 1);
  allocate_derivs(&sh, ntotal);
  /*      sh  = dvector(0, ntotal-1);*/
  t = dvector(0, ntotal - 1);
  vv = dvector(0, ntotal - 1);

  /* check */
  if (output == 1)
  {
    printf("bicgstab:  itmax %d, tol %e\n", itmax, (double)tol);
    fflush(stdout);
  }

  /* compute initial residual rt = r = F - J*dv */
  J_times_dv(nvar, n1, n2, n3, dv, r, u);
  for (int j = 0; j < ntotal; j++)
    rt[j] = r[j] = F[j] - r[j];

  *normres = norm2(r, ntotal);
  if (output == 1)
  {
    printf("bicgstab: %5d  %10.3e\n", 0, (double)*normres);
    fflush(stdout);
  }

  if (*normres <= tol)
    return 0;

  /* cgs iteration */
  for (ii = 0; ii < itmax; ii++)
  {
    rho = scalarproduct(rt, r, ntotal);
    if (fabs(rho) < rhotol)
      break;

    /* compute direction vector p */
    if (ii == 0)
    {
      for (int j = 0; j < ntotal; j++)
        p[j] = r[j];
    }
    else
    {
      beta = (rho / rho1) * (alpha / omega);
      for (int j = 0; j < ntotal; j++)
        p[j] = r[j] + beta * (p[j] - omega * vv[j]);
    }

    /* compute direction adjusting vector ph and scalar alpha */
    for (int j = 0; j < ntotal; j++)
      ph.d0[j] = 0;
    for (int j = 0; j < NRELAX; j++) /* solves JFD*ph = p by relaxation*/
      relax(ph.d0, nvar, n1, n2, n3, p, ncols, cols, JFD);

    J_times_dv(nvar, n1, n2, n3, ph, vv, u); /* vv=J*ph*/
    alpha = rho / scalarproduct(rt, vv, ntotal);
    for (int j = 0; j < ntotal; j++)
      s[j] = r[j] - alpha * vv[j];

    /* early check of tolerance */
    *normres = norm2(s, ntotal);
    if (*normres <= tol)
    {
      for (int j = 0; j < ntotal; j++)
        dv.d0[j] += alpha * ph.d0[j];
      if (output == 1)
      {
        printf("bicgstab: %5d  %10.3e  %10.3e  %10.3e  %10.3e\n",
               ii + 1, (double)*normres, (double)alpha, (double)beta, (double)omega);
        fflush(stdout);
      }
      break;
    }

    /* compute stabilizer vector sh and scalar omega */
    for (int j = 0; j < ntotal; j++)
      sh.d0[j] = 0;
    for (int j = 0; j < NRELAX; j++) /* solves JFD*sh = s by relaxation*/
      relax(sh.d0, nvar, n1, n2, n3, s, ncols, cols, JFD);

    J_times_dv(nvar, n1, n2, n3, sh, t, u); /* t=J*sh*/
    omega = scalarproduct(t, s, ntotal) / scalarproduct(t, t, ntotal);

    /* compute new solution approximation */
    for (int j = 0; j < ntotal; j++)
    {
      dv.d0[j] += alpha * ph.d0[j] + omega * sh.d0[j];
      r[j] = s[j] - omega * t[j];
    }
    /* are we done? */
    *normres = norm2(r, ntotal);
    if (output == 1)
    {
      printf("bicgstab: %5d  %10.3e  %10.3e  %10.3e  %10.3e\n",
             ii + 1, (double)*normres, (double)alpha, (double)beta, (double)omega);
      fflush(stdout);
    }
    if (*normres <= tol)
      break;
    rho1 = rho;
    if (fabs(omega) < omegatol)
      break;
  }

  /* free temporary storage */
  free_dvector(r, 0, ntotal - 1);
  free_dvector(p, 0, ntotal - 1);
  /*      free_dvector(ph,  0, ntotal-1);*/
  free_derivs(&ph, ntotal);
  free_dvector(rt, 0, ntotal - 1);
  free_dvector(s, 0, ntotal - 1);
  /*      free_dvector(sh,  0, ntotal-1);*/
  free_derivs(&sh, ntotal);
  free_dvector(t, 0, ntotal - 1);
  free_dvector(vv, 0, ntotal - 1);

  free_dvector(F, 0, ntotal - 1);
  free_derivs(&u, ntotal);

  free_dmatrix(JFD, 0, ntotal - 1, 0, maxcol - 1);
  free_imatrix(cols, 0, ntotal - 1, 0, maxcol - 1);
  free_ivector(ncols, 0, ntotal - 1);

  /* iteration failed */
  if (ii > itmax)
    return -1;

  /* breakdown */
  if (fabs(rho) < rhotol)
    return -10;
  if (fabs(omega) < omegatol)
    return -11;

  /* success! */
  return ii + 1;
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::allocate_derivs(derivs *v, int n)
{
  int m = n - 1;
  (*v).d0 = dvector(0, m);
  (*v).d1 = dvector(0, m);
  (*v).d2 = dvector(0, m);
  (*v).d3 = dvector(0, m);
  (*v).d11 = dvector(0, m);
  (*v).d12 = dvector(0, m);
  (*v).d13 = dvector(0, m);
  (*v).d22 = dvector(0, m);
  (*v).d23 = dvector(0, m);
  (*v).d33 = dvector(0, m);
}

/* --------------------------------------------------------------------------*/
void TwoPunctures::free_derivs(derivs *v, int n)
{
  int m = n - 1;
  free_dvector((*v).d0, 0, m);
  free_dvector((*v).d1, 0, m);
  free_dvector((*v).d2, 0, m);
  free_dvector((*v).d3, 0, m);
  free_dvector((*v).d11, 0, m);
  free_dvector((*v).d12, 0, m);
  free_dvector((*v).d13, 0, m);
  free_dvector((*v).d22, 0, m);
  free_dvector((*v).d23, 0, m);
  free_dvector((*v).d33, 0, m);
}
/* --------------------------------------------------------------------------*/
int TwoPunctures::Index(int ivar, int i, int j, int k, int nvar, int n1, int n2, int n3)
{
  int i1 = i, j1 = j, k1 = k;

  if (i1 < 0)
    i1 = -(i1 + 1);
  if (i1 >= n1)
    i1 = 2 * n1 - (i1 + 1);

  if (j1 < 0)
    j1 = -(j1 + 1);
  if (j1 >= n2)
    j1 = 2 * n2 - (j1 + 1);

  if (k1 < 0)
    k1 = k1 + n3;
  if (k1 >= n3)
    k1 = k1 - n3;

  return ivar + nvar * (i1 + n1 * (j1 + n2 * k1));
}
/*-----------------------------------------------------------*/
/********           Nonlinear Equations                ***********/
/*-----------------------------------------------------------*/
void TwoPunctures::NonLinEquations(double rho_adm,
                                   double A, double B, double X, double R,
                                   double x, double r, double phi,
                                   double y, double z, derivs U, double *values)
{
  double r_plus, r_minus, psi, psi2, psi4, psi7;
  double mu;

  r_plus = sqrt((x - par_b) * (x - par_b) + y * y + z * z);
  r_minus = sqrt((x + par_b) * (x + par_b) + y * y + z * z);

  psi = 1. + 0.5 * par_m_plus / r_plus + 0.5 * par_m_minus / r_minus + U.d0[0];
  psi2 = psi * psi;
  psi4 = psi2 * psi2;
  psi7 = psi * psi2 * psi4;

  values[0] = U.d11[0] + U.d22[0] + U.d33[0] + 0.125 * BY_KKofxyz(x, y, z) / psi7 + 2.0 * Pi / psi2 / psi * rho_adm;
}
double TwoPunctures::BY_KKofxyz(double x, double y, double z)
{
  int i, j;
  double r_plus, r2_plus, r3_plus, r_minus, r2_minus, r3_minus, np_Pp, nm_Pm,
      Aij, AijAij, n_plus[3], n_minus[3], np_Sp[3], nm_Sm[3];

  r2_plus = (x - par_b) * (x - par_b) + y * y + z * z;
  r2_minus = (x + par_b) * (x + par_b) + y * y + z * z;
  r_plus = sqrt(r2_plus);
  r_minus = sqrt(r2_minus);
  r3_plus = r_plus * r2_plus;
  r3_minus = r_minus * r2_minus;

  n_plus[0] = (x - par_b) / r_plus;
  n_minus[0] = (x + par_b) / r_minus;
  n_plus[1] = y / r_plus;
  n_minus[1] = y / r_minus;
  n_plus[2] = z / r_plus;
  n_minus[2] = z / r_minus;

  /* dot product: np_Pp = (n_+).(P_+); nm_Pm = (n_-).(P_-) */
  np_Pp = 0;
  nm_Pm = 0;
  for (i = 0; i < 3; i++)
  {
    np_Pp += n_plus[i] * par_P_plus[i];
    nm_Pm += n_minus[i] * par_P_minus[i];
  }
  /* cross product: np_Sp[i] = [(n_+) x (S_+)]_i; nm_Sm[i] = [(n_-) x (S_-)]_i*/
  np_Sp[0] = n_plus[1] * par_S_plus[2] - n_plus[2] * par_S_plus[1];
  np_Sp[1] = n_plus[2] * par_S_plus[0] - n_plus[0] * par_S_plus[2];
  np_Sp[2] = n_plus[0] * par_S_plus[1] - n_plus[1] * par_S_plus[0];
  nm_Sm[0] = n_minus[1] * par_S_minus[2] - n_minus[2] * par_S_minus[1];
  nm_Sm[1] = n_minus[2] * par_S_minus[0] - n_minus[0] * par_S_minus[2];
  nm_Sm[2] = n_minus[0] * par_S_minus[1] - n_minus[1] * par_S_minus[0];
  AijAij = 0;
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 3; j++)
    { /* Bowen-York-Curvature :*/
      Aij =
          +1.5 * (par_P_plus[i] * n_plus[j] + par_P_plus[j] * n_plus[i] + np_Pp * n_plus[i] * n_plus[j]) / r2_plus + 1.5 * (par_P_minus[i] * n_minus[j] + par_P_minus[j] * n_minus[i] + nm_Pm * n_minus[i] * n_minus[j]) / r2_minus - 3.0 * (np_Sp[i] * n_plus[j] + np_Sp[j] * n_plus[i]) / r3_plus - 3.0 * (nm_Sm[i] * n_minus[j] + nm_Sm[j] * n_minus[i]) / r3_minus;
      if (i == j)
        Aij -= +1.5 * (np_Pp / r2_plus + nm_Pm / r2_minus);
      AijAij += Aij * Aij;
    }
  }

  return AijAij;
}
void TwoPunctures::SetMatrix_JFD(int nvar, int n1, int n2, int n3, derivs u,
                                 int *ncols, int **cols, double **Matrix)
{
  int column, row, mcol;
  int i, i1, i_0, i_1, j, j1, j_0, j_1, k, k1, k_0, k_1, N1, N2, N3,
      ivar, ivar1, ntotal = nvar * n1 * n2 * n3;
  double *values;
  derivs dv;

  values = dvector(0, nvar - 1);
  allocate_derivs(&dv, ntotal);

  N1 = n1 - 1;
  N2 = n2 - 1;
  N3 = n3 - 1;

  for (i = 0; i < n1; i++)
  {
    for (j = 0; j < n2; j++)
    {
      for (k = 0; k < n3; k++)
      {
        for (ivar = 0; ivar < nvar; ivar++)
        {
          row = Index(ivar, i, j, k, nvar, n1, n2, n3);
          ncols[row] = 0;
          dv.d0[row] = 0;
        }
      }
    }
  }
  for (i = 0; i < n1; i++)
  {
    for (j = 0; j < n2; j++)
    {
      for (k = 0; k < n3; k++)
      {
        for (ivar = 0; ivar < nvar; ivar++)
        {
          column = Index(ivar, i, j, k, nvar, n1, n2, n3);
          dv.d0[column] = 1;

          i_0 = maximum2(0, i - 1);
          i_1 = minimum2(N1, i + 1);
          j_0 = maximum2(0, j - 1);
          j_1 = minimum2(N2, j + 1);
          k_0 = k - 1;
          k_1 = k + 1;
          /*					i_0 = 0;
                    i_1 = N1;
                    j_0 = 0;
                    j_1 = N2;
                    k_0 = 0;
                    k_1 = N3;*/

          for (i1 = i_0; i1 <= i_1; i1++)
          {
            for (j1 = j_0; j1 <= j_1; j1++)
            {
              for (k1 = k_0; k1 <= k_1; k1++)
              {
                JFD_times_dv(i1, j1, k1, nvar, n1, n2, n3,
                             dv, u, values);
                for (ivar1 = 0; ivar1 < nvar; ivar1++)
                {
                  if (values[ivar1] != 0)
                  {
                    row = Index(ivar1, i1, j1, k1, nvar, n1, n2, n3);
                    mcol = ncols[row];
                    cols[row][mcol] = column;
                    Matrix[row][mcol] = values[ivar1];
                    ncols[row] += 1;
                  }
                }
              }
            }
          }

          dv.d0[column] = 0;
        }
      }
    }
  }
  free_derivs(&dv, ntotal);
  free_dvector(values, 0, nvar - 1);
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::J_times_dv(int nvar, int n1, int n2, int n3, derivs dv, double *Jdv, derivs u)
{ /*      Calculates the left hand sides of the non-linear equations F_m(v_n)=0*/
  /*      and the function u (u.d0[]) as well as its derivatives*/
  /*      (u.d1[], u.d2[], u.d3[], u.d11[], u.d12[], u.d13[], u.d22[], u.d23[], u.d33[])*/
  /*      at interior points and at the boundaries "+/-"*/
  int i, j, k, ivar, indx;
  double al, be, A, B, X, R, x, r, phi, y, z, Am1, *values;
  derivs dU, U;

  Derivatives_AB3(nvar, n1, n2, n3, dv);

  for (i = 0; i < n1; i++)
  {
    values = dvector(0, nvar - 1);
    allocate_derivs(&dU, nvar);
    allocate_derivs(&U, nvar);
    for (j = 0; j < n2; j++)
    {
      for (k = 0; k < n3; k++)
      {

        al = Pih * (2 * i + 1) / n1;
        A = -cos(al);
        be = Pih * (2 * j + 1) / n2;
        B = -cos(be);
        phi = 2. * Pi * k / n3;

        Am1 = A - 1;
        for (ivar = 0; ivar < nvar; ivar++)
        {
          indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
          dU.d0[ivar] = Am1 * dv.d0[indx];                     /* dU*/
          dU.d1[ivar] = dv.d0[indx] + Am1 * dv.d1[indx];       /* dU_A*/
          dU.d2[ivar] = Am1 * dv.d2[indx];                     /* dU_B*/
          dU.d3[ivar] = Am1 * dv.d3[indx];                     /* dU_3*/
          dU.d11[ivar] = 2 * dv.d1[indx] + Am1 * dv.d11[indx]; /* dU_AA*/
          dU.d12[ivar] = dv.d2[indx] + Am1 * dv.d12[indx];     /* dU_AB*/
          dU.d13[ivar] = dv.d3[indx] + Am1 * dv.d13[indx];     /* dU_AB*/
          dU.d22[ivar] = Am1 * dv.d22[indx];                   /* dU_BB*/
          dU.d23[ivar] = Am1 * dv.d23[indx];                   /* dU_B3*/
          dU.d33[ivar] = Am1 * dv.d33[indx];                   /* dU_33*/
          U.d0[ivar] = u.d0[indx];                             /* U   */
          U.d1[ivar] = u.d1[indx];                             /* U_x*/
          U.d2[ivar] = u.d2[indx];                             /* U_y*/
          U.d3[ivar] = u.d3[indx];                             /* U_z*/
          U.d11[ivar] = u.d11[indx];                           /* U_xx*/
          U.d12[ivar] = u.d12[indx];                           /* U_xy*/
          U.d13[ivar] = u.d13[indx];                           /* U_xz*/
          U.d22[ivar] = u.d22[indx];                           /* U_yy*/
          U.d23[ivar] = u.d23[indx];                           /* U_yz*/
          U.d33[ivar] = u.d33[indx];                           /* U_zz*/
        }
        /* Calculation of (X,R) and*/
        /* (dU_X, dU_R, dU_3, dU_XX, dU_XR, dU_X3, dU_RR, dU_R3, dU_33)*/
        AB_To_XR(nvar, A, B, &X, &R, dU);
        /* Calculation of (x,r) and*/
        /* (dU, dU_x, dU_r, dU_3, dU_xx, dU_xr, dU_x3, dU_rr, dU_r3, dU_33)*/
        C_To_c(nvar, X, R, &x, &r, dU);
        /* Calculation of (y,z) and*/
        /* (dU, dU_x, dU_y, dU_z, dU_xx, dU_xy, dU_xz, dU_yy, dU_yz, dU_zz)*/
        rx3_To_xyz(nvar, x, r, phi, &y, &z, dU);
        LinEquations(A, B, X, R, x, r, phi, y, z, dU, U, values);
        for (ivar = 0; ivar < nvar; ivar++)
        {
          indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
          Jdv[indx] = values[ivar] * FAC;
        }
      }
    }
    free_dvector(values, 0, nvar - 1);
    free_derivs(&dU, nvar);
    free_derivs(&U, nvar);
  }
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::relax(double *dv, int const nvar, int const n1, int const n2, int const n3,
                         double const *rhs, int const *ncols, int **cols, double **JFD)
{
  int i, j, k, n;

  for (k = 0; k < n3; k = k + 2)
  {
    for (n = 0; n < N_PlaneRelax; n++)
    {
      for (i = 2; i < n1; i = i + 2)
        LineRelax_be(dv, i, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (i = 1; i < n1; i = i + 2)
        LineRelax_be(dv, i, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (j = 1; j < n2; j = j + 2)
        LineRelax_al(dv, j, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (j = 0; j < n2; j = j + 2)
        LineRelax_al(dv, j, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
    }
  }
  for (k = 1; k < n3; k = k + 2)
  {
    for (n = 0; n < N_PlaneRelax; n++)
    {
      for (i = 0; i < n1; i = i + 2)
        LineRelax_be(dv, i, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (i = 1; i < n1; i = i + 2)
        LineRelax_be(dv, i, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (j = 1; j < n2; j = j + 2)
        LineRelax_al(dv, j, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
      for (j = 0; j < n2; j = j + 2)
        LineRelax_al(dv, j, k, nvar, n1, n2, n3, rhs, ncols, cols, JFD);
    }
  }
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::LineRelax_be(double *dv,
                                int const i, int const k, int const nvar,
                                int const n1, int const n2, int const n3,
                                double const *rhs, int const *ncols, int **cols,
                                double **JFD)
{
  int j, m, Ic, Ip, Im, col, ivar;

  double *diag = new double[n2];
  double *e = new double[n2 - 1]; /* above diagonal */
  double *f = new double[n2 - 1]; /* below diagonal */
  double *b = new double[n2];     /* rhs */
  double *x = new double[n2];     /* solution vector */

  //  gsl_vector *diag = gsl_vector_alloc(n2);
  //  gsl_vector *e = gsl_vector_alloc(n2-1); /* above diagonal */
  //  gsl_vector *f = gsl_vector_alloc(n2-1); /* below diagonal */
  //  gsl_vector *b = gsl_vector_alloc(n2);   /* rhs */
  //  gsl_vector *x = gsl_vector_alloc(n2);   /* solution vector */

  for (ivar = 0; ivar < nvar; ivar++)
  {
    for (j = 0; j < n2 - 1; j++)
    {
      diag[j] = e[j] = f[j] = 0;
    }
    diag[n2 - 1] = 0;

    //    gsl_vector_set_zero(diag);
    //    gsl_vector_set_zero(e);
    //    gsl_vector_set_zero(f);
    for (j = 0; j < n2; j++)
    {
      Ip = Index(ivar, i, j + 1, k, nvar, n1, n2, n3);
      Ic = Index(ivar, i, j, k, nvar, n1, n2, n3);
      Im = Index(ivar, i, j - 1, k, nvar, n1, n2, n3);
      b[j] = rhs[Ic];
      //      gsl_vector_set(b,j,rhs[Ic]);
      for (m = 0; m < ncols[Ic]; m++)
      {
        col = cols[Ic][m];
        if (col != Ip && col != Ic && col != Im)
          b[j] -= JFD[Ic][m] * dv[col];
        //          *gsl_vector_ptr(b, j) -= JFD[Ic][m] * dv[col];
        else
        {
          if (col == Im && j > 0)
            f[j - 1] = JFD[Ic][m];
          //            gsl_vector_set(f,j-1,JFD[Ic][m]);
          if (col == Ic)
            diag[j] = JFD[Ic][m];
          //            gsl_vector_set(diag,j,JFD[Ic][m]);
          if (col == Ip && j < n2 - 1)
            e[j] = JFD[Ic][m];
          //            gsl_vector_set(e,j,JFD[Ic][m]);
        }
      }
    }
    //          A x = b
    //          A = ( d_0 e_0  0   0  )
    //              ( f_0 d_1 e_1  0  )
    //              (  0  f_1 d_2 e_2 )
    //              (  0   0  f_2 d_3 )
    //
    ThomasAlgorithm(n2, f, diag, e, x, b);
    //    gsl_linalg_solve_tridiag(diag, e, f, b, x);
    for (j = 0; j < n2; j++)
    {
      Ic = Index(ivar, i, j, k, nvar, n1, n2, n3);
      dv[Ic] = x[j];
      //      dv[Ic] = gsl_vector_get(x, j);
    }
  }

  delete[] diag;
  delete[] e;
  delete[] f;
  delete[] b;
  delete[] x;
  //  gsl_vector_free(diag);
  //  gsl_vector_free(e);
  //  gsl_vector_free(f);
  //  gsl_vector_free(b);
  //  gsl_vector_free(x);
}
/* --------------------------------------------------------------------------*/
void TwoPunctures::JFD_times_dv(int i, int j, int k, int nvar, int n1, int n2,
                                int n3, derivs dv, derivs u, double *values)
{ /* Calculates rows of the vector 'J(FD)*dv'.*/
  /* First row to be calculated: row = Index(0,      i, j, k; nvar, n1, n2, n3)*/
  /* Last  row to be calculated: row = Index(nvar-1, i, j, k; nvar, n1, n2, n3)*/
  /* These rows are stored in the vector JFDdv[0] ... JFDdv[nvar-1].*/
  int ivar, indx;
  double al, be, A, B, X, R, x, r, phi, y, z, Am1;
  double sin_al, sin_al_i1, sin_al_i2, sin_al_i3, cos_al;
  double sin_be, sin_be_i1, sin_be_i2, sin_be_i3, cos_be;
  double dV0, dV1, dV2, dV3, dV11, dV12, dV13, dV22, dV23, dV33,
      ha, ga, ga2, hb, gb, gb2, hp, gp, gp2, gagb, gagp, gbgp;
  derivs dU, U;

  allocate_derivs(&dU, nvar);
  allocate_derivs(&U, nvar);

  if (k < 0)
    k = k + n3;
  if (k >= n3)
    k = k - n3;

  ha = Pi / n1; /* ha: Stepsize with respect to (al)*/
  al = ha * (i + 0.5);
  A = -cos(al);
  ga = 1 / ha;
  ga2 = ga * ga;

  hb = Pi / n2; /* hb: Stepsize with respect to (be)*/
  be = hb * (j + 0.5);
  B = -cos(be);
  gb = 1 / hb;
  gb2 = gb * gb;
  gagb = ga * gb;

  hp = 2 * Pi / n3; /* hp: Stepsize with respect to (phi)*/
  phi = hp * j;
  gp = 1 / hp;
  gp2 = gp * gp;
  gagp = ga * gp;
  gbgp = gb * gp;

  sin_al = sin(al);
  sin_be = sin(be);
  sin_al_i1 = 1 / sin_al;
  sin_be_i1 = 1 / sin_be;
  sin_al_i2 = sin_al_i1 * sin_al_i1;
  sin_be_i2 = sin_be_i1 * sin_be_i1;
  sin_al_i3 = sin_al_i1 * sin_al_i2;
  sin_be_i3 = sin_be_i1 * sin_be_i2;
  cos_al = -A;
  cos_be = -B;

  Am1 = A - 1;
  for (ivar = 0; ivar < nvar; ivar++)
  {
    int iccc = Index(ivar, i, j, k, nvar, n1, n2, n3),
        ipcc = Index(ivar, i + 1, j, k, nvar, n1, n2, n3),
        imcc = Index(ivar, i - 1, j, k, nvar, n1, n2, n3),
        icpc = Index(ivar, i, j + 1, k, nvar, n1, n2, n3),
        icmc = Index(ivar, i, j - 1, k, nvar, n1, n2, n3),
        iccp = Index(ivar, i, j, k + 1, nvar, n1, n2, n3),
        iccm = Index(ivar, i, j, k - 1, nvar, n1, n2, n3),
        icpp = Index(ivar, i, j + 1, k + 1, nvar, n1, n2, n3),
        icmp = Index(ivar, i, j - 1, k + 1, nvar, n1, n2, n3),
        icpm = Index(ivar, i, j + 1, k - 1, nvar, n1, n2, n3),
        icmm = Index(ivar, i, j - 1, k - 1, nvar, n1, n2, n3),
        ipcp = Index(ivar, i + 1, j, k + 1, nvar, n1, n2, n3),
        imcp = Index(ivar, i - 1, j, k + 1, nvar, n1, n2, n3),
        ipcm = Index(ivar, i + 1, j, k - 1, nvar, n1, n2, n3),
        imcm = Index(ivar, i - 1, j, k - 1, nvar, n1, n2, n3),
        ippc = Index(ivar, i + 1, j + 1, k, nvar, n1, n2, n3),
        impc = Index(ivar, i - 1, j + 1, k, nvar, n1, n2, n3),
        ipmc = Index(ivar, i + 1, j - 1, k, nvar, n1, n2, n3),
        immc = Index(ivar, i - 1, j - 1, k, nvar, n1, n2, n3);
    /* Derivatives of (dv) w.r.t. (al,be,phi):*/
    dV0 = dv.d0[iccc];
    dV1 = 0.5 * ga * (dv.d0[ipcc] - dv.d0[imcc]);
    dV2 = 0.5 * gb * (dv.d0[icpc] - dv.d0[icmc]);
    dV3 = 0.5 * gp * (dv.d0[iccp] - dv.d0[iccm]);
    dV11 = ga2 * (dv.d0[ipcc] + dv.d0[imcc] - 2 * dv.d0[iccc]);
    dV22 = gb2 * (dv.d0[icpc] + dv.d0[icmc] - 2 * dv.d0[iccc]);
    dV33 = gp2 * (dv.d0[iccp] + dv.d0[iccm] - 2 * dv.d0[iccc]);
    dV12 =
        0.25 * gagb * (dv.d0[ippc] - dv.d0[ipmc] + dv.d0[immc] - dv.d0[impc]);
    dV13 =
        0.25 * gagp * (dv.d0[ipcp] - dv.d0[imcp] + dv.d0[imcm] - dv.d0[ipcm]);
    dV23 =
        0.25 * gbgp * (dv.d0[icpp] - dv.d0[icpm] + dv.d0[icmm] - dv.d0[icmp]);
    /* Derivatives of (dv) w.r.t. (A,B,phi):*/
    dV11 = sin_al_i3 * (sin_al * dV11 - cos_al * dV1);
    dV12 = sin_al_i1 * sin_be_i1 * dV12;
    dV13 = sin_al_i1 * dV13;
    dV22 = sin_be_i3 * (sin_be * dV22 - cos_be * dV2);
    dV23 = sin_be_i1 * dV23;
    dV1 = sin_al_i1 * dV1;
    dV2 = sin_be_i1 * dV2;
    /* Derivatives of (dU) w.r.t. (A,B,phi):*/
    dU.d0[ivar] = Am1 * dV0;
    dU.d1[ivar] = dV0 + Am1 * dV1;
    dU.d2[ivar] = Am1 * dV2;
    dU.d3[ivar] = Am1 * dV3;
    dU.d11[ivar] = 2 * dV1 + Am1 * dV11;
    dU.d12[ivar] = dV2 + Am1 * dV12;
    dU.d13[ivar] = dV3 + Am1 * dV13;
    dU.d22[ivar] = Am1 * dV22;
    dU.d23[ivar] = Am1 * dV23;
    dU.d33[ivar] = Am1 * dV33;

    indx = Index(ivar, i, j, k, nvar, n1, n2, n3);
    U.d0[ivar] = u.d0[indx];   /* U   */
    U.d1[ivar] = u.d1[indx];   /* U_x*/
    U.d2[ivar] = u.d2[indx];   /* U_y*/
    U.d3[ivar] = u.d3[indx];   /* U_z*/
    U.d11[ivar] = u.d11[indx]; /* U_xx*/
    U.d12[ivar] = u.d12[indx]; /* U_xy*/
    U.d13[ivar] = u.d13[indx]; /* U_xz*/
    U.d22[ivar] = u.d22[indx]; /* U_yy*/
    U.d23[ivar] = u.d23[indx]; /* U_yz*/
    U.d33[ivar] = u.d33[indx]; /* U_zz*/
  }
  /* Calculation of (X,R) and*/
  /* (dU_X, dU_R, dU_3, dU_XX, dU_XR, dU_X3, dU_RR, dU_R3, dU_33)*/
  AB_To_XR(nvar, A, B, &X, &R, dU);
  /* Calculation of (x,r) and*/
  /* (dU, dU_x, dU_r, dU_3, dU_xx, dU_xr, dU_x3, dU_rr, dU_r3, dU_33)*/
  C_To_c(nvar, X, R, &x, &r, dU);
  /* Calculation of (y,z) and*/
  /* (dU, dU_x, dU_y, dU_z, dU_xx, dU_xy, dU_xz, dU_yy, dU_yz, dU_zz)*/
  rx3_To_xyz(nvar, x, r, phi, &y, &z, dU);
  LinEquations(A, B, X, R, x, r, phi, y, z, dU, U, values);
  for (ivar = 0; ivar < nvar; ivar++)
    values[ivar] *= FAC;

  free_derivs(&dU, nvar);
  free_derivs(&U, nvar);
}
#undef FAC
/*-----------------------------------------------------------*/
/********               Linear Equations                ***********/
/*-----------------------------------------------------------*/
void TwoPunctures::LinEquations(double A, double B, double X, double R,
                                double x, double r, double phi,
                                double y, double z, derivs dU, derivs U, double *values)
{
  double r_plus, r_minus, psi, psi2, psi4, psi8;

  r_plus = sqrt((x - par_b) * (x - par_b) + y * y + z * z);
  r_minus = sqrt((x + par_b) * (x + par_b) + y * y + z * z);

  psi =
      1. + 0.5 * par_m_plus / r_plus + 0.5 * par_m_minus / r_minus + U.d0[0];
  psi2 = psi * psi;
  psi4 = psi2 * psi2;
  psi8 = psi4 * psi4;

  values[0] = dU.d11[0] + dU.d22[0] + dU.d33[0] - 0.875 * BY_KKofxyz(x, y, z) / psi8 * dU.d0[0];
}
/* -------------------------------------------------------------------------*/
void TwoPunctures::LineRelax_al(double *dv,
                                int const j, int const k, int const nvar,
                                int const n1, int const n2, int const n3,
                                double const *rhs, int const *ncols,
                                int **cols, double **JFD)
{
  int i, m, Ic, Ip, Im, col, ivar;

  double *diag = new double[n1];
  double *e = new double[n1 - 1]; /* above diagonal */
  double *f = new double[n1 - 1]; /* below diagonal */
  double *b = new double[n1];     /* rhs */
  double *x = new double[n1];     /* solution vector */

  //  gsl_vector *diag = gsl_vector_alloc(n1);
  //  gsl_vector *e = gsl_vector_alloc(n1-1); /* above diagonal */
  //  gsl_vector *f = gsl_vector_alloc(n1-1); /* below diagonal */
  //  gsl_vector *b = gsl_vector_alloc(n1);   /* rhs */
  //  gsl_vector *x = gsl_vector_alloc(n1);   /* solution vector */

  for (ivar = 0; ivar < nvar; ivar++)
  {
    for (i = 0; i < n1 - 1; i++)
    {
      diag[i] = e[i] = f[i] = 0;
    }
    diag[n1 - 1] = 0;

    //    gsl_vector_set_zero(diag);
    //    gsl_vector_set_zero(e);
    //    gsl_vector_set_zero(f);
    for (i = 0; i < n1; i++)
    {
      Ip = Index(ivar, i + 1, j, k, nvar, n1, n2, n3);
      Ic = Index(ivar, i, j, k, nvar, n1, n2, n3);
      Im = Index(ivar, i - 1, j, k, nvar, n1, n2, n3);
      b[i] = rhs[Ic];
      //      gsl_vector_set(b,i,rhs[Ic]);
      for (m = 0; m < ncols[Ic]; m++)
      {
        col = cols[Ic][m];
        if (col != Ip && col != Ic && col != Im)
          b[i] -= JFD[Ic][m] * dv[col];
        //          *gsl_vector_ptr(b, i) -= JFD[Ic][m] * dv[col];
        else
        {
          if (col == Im && i > 0)
            f[i - 1] = JFD[Ic][m];
          //            gsl_vector_set(f,i-1,JFD[Ic][m]);
          if (col == Ic)
            diag[i] = JFD[Ic][m];
          //            gsl_vector_set(diag,i,JFD[Ic][m]);
          if (col == Ip && i < n1 - 1)
            e[i] = JFD[Ic][m];
          //            gsl_vector_set(e,i,JFD[Ic][m]);
        }
      }
    }
    ThomasAlgorithm(n1, f, diag, e, x, b);
    //    gsl_linalg_solve_tridiag(diag, e, f, b, x);
    for (i = 0; i < n1; i++)
    {
      Ic = Index(ivar, i, j, k, nvar, n1, n2, n3);
      dv[Ic] = x[i];
      //      dv[Ic] = gsl_vector_get(x, i);
    }
  }

  delete[] diag;
  delete[] e;
  delete[] f;
  delete[] b;
  delete[] x;

  //  gsl_vector_free(diag);
  //  gsl_vector_free(e);
  //  gsl_vector_free(f);
  //  gsl_vector_free(b);
  //  gsl_vector_free(x);
}
/* -------------------------------------------------------------------------*/
// a[N], b[N-1], c[N-1], x[N], q[N]
//          A x = q
//          A = ( a_0 c_0  0   0  )
//              ( b_0 a_1 c_1  0  )
//              (  0  b_1 a_2 c_2 )
//              (  0   0  b_2 a_3 )
//"Parallel Scientific Computing in C++ and MPI" P361
void TwoPunctures::ThomasAlgorithm(int N, double *b, double *a, double *c, double *x, double *q)
{
  int i;
  double *l, *u, *d, *y;
  l = new double[N - 1];
  u = new double[N - 1];
  d = new double[N];
  y = new double[N];

  /* LU Decomposition */
  d[0] = a[0];
  u[0] = c[0];

  for (i = 0; i < N - 2; i++)
  {
    l[i] = b[i] / d[i];
    d[i + 1] = a[i + 1] - l[i] * u[i];
    u[i + 1] = c[i + 1];
  }

  l[N - 2] = b[N - 2] / d[N - 2];
  d[N - 1] = a[N - 1] - l[N - 2] * u[N - 2];

  /* Forward Substitution [L][y] = [q] */
  y[0] = q[0];
  for (i = 1; i < N; i++)
    y[i] = q[i] - l[i - 1] * y[i - 1];

  /* Backward Substitution [U][x] = [y] */
  x[N - 1] = y[N - 1] / d[N - 1];

  for (i = N - 2; i >= 0; i--)
    x[i] = (y[i] - u[i] * x[i + 1]) / d[i];

  delete[] l;
  delete[] u;
  delete[] d;
  delete[] y;

  return;
}
// --------------------------------------------------------------------------*/
// Calculates the value of v at an arbitrary position (x,y,z) if the spectral coefficients are know*/*/
/* --------------------------------------------------------------------------*/
/* Calculates the value of v at an arbitrary position (A,B,phi)*/
double TwoPunctures::Spec_IntPolABphiFast(parameters par, double *v, int ivar, double A, double B, double phi)
{
  int i, j, k, N;
  double *p, *values1, **values2, result;

  int nvar = par.nvar;
  int n1 = par.n1;
  int n2 = par.n2;
  int n3 = par.n3;
  N = maximum3(n1, n2, n3);

  p = dvector(0, N);
  values1 = dvector(0, N);
  values2 = dmatrix(0, N, 0, N);

  for (k = 0; k < n3; k++)
  {
    for (j = 0; j < n2; j++)
    {
      for (i = 0; i < n1; i++)
        p[i] = v[ivar + nvar * (i + n1 * (j + n2 * k))];
      //      chebft_Zeros (p, n1, 0);
      values2[j][k] = chebev(-1, 1, p, n1, A);
    }
  }

  for (k = 0; k < n3; k++)
  {
    for (j = 0; j < n2; j++)
      p[j] = values2[j][k];
    //    chebft_Zeros (p, n2, 0);
    values1[k] = chebev(-1, 1, p, n2, B);
  }

  //  fourft (values1, n3, 0);
  result = fourev(values1, n3, phi);

  free_dvector(p, 0, N);
  free_dvector(values1, 0, N);
  free_dmatrix(values2, 0, N, 0, N);

  return result;
  //  */
  //  return 0.;
}

/* Calculates the value of v at an arbitrary position (x,y,z) given the spectral coefficients*/
double TwoPunctures::Spec_IntPolFast(parameters par, int ivar, double *v, double x, double y, double z)
{
  double xs, ys, zs, rs2, phi, X, R, A, B, aux1, aux2, result, Ui;

  int nvar = par.nvar;
  int n1 = par.n1;
  int n2 = par.n2;
  int n3 = par.n3;
  double par_b = par.b;

  xs = x / par.b;
  ys = y / par.b;
  zs = z / par.b;
  rs2 = ys * ys + zs * zs;
  phi = atan2(z, y);
  if (phi < 0)
    phi += 2. * Pi;

  aux1 = 0.5 * (xs * xs + rs2 - 1.);
  aux2 = sqrt(aux1 * aux1 + rs2);

  // Note from YT: aux2-aux1 can be equal to 1. When that happens, numerical
  // truncation may make it slightly larger than 1. This makes
  // R NAN! I also worry that aux2-aux1 and aux1+axu2 may become negative due to
  // truncation error, which gives rise to NAN for X and R.
  // The following few lines attempt to fix these.
  double aux2_plus_aux1, aux2_minus_aux1;
  if (aux1 < 0)
  {
    aux2_plus_aux1 = rs2 / (aux2 - aux1);
    aux2_minus_aux1 = aux2 - aux1;
  }
  else
  {
    aux2_plus_aux1 = aux2 + aux1;
    aux2_minus_aux1 = rs2 / (aux2 + aux1);
  }
  if (fabs(aux1) + fabs(aux2) < 1.e-20)
  {
    aux2_plus_aux1 = 0.0;
    aux2_minus_aux1 = 0.0;
  }
  double sqrt_aux2_minus_aux1 = sqrt(fabs(aux2_minus_aux1));

  // Note from YT: The following two lines have replaced by the 6 lines belows.
  //      X = asinhd(sqrt(aux1+aux2));
  //      R = asin(sqrt(fabs(-aux1+aux2)));

  X = asinh(sqrt(aux2_plus_aux1));
  if (sqrt_aux2_minus_aux1 > 1.0)
  {
    R = 0.5 * Pi;
  }
  else
  {
    R = asin(sqrt_aux2_minus_aux1);
  }

  if (x < 0)
    R = Pi - R;

  A = 2. * tanh(0.5 * X) - 1.;
  B = tan(0.5 * R - Piq);

  result = Spec_IntPolABphiFast(par, v, ivar, A, B, phi);

  Ui = (A - 1) * result;

  return Ui;
}

// Evaluates the spectral expansion coefficients of v
void TwoPunctures::SpecCoef(parameters par, int ivar, double *v, double *cf)
{
  // Here v is a pointer to the values of the variable v at the collocation points
  int i, j, k, N, n, l, m;
  double *p, ***values3, ***values4;

  int nvar = par.nvar;
  int n1 = par.n1;
  int n2 = par.n2;
  int n3 = par.n3;

  N = maximum3(n1, n2, n3);
  p = dvector(0, N);
  values3 = d3tensor(0, n1, 0, n2, 0, n3);
  values4 = d3tensor(0, n1, 0, n2, 0, n3);

  // Caclulate values3[n,j,k] = a_n^{j,k} = (sum_i^(n1-1) f(A_i,B_j,phi_k) Tn(-A_i))/k_n , k_n = N/2 or N
  for (k = 0; k < n3; k++)
  {
    for (j = 0; j < n2; j++)
    {
      for (i = 0; i < n1; i++)
        p[i] = v[ivar + (i + n1 * (j + n2 * k))];
      chebft_Zeros(p, n1, 0);
      for (n = 0; n < n1; n++)
      {
        values3[n][j][k] = p[n];
      }
    }
  }

  // Caclulate values4[n,l,k] = a_{n,l}^{k} = (sum_j^(n2-1) a_n^{j,k} Tn(B_j))/k_l , k_l = N/2 or N

  for (n = 0; n < n1; n++)
  {
    for (k = 0; k < n3; k++)
    {
      for (j = 0; j < n2; j++)
        p[j] = values3[n][j][k];
      chebft_Zeros(p, n2, 0);
      for (l = 0; l < n2; l++)
      {
        values4[n][l][k] = p[l];
      }
    }
  }

  // Caclulate coefficients  a_{n,l,m} = (sum_k^(n3-1) a_{n,m}^{k} fourier(phi_k))/k_m , k_m = N/2 or N
  for (i = 0; i < n1; i++)
  {
    for (j = 0; j < n2; j++)
    {
      for (k = 0; k < n3; k++)
        p[k] = values4[i][j][k];
      fourft(p, n3, 0);
      for (k = 0; k < n3; k++)
      {
        cf[ivar + (i + n1 * (j + n2 * k))] = p[k];
      }
    }
  }

  free_dvector(p, 0, N);
  free_d3tensor(values3, 0, n1, 0, n2, 0, n3);
  free_d3tensor(values4, 0, n1, 0, n2, 0, n3);
}