//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : crossover_class.c
// Author      : Thomas Pfahler (TPf)
// Last change : TPf, Nov 21, 1996, initial version
//

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:Fp_polynomial.h>
#else
#include <LiDIA/Fp_polynomial.h>
#endif


crossover_class Fp_polynomial::crossovers;


void spline(const int x[], const int y[], int n, double yp1, double ypn,
    double y2[])
//see Numerical Recipes in C, ISBN 0-521-43108-5, p. 113ff
{
    int i, k;
    double p, qn, sig, un;
    double *u = new double[n];
    if (!u)
	lidia_error_handler("Fp_polynomial", "spline::out of memory");

    if (yp1 > 0.99e30)
	y2[0] = u[0] = 0.0;
    else
    {
	y2[0] = -0.5;
	u[0] = (3.0/(x[1]-x[0])) * double((y[1]-y[0]) / double(x[1]-x[0]) - yp1);
    }
    
    for (i = 1; i < n-1; i++)
    {
	sig = double(x[i]-x[i-1]) / double(x[i+1]-x[i-1]);
	p = sig*y2[i-1]+2.0;
	y2[i] = (sig-1.0)/p;
	u[i] = double(y[i+1]-y[i]) / double(x[i+1]-x[i])
		- double(y[i]-y[i-1]) / double(x[i]-x[i-1]);
	u[i] = (6.0 * u[i] / double(x[i+1]-x[i-1]) - sig * u[i-1]) / p;
    }
    if (ypn > 0.99e30)
	qn = un = 0.0;
    else
    {
	qn = 0.5;
	un = (3.0 / double(x[n-1]-x[n-2]))
	    * (ypn - double(y[n-1]-y[n-2]) / double(x[n-1]-x[n-2]));
    }
    y2[n-1] = (un-qn*u[n-2])/(qn*y2[n-2]+1.0);
    for (k = n-2; k >= 0; k--)
	y2[k] = y2[k]*y2[k+1]+u[k];
}

double splint(const int xa[], const int ya[], const double y2a[], int n,
    double x)
//see Numerical Recipes in C, ISBN 0-521-43108-5, p. 113ff
{
    int klo, khi, k;
    double h, b, a, y;

    klo = 0;
    khi = n-1;
    while (khi-klo > 1)
    {
	k = (khi+klo) >> 1;
	if (xa[k] > x) khi = k;
	else           klo = k;
    }
    h = xa[khi]-xa[klo];
    if (h == 0.0)
	lidia_error_handler("crossover_class","splint(...)::identical x-values for interpolation)");
    a = double(xa[khi]-x) / h;
    b = double(x-xa[klo]) / h;
    y = a*ya[klo] + b*ya[khi] +
	((a*a*a-a)*y2a[klo] + (b*b*b-b)*y2a[khi]) * (h*h) / 6.0;
    return y;
}



crossover_class::crossover_class()
{
//
#include "crossover.tbl"
//
    init(x_val, fftmul_val, fftdiv_val, inv_val);
    gcd = gcd_val;
    halfgcd = halfgcd_val;
    log2_newton = log2_newton_val;
}

void crossover_class::init(const int x_val[CROV_NUM_VALUES],
	const int fftmul_val[CROV_NUM_VALUES],
	const int fftdiv_val[CROV_NUM_VALUES],
	const int inv_val[CROV_NUM_VALUES])
{
    for (int i = 0; i < CROV_NUM_VALUES; i++)
    {
	x[i]        = x_val[i];
	y_fftmul[i] = fftmul_val[i];
	y_fftdiv[i] = fftdiv_val[i];
	y_inv[i]    = inv_val[i];
    }
    spline(x, y_fftmul, CROV_NUM_VALUES, 0.0, 0.0, y2_fftmul);
    spline(x, y_fftdiv, CROV_NUM_VALUES, 0.0, 0.0, y2_fftdiv);
    spline(x,    y_inv, CROV_NUM_VALUES, 0.0, 0.0,    y2_inv);
}

int crossover_class::fftmul_crossover(const bigint &modulus) const
{
    int bl = modulus.bit_length(), r;
    if (bl >= x[CROV_NUM_VALUES])
	r = y_fftmul[CROV_NUM_VALUES];
    else
	r = int( splint(x, y_fftmul, y2_fftmul, CROV_NUM_VALUES, bl) );
    return comparator<int>::max(r, 1);
}
    
int crossover_class::fftdiv_crossover(const bigint &modulus) const
{
    int bl = modulus.bit_length(), r;
    if (bl >= x[CROV_NUM_VALUES])
	r = y_fftdiv[CROV_NUM_VALUES];
    else
	r = int( splint(x, y_fftdiv, y2_fftdiv, CROV_NUM_VALUES, bl) );
    return comparator<int>::max(r, 1);
}
    
int crossover_class::inv_crossover(const bigint &modulus) const
{
    int bl = modulus.bit_length(), r;
    if (bl >= x[CROV_NUM_VALUES])
	r = y_inv[CROV_NUM_VALUES];
    else
	r = int( splint(x, y_inv, y2_inv, CROV_NUM_VALUES, bl) );
    return comparator<int>::max(r, 1);
}
    
int crossover_class::halfgcd_crossover(const bigint &modulus) const
{
//  modulus.bit_length(); //dummy instruction bec. of warning "unused variable"
    return halfgcd;
}

int crossover_class::gcd_crossover(const bigint &modulus) const
{
//  modulus.bit_length(); //dummy instruction bec. of warning "unused variable"
    return gcd;
}

int crossover_class::log2_newton_crossover(const bigint &modulus) const
{
//  modulus.bit_length(); //dummy instruction bec. of warning "unused variable"
    return log2_newton;
}

