/*
* Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
* Copyright (C) 2005-2008 - INRIA - Serge STEER
* Copyright (C) 2011 - DIGITEO - Allan CORNET
*
* This file must be used under the terms of the CeCILL.
* This source file is licensed as described in the file COPYING, which
* you should have received as part of this distribution.  The terms
* are also available at
* http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
*
*/
/*--------------------------------------------------------------------------*/
#include <string.h>
#include "zbeshv.h"
#include "machine.h"
#include "core_math.h"
#include "returnanan.h"
/*--------------------------------------------------------------------------*/
/* fortran subroutines */
extern int C2F(dset)();
extern int C2F(dscal)();
extern int C2F(wscal)();
extern int C2F(zbesh)();
extern int C2F(dcopy)();
extern double C2F(dlamch)();
/*--------------------------------------------------------------------------*/
static int zbeshg(double *x1r, double *x1i, double *alpha,
                  int *kode, int *k, int *n, double *yr,
                  double *yi, int *nz, double *wr,
                  double *wi, int *ierr);
/*--------------------------------------------------------------------------*/
/* compute besseli function for x and alpha given by vectors */
/* w : working array of size 2*na (used only if nz>0 and alpha */
/* contains negative values */
int zbeshv(double *xr, double *xi, int *nx,
           double *alpha, int *na, int *kode, int *k, double
           *yr, double *yi, double *wr, double *wi, int *ierr)
{
    double eps = C2F(dlamch)("p", strlen("p"));
    int iOne = 1;
    int i = 0, j = 0, nz = 0;

    *ierr = 0;
    if (*na < 0)
    {
        /* element wise case x and alpha are supposed to have the same size */
        for (i = 1; i <= *nx; ++i)
        {
            int ier = 0;
            zbeshg(&xr[i - 1], &xi[i - 1], &alpha[i - 1], kode, k, &iOne, &yr[i - 1],
                   &yi[i - 1], &nz, &wr[1], &wi[0], &ier);
            *ierr = Max(*ierr, ier);
        }
    }
    else if (*na == 1)
    {
        for (i = 1; i <= *nx; ++i)
        {
            int ier = 0;
            zbeshg(&xr[i - 1], &xi[i - 1], &alpha[0], kode, k, &iOne, &yr[i - 1],
                   &yi[i - 1], &nz, &wr[0], &wi[0], &ier);
            *ierr = Max(*ierr, ier);
        }
    }
    else
    {
        /* compute besselh(x(i),y(j)), i=1,nx,j=1,na */
        double dTmp = 0;
        int n = 0;
        int  l = 1;
L5:
        n = 0;
L10:
        ++n;
        j = l + n;
        if (j <= *na && (dTmp = alpha[j] + 1 - alpha[j - 1], fabs(dTmp)) <= eps)
        {
            goto L10;
        }
        for (i = 1; i <= *nx; ++i)
        {
            int ier = 0;
            zbeshg(&xr[i - 1], &xi[i - 1], &alpha[l - 1], kode, k, &n, &wr[0], &wi[0],
                   &nz, &wr[*na], &wi[*na], &ier);

            *ierr = Max(*ierr, ier);

            C2F(dcopy)(&n, &wr[0], &iOne, &yr[(i + (l - 1) * *nx) - 1], nx);

            C2F(dcopy)(&n, &wi[0], &iOne, &yi[(i + (l - 1) * *nx) - 1], nx);
        }

        l = j;

        if (l <= *na)
        {
            goto L5;
        }
    }
    return 0;
}
/*--------------------------------------------------------------------------*/
static int zbeshg(double *x1r, double *x1i, double *alpha,
                  int *kode, int *k, int *n, double *yr,
                  double *yi, int *nz, double *wr,
                  double *wi, int *ierr)
{
    int iOne = 1;
    int iTwo = 2;
    double dNegOne = -1.;

    double nan = C2F(returnanan)();

    int iVal = 0;
    int nn = 0;
    double xr = *x1r;
    double xi = *x1i;

    /* extends cbesi for the case where alpha is negative */

    if (ISNAN(xr) || ISNAN(xi) || ISNAN(*alpha))
    {
        /* NaN case */
        C2F(dset)(n, &nan, &yr[0], &iOne);
        C2F(dset)(n, &nan, &yi[0], &iOne);

        *ierr = 4;
    }
    else if (*alpha >= 0.)
    {
        C2F(zbesh)(&xr, &xi, alpha, kode, k, n, &yr[0], &yi[0], nz, ierr);
        if (*ierr == 1 || *ierr == 2 || *ierr >= 4)
        {
            C2F(dset)(n, &nan, &yr[0], &iOne);
            C2F(dset)(n, &nan, &yi[0], &iOne);
        }
    }
    else if (*alpha == (int)(*alpha))
    {
        double a1 = 0.;
        /* alpha < 0 and int, */
        /*  transform to positive value of alpha */
        if (*alpha - 1 + *n >= 0.)
        {
            /* 0 is between alpha and alpha+n */
            a1 = 0.;
            /* Computing MIN */
            nn = Min(*n, (int) (-(*alpha)));
        }
        else
        {
            a1 = -(*alpha - 1 + *n);
            nn = *n;
        }
        C2F(zbesh)(&xr, &xi, &a1, kode, k, n, &wr[0], &wi[0], nz, ierr);
        if (*ierr == 1 || *ierr == 2 || *ierr >= 4)
        {
            C2F(dset)(n, &nan, &yr[0], &iOne);
            C2F(dset)(n, &nan, &yi[0], &iOne);
        }
        else
        {
            if (*n > nn)
            {
                /* 0 is between alpha and alpha+n */
                iVal = *n - nn;
                C2F(dcopy)(&iVal, &wr[0], &iOne, &yr[nn], &iOne);
                C2F(dcopy)(&iVal, &wi[0], &iOne, &yi[nn], &iOne);
                C2F(dcopy)(&nn, &wr[1], &iOne, &yr[0], &iOne);
                C2F(dcopy)(&nn, &wi[1], &iOne, &yi[0], &iOne);
            }
            else
            {
                /* alpha and alpha+n are negative */
                C2F(dcopy)(n, &wr[0], &iOne, &yr[0], &iOne);
                C2F(dcopy)(n, &wi[0], &iOne, &yi[0], &iOne);
            }
        }
        iVal = (nn - (((int) fabs(*alpha) + 1) % 2) + 1) / 2;
        C2F(dscal)(&iVal, &dNegOne, &yr[((int) fabs(*alpha) + 1) % 2], &iTwo);
        C2F(dscal)(&iVal, &dNegOne, &yi[((int) fabs(*alpha) + 1) % 2], &iTwo);
    }
    else
    {
        int nz1 = 0;
        double a1 = 0.;
        /* first alpha is negative non int, transform to positive value of alpha */
        if (*alpha - 1. + *n >= 0.)
        {
            /* 0 is between alpha and alpha+n */
            nn = (int) (-(*alpha)) + 1;
        }
        else
        {
            nn = *n;
        }

        /* compute for negative value of alpha+k, transform problem for */
        /* a1:a1+(nn-1) with a1 positive  a1+k =abs(alpha+nn-k) */
        a1 = -(*alpha - 1. + nn);
        C2F(zbesh)(&xr, &xi, &a1, kode, k, n, &wr[0], &wi[0], &nz1, ierr);
        *nz = Max(nz1, 0);
        if (*ierr == 0)
        {
            double a = cos(a1 * M_PI);
            double b = sin(a1 * M_PI);
            if (*k == 1)
            {
                C2F(wscal)(&nn, &a, &b, &wr[0], &wi[0], &iOne);
            }
            else
            {
                double dNegB = -b;
                C2F(wscal)(&nn, &a, &dNegB, &wr[0], &wi[0], &iOne);
            }
            /* change sign to take into account that sin((a1+k)*pi) and cos((a1+k)*pi) */
            /* changes sign with k */
            if (nn >= 2)
            {
                iVal = nn / 2;
                C2F(dscal)(&iVal, &dNegOne, &wr[1], &iTwo);
                C2F(dscal)(&iVal, &dNegOne, &wi[1], &iTwo);
            }
        }
        else if (*ierr == 1 || *ierr == 2 || *ierr >= 4)
        {
            C2F(dset)(&nn, &nan, &wr[0], &iOne);
            C2F(dset)(&nn, &nan, &wi[0], &iOne);
        }

        /* store the result in the correct order */
        C2F(dcopy)(&nn, &wr[0], &iOne, &yr[0], &iOne);
        C2F(dcopy)(&nn, &wi[0], &iOne, &yi[0], &iOne);

        /* compute for positive value of alpha+k is any */
        if (*n > nn)
        {
            int ier = 0;
            a1 = 1. - a1;
            iVal = *n - nn;
            C2F(zbesh)(&xr, &xi, &a1, kode, k, &iVal, &yr[nn], &yi[nn], nz, &ier);
            if (ier == 1 || ier == 2 || ier >= 4)
            {
                iVal = *n - nn;
                C2F(dset)(&iVal, &nan, &yr[nn], &iOne);
                C2F(dset)(&iVal, &nan, &yi[nn], &iOne);
            }
            *ierr = Max(*ierr, ier);
        }
    }
    return 0;
}
/*--------------------------------------------------------------------------*/
