/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* ## Notice
*
* The original C++ code and copyright notice are from the [Boost library]{@link https://github.com/boostorg/math/blob/develop/include/boost/math/special_functions/detail/bessel_j1.hpp}. The implementation has been modified for JavaScript.
*
* ```text
* Copyright Xiaogang Zhang, 2006.
*
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
* ```
*/

'use strict';

// MODULES //

var sqrt = require( './../../../../base/special/sqrt' );
var abs = require( './../../../../base/special/abs' );
var sincos = require( './../../../../base/special/sincos' );
var PINF = require( '@stdlib/constants/float64/pinf' );
var SQRT_PI = require( '@stdlib/constants/float64/sqrt-pi' );
var poly1 = require( './rational_p1q1.js' );
var poly2 = require( './rational_p2q2.js' );
var polyC = require( './rational_pcqc.js' );
var polyS = require( './rational_psqs.js' );


// VARIABLES //

var x1 = 3.8317059702075123156e+00;
var x2 = 7.0155866698156187535e+00;
var x11 = 9.810e+02;
var x12 = -3.2527979248768438556e-04;
var x21 = 1.7960e+03;
var x22 = -3.8330184381246462950e-05;

// `sincos` workspace:
var sc = [ 0.0, 0.0 ]; // WARNING: not thread safe


// MAIN //

/**
* Computes the Bessel function of the first kind of order one.
*
* ## Notes
*
* -   Accuracy for subnormal `x` is very poor. Full accuracy is achieved at `1.0e-308` but trends progressively to zero at `5e-324`. This suggests that underflow (or overflow, perhaps due to a reciprocal) is effectively cutting off digits of precision until the computation loses all accuracy at `5e-324`.
*
* @param {number} x - input value
* @returns {number} evaluated Bessel function
*
* @example
* var v = j1( 0.0 );
* // returns 0.0
*
* v = j1( 1.0 );
* // returns ~0.440
*
* v = j1( Infinity );
* // returns 0.0
*
* v = j1( -Infinity );
* // returns 0.0
*
* v = j1( NaN );
* // returns NaN
*/
function j1( x ) {
	var value;
	var rc;
	var rs;
	var y2;
	var r;
	var y;
	var f;
	var w;

	w = abs( x );
	if ( x === 0.0 ) {
		return 0.0;
	}
	if ( w === PINF ) {
		return 0.0;
	}
	if ( w <= 4.0 ) {
		y = x * x;
		r = poly1( y );
		f = w * ( w+x1 ) * ( ( w - (x11/256.0) ) - x12 );
		value = f * r;
	} else if ( w <= 8.0 ) {
		y = x * x;
		r = poly2( y );
		f = w * ( w+x2 ) * ( ( w - (x21/256.0) ) - x22 );
		value = f * r;
	} else {
		y = 8.0 / w;
		y2 = y * y;
		rc = polyC( y2 );
		rs = polyS( y2 );
		f = 1.0 / ( sqrt( w ) * SQRT_PI );

		/*
		* What follows is really just:
		*
		* ```
		* z = w - 0.75 * pi;
		* value = f * ( rc * cos( z ) - y * rs * sin( z ) );
		* ```
		*
		* but using the sin/cos addition rules plus constants for the values of sin/cos of `3π/4` which then cancel out with corresponding terms in "f".
		*/
		sincos( sc, w );
		value = f * ( ( rc * (sc[0]-sc[1]) ) + ( (y*rs) * (sc[0]+sc[1]) ) );
	}
	if ( x < 0.0 ) {
		value *= -1.0;
	}
	return value;
}


// EXPORTS //

module.exports = j1;
