<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */


"use strict";

(/**
 * @param {Window} window
 * @param {undefined} undefined
 */
	function (window, undefined) {
	var cErrorType = AscCommonExcel.cErrorType;
	var cNumber = AscCommonExcel.cNumber;
	var cString = AscCommonExcel.cString;
	var cError = AscCommonExcel.cError;
	var cArea = AscCommonExcel.cArea;
	var cArea3D = AscCommonExcel.cArea3D;
	var cArray = AscCommonExcel.cArray;
	var cUndefined = AscCommonExcel.cUndefined;
	var cBaseFunction = AscCommonExcel.cBaseFunction;
	var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup;

	var rtl_math_erf = AscCommonExcel.rtl_math_erf;

	var rg_validBINNumber = /^[01]{1,10}$/, rg_validDEC2BINNumber = /^-?[0-9]{1,3}$/, rg_validDEC2OCTNumber = /^-?[0-9]{1,9}$/, rg_validDEC2HEXNumber = /^-?[0-9]{1,12}$/, rg_validHEXNumber = /^[0-9A-F]{1,10}$/i, rg_validOCTNumber = /^[0-7]{1,10}$/, rg_complex_number = new XRegExp("^(?<real>[-+]?(?:\\d*(?:\\.\\d+)?(?:[Ee][+-]?\\d+)?))?(?<img>([-+]?(\\d*(?:\\.\\d+)?(?:[Ee][+-]?\\d+)?)?[ij])?)", "g");

	var NumberBase = {
		BIN: 2, OCT: 8, DEC: 10, HEX: 16
	};

	var f_PI_DIV_2 = Math.PI / 2;
	var f_PI_DIV_4 = Math.PI / 4;
	var f_2_DIV_PI = 2 / Math.PI;

	function BesselJ(x, N) {
		if (N < 0) {
			return new cError(cErrorType.not_numeric);
		}
		if (x === 0.0) {
			return new cNumber((N == 0) ? 1 : 0);
		}

		/*  The algorithm works only for x>0, therefore remember sign. BesselJ
		 with integer order N is an even function for even N (means J(-x)=J(x))
		 and an odd function for odd N (means J(-x)=-J(x)).*/
		var fSign = (N % 2 == 1 && x < 0) ? -1 : 1;
		var fX = Math.abs(x);

		var fMaxIteration = 9000000; //experimental, for to return in < 3 seconds
		var fEstimateIteration = fX * 1.5 + N;
		var bAsymptoticPossible = Math.pow(fX, 0.4) > N;
		if (fEstimateIteration > fMaxIteration) {
			if (bAsymptoticPossible) {
				return new cNumber(fSign * Math.sqrt(f_2_DIV_PI / fX) * Math.cos(fX - N * f_PI_DIV_2 - f_PI_DIV_4));
			} else {
				return new cError(cErrorType.not_numeric);
			}
		}

		var epsilon = 1.0e-15; // relative error
		var bHasfound = false, k = 0, u;

		// first used with k=1
		var m_bar, g_bar, g_bar_delta_u, g = 0, delta_u = 0, f_bar = -1;  // f_bar_k = 1/f_k, but only used for k=0

		if (N == 0) {
			u = 1;
			g_bar_delta_u = 0;
			g_bar = -2 / fX;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			k = 2;
		} else {
			u = 0;
			for (k = 1; k <= N - 1; k = k + 1) {
				m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
				g_bar_delta_u = -g * delta_u - m_bar * u; // alpha_k = 0.0
				g_bar = m_bar - 2 * k / fX + g;
				delta_u = g_bar_delta_u / g_bar;
				u = u + delta_u;
				g = -1 / g_bar;
				f_bar = f_bar * g;
			}
			// Step alpha_N = 1.0
			m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
			g_bar_delta_u = f_bar - g * delta_u - m_bar * u; // alpha_k = 1.0
			g_bar = m_bar - 2 * k / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			k = k + 1;
		}
		// Loop until desired accuracy, always alpha_k = 0.0
		do {
			m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
			g_bar_delta_u = -g * delta_u - m_bar * u;
			g_bar = m_bar - 2 * k / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasfound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasfound && k <= fMaxIteration);
		if (bHasfound) {
			return new cNumber(u * fSign);
		} else {
			return new cError(cErrorType.not_numeric);
		}// unlikely to happen
	}

	function BesselI(x, n) {
		var nMaxIteration = 20000, fXHalf = x / 2, fResult = 0, fEpsilon = 1.0E-30;
		if (n < 0) {
			return new cError(cErrorType.not_numeric);
		}

		/*  Start the iteration without TERM(n,0), which is set here.

		 TERM(n,0) = (x/2)^n / n!
		 */
		var nK = 0, fTerm = 1;
		// avoid overflow in Fak(n)
		/*for ( nK = 1; nK <= n; ++nK ) {
		 fTerm = fTerm / nK * fXHalf;
		 }*/

		fTerm = Math.pow(fXHalf, n) / Math.fact(n);

		fResult = fTerm;    // Start result with TERM(n,0).
		if (fTerm !== 0) {
			nK = 1;
			do {
				fTerm = Math.pow(fXHalf, n + 2 * nK) / ( Math.fact(nK) * Math.fact(n + nK) );

				/*  Calculation of TERM(n,k) from TERM(n,k-1):

				 (x/2)^(n+2k)
				 TERM(n,k)  =  --------------
				 k! (n+k)!

				 (x/2)^2 (x/2)^(n+2(k-1))
				 =  --------------------------
				 k (k-1)! (n+k) (n+k-1)!

				 (x/2)^2     (x/2)^(n+2(k-1))
				 =  --------- * ------------------
				 k(n+k)      (k-1)! (n+k-1)!

				 x^2/4
				 =  -------- TERM(n,k-1)
				 k(n+k)
				 */
//            fTerm = fTerm * fXHalf / nK * fXHalf / (nK + n);
				fResult = fResult + fTerm;
				nK++;
			} while ((Math.abs(fTerm) > Math.abs(fResult) * fEpsilon) && (nK < nMaxIteration));

		}
		return new cNumber(fResult);
	}

	function Besselk0(fNum) {
		var fRet, y;

		if (fNum <= 2) {
			var fNum2 = fNum * 0.5;
			y = fNum2 * fNum2;
			fRet = -Math.log10(fNum2) * BesselI(fNum, 0) + ( -0.57721566 + y * ( 0.42278420 +
				y * ( 0.23069756 + y * ( 0.3488590e-1 + y * ( 0.262698e-2 + y * ( 0.10750e-3 + y * 0.74e-5 ) ) ) ) ) );
		} else {
			y = 2 / fNum;
			fRet = Math.exp(-fNum) / Math.sqrt(fNum) * ( 1.25331414 + y * ( -0.7832358e-1 + y * ( 0.2189568e-1 +
				y * ( -0.1062446e-1 + y * ( 0.587872e-2 + y * ( -0.251540e-2 + y * 0.53208e-3 ) ) ) ) ) );
		}

		return new cNumber(fRet);
	}

	function Besselk1(fNum) {
		var fRet, y;

		if (fNum <= 2) {
			var fNum2 = fNum * 0.5;
			y = fNum2 * fNum2;
			fRet = Math.log10(fNum2) * BesselI(fNum, 1) + ( 1 + y * ( 0.15443144 + y * ( -0.67278579 +
				y * ( -0.18156897 + y * ( -0.1919402e-1 + y * ( -0.110404e-2 + y * ( -0.4686e-4 ) ) ) ) ) ) ) / fNum;
		} else {
			y = 2 / fNum;
			fRet = Math.exp(-fNum) / Math.sqrt(fNum) * ( 1.25331414 + y * ( 0.23498619 + y * ( -0.3655620e-1 +
				y * ( 0.1504268e-1 + y * ( -0.780353e-2 + y * ( 0.325614e-2 + y * ( -0.68245e-3 ) ) ) ) ) ) );
		}

		return new cNumber(fRet);
	}

	function BesselK(fNum, nOrder) {
		switch (nOrder) {
			case 0:
				return Besselk0(fNum);
			case 1:
				return Besselk1(fNum);
			default: {
				var fBkp;

				var fTox = 2 / fNum, fBkm = Besselk0(fNum), fBk = Besselk1(fNum);

				if (fBkm instanceof cError) {
					return fBkm;
				}
				if (fBk instanceof cError) {
					return fBk;
				}

				fBkm = fBkm.getValue();
				fBk = fBk.getValue();

				for (var n = 1; n < nOrder; n++) {
					fBkp = fBkm + n * fTox * fBk;
					fBkm = fBk;
					fBk = fBkp;
				}

				return new cNumber(fBk);
			}
		}
	}

	function Bessely0(fX) {
		if (fX <= 0) {
			return new cError(cErrorType.not_numeric);
		}
		var fMaxIteration = 9000000; // should not be reached
		if (fX > 5.0e+6) { // iteration is not considerable better then approximation
			return new cNumber(Math.sqrt(1 / Math.PI / fX) * (Math.sin(fX) - Math.cos(fX)));
		}
		var epsilon = 1.0e-15, EulerGamma = 0.57721566490153286060;
		var alpha = Math.log10(fX / 2) + EulerGamma;
		var u = alpha;

		var k = 1, m_bar = 0, g_bar_delta_u = 0, g_bar = -2 / fX;
		var delta_u = g_bar_delta_u / g_bar, g = -1 / g_bar, f_bar = -1 * g, sign_alpha = 1, km1mod2, bHasFound = false;
		k = k + 1;
		do {
			km1mod2 = Math.fmod(k - 1, 2);
			m_bar = (2 * km1mod2) * f_bar;
			if (km1mod2 == 0) {
				alpha = 0;
			} else {
				alpha = sign_alpha * (4 / k);
				sign_alpha = -sign_alpha;
			}
			g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u;
			g_bar = m_bar - (2 * k) / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasFound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasFound && k < fMaxIteration);
		if (bHasFound) {
			return new cNumber(u * f_2_DIV_PI);
		} else {
			return new cError(cErrorType.not_numeric);
		}
	}

// See #i31656# for a commented version of this implementation, attachment #desc6
// http://www.openoffice.org/nonav/issues/showattachment.cgi/63609/Comments%20to%20the%20implementation%20of%20the%20Bessel%20functions.odt
	function Bessely1(fX) {
		if (fX <= 0) {
			return new cError(cErrorType.not_numeric);
		}
		var fMaxIteration = 9000000; // should not be reached
		if (fX > 5e+6) { // iteration is not considerable better then approximation
			return new cNumber(-Math.sqrt(1 / Math.PI / fX) * (Math.sin(fX) + Math.cos(fX)));
		}
		var epsilon = 1.0e-15, EulerGamma = 0.57721566490153286060, alpha = 1 /
			fX, f_bar = -1, u = alpha, k = 1, m_bar = 0;
		alpha = 1 - EulerGamma - Math.log10(fX / 2);
		var g_bar_delta_u = -alpha, g_bar = -2 / fX, delta_u = g_bar_delta_u / g_bar;
		u = u + delta_u;
		var g = -1 / g_bar;
		f_bar = f_bar * g;
		var sign_alpha = -1, km1mod2, //will be (k-1) mod 2
			q, // will be (k-1) div 2
			bHasFound = false;
		k = k + 1;
		do {
			km1mod2 = Math.fmod(k - 1, 2);
			m_bar = (2 * km1mod2) * f_bar;
			q = (k - 1) / 2;
			if (km1mod2 == 0) { // k is odd
				alpha = sign_alpha * (1 / q + 1 / (q + 1));
				sign_alpha = -sign_alpha;
			} else {
				alpha = 0;
			}
			g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u;
			g_bar = m_bar - (2 * k) / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasFound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasFound && k < fMaxIteration);
		if (bHasFound) {
			return new cNumber(-u * 2 / Math.PI);
		} else {
			return new cError(cErrorType.not_numeric);
		}
	}

	function BesselY(fNum, nOrder) {
		switch (nOrder) {
			case 0:
				return Bessely0(fNum);
			case 1:
				return Bessely1(fNum);
			default: {
				var fByp, fTox = 2 / fNum, fBym = Bessely0(fNum), fBy = Bessely1(fNum);

				if (fBym instanceof cError) {
					return fBym;
				}
				if (fBy instanceof cError) {
					return fBy;
				}

				fBym = fBym.getValue();
				fBy = fBy.getValue();

				for (var n = 1; n < nOrder; n++) {
					fByp = n * fTox * fBy - fBym;
					fBym = fBy;
					fBy = fByp;
				}

				return new cNumber(fBy);
			}
		}
	}

	function validBINNumber(n) {
		return rg_validBINNumber.test(n);
	}

	function validDEC2BINNumber(n) {
		return rg_validDEC2BINNumber.test(n);
	}

	function validDEC2OCTNumber(n) {
		return rg_validDEC2OCTNumber.test(n);
	}

	function validDEC2HEXNumber(n) {
		return rg_validDEC2HEXNumber.test(n);
	}

	function validHEXNumber(n) {
		return rg_validHEXNumber.test(n);
	}

	function validOCTNumber(n) {
		return rg_validOCTNumber.test(n);
	}

	function convertFromTo(src, from, to, charLim) {
		var res = parseInt(src, from).toString(to);
		if (charLim == undefined) {
			return new cString(res.toUpperCase());
		} else {
			charLim = parseInt(charLim);
			if (charLim >= res.length) {
				return new cString(( '0'.repeat(charLim - res.length) + res).toUpperCase());
			} else {
				return new cError(cErrorType.not_numeric);
			}
		}
	}

	function Complex(r, i, suffix) {
		if (arguments.length == 1) {
			return this.ParseString(arguments[0]);
		} else {
			this.real = r;
			this.img = i;
			this.suffix = suffix ? suffix : "i";
			return this;
		}
	}

	Complex.prototype = {

		constructor: Complex, toString: function () {
			var res = [];
			var hasImag = this.img != 0, hasReal = !hasImag || (this.real != 0);

			if (hasReal) {

				res.push(this.real);
			}
			if (hasImag) {
				if (this.img == 1) {
					if (hasReal) {
						res.push('+');
					}
				} else if (this.img == -1) {
					res.push("-");
				} else {
					this.img > 0 && hasReal ? res.push("+" + this.img) : res.push(this.img);
				}
				res.push(this.suffix ? this.suffix : "i");
			}
			return res.join("");
		}, Real: function () {
			return this.real;
		}, Imag: function () {
			return this.img;
		}, Abs: function () {
			return Math.sqrt(this.real * this.real + this.img * this.img);
		}, Arg: function () {
			if (this.real == 0.0 && this.img == 0.0) {
				return new cError(cErrorType.division_by_zero);
			}

			var phi = Math.acos(this.real / this.Abs());

			if (this.img < 0.0) {
				phi = -phi;
			}

			return phi;
		}, Conj: function () {
			var c = new Complex(this.real, -this.img, this.suffix);
			return c.toString();
		}, Cos: function () {
			if (this.img) {
				var a = Math.cos(this.real) * Math.cosh(this.img);
				this.img = -( Math.sin(this.real) * Math.sinh(this.img) );
				this.real = a;
			} else {
				this.real = Math.cos(this.real);
			}
		}, Tan: function () {
			if (this.img) {
				var a = Math.sin(2 * this.real) / (Math.cos(2 * this.real) + Math.cosh(2 * this.img));
				this.img = Math.sinh(2 * this.img) / (Math.cos(2 * this.real) + Math.cosh(2 * this.img));
				this.real = a;
			} else {
				this.real = Math.tan(this.real);
			}
		}, Cot: function () {
			if (this.img) {
				var a = Math.sin(2 * this.real) / (Math.cosh(2 * this.img) - Math.cos(2 * this.real));
				this.img = -(Math.sinh(2 * this.img) / (Math.cosh(2 * this.img) - Math.cos(2 * this.real)));
				this.real = a;
			} else {
				this.real = 1 / Math.tan(this.real);
			}
		}, Cosh: function () {
			if (this.img) {
				var a = Math.cosh(this.real) * Math.cos(this.img);
				this.img = Math.sinh(this.real) * Math.sin(this.img);
				this.real = a;
			} else {
				this.real = Math.cosh(this.real);
			}
		}, Csc: function () {
			if (this.img) {
				var a = (2 * Math.sin(this.real) * Math.cosh(this.img)) / (Math.cosh(2 * this.img) - Math.cos(2 *this.real));
				this.img = (-2 * Math.cos(this.real) * Math.sinh(this.img)) / (Math.cosh(2 * this.img) - Math.cos(2 *this.real));
				this.real = a;
			} else {
				this.real = 1 / Math.sin(this.real);
			}
		}, Csch: function () {
			if (this.img) {
				var a = (2 * Math.sinh(this.real) * Math.cos(this.img)) / (Math.cosh(2 * this.real) - Math.cos(2 *this.img));
				this.img = (-2 * Math.cosh(this.real) * Math.sin(this.img)) / (Math.cosh(2 * this.real) - Math.cos(2 *this.img));
				this.real = a;
			} else {
				this.real = 1 / Math.sinh(this.real);
			}
		}, Sec: function () {
			if (this.img) {
				var a = (2 * Math.cos(this.real) * Math.cosh(this.img)) / (Math.cosh(2 * this.img) + Math.cos(2 *this.real));
				this.img = (2 * Math.sin(this.real) * Math.sinh(this.img)) / (Math.cosh(2 * this.img) + Math.cos(2 *this.real));
				this.real = a;
			} else {
				this.real = 1 / Math.cos(this.real);
			}
		}, Sech: function () {
			if (this.img) {
				var a = (2 * Math.cosh(this.real) * Math.cos(this.img)) / (Math.cosh(2 * this.real) + Math.cos(2 *this.img));
				this.img = (-2 * Math.sinh(this.real) * Math.sin(this.img)) / (Math.cosh(2 * this.real) + Math.cos(2 *this.img));
				this.real = a;
			} else {
				this.real = 1 / Math.cosh(this.real);
			}
		}, Sin: function () {
			if (this.img) {
				var a = Math.sin(this.real) * Math.cosh(this.img);
				this.img = Math.cos(this.real) * Math.sinh(this.img);
				this.real = a;
			} else {
				this.real = Math.sin(this.real);
			}
		}, Sinh: function () {
			if (this.img) {
				var a = Math.sinh(this.real) * Math.cos(this.img);
				this.img = Math.cosh(this.real) * Math.sin(this.img);
				this.real = a;
			} else {
				this.real = Math.sinh(this.real);
			}
		}, Div: function (comp) {

			var a = this.real, b = this.img, c = comp.real, d = comp.img, f = 1 / (c * c + d * d);

			if (Math.abs(f) == Infinity) {
				return new cError(cErrorType.not_numeric);
			}

			return new Complex((a * c + b * d) * f, (b * c - a * d) * f, this.suffix);

		}, Exp: function () {

			var e = Math.exp(this.real), c = Math.cos(this.img), s = Math.sin(this.img);

			this.real = e * c;
			this.img = e * s;

		}, Ln: function () {

			var abs = this.Abs(), arg = this.Arg();

			if (abs == 0 || arg instanceof cError) {
				return new cError(cErrorType.not_numeric);
			}

			this.real = Math.ln(abs);
			this.img = arg;

		}, Log10: function () {

			var c = new Complex(Math.ln(10), 0);
			var r = this.Ln();

			if (r instanceof cError) {
				return r;
			}

			c = this.Div(c);

			if (c instanceof cError) {
				return c;
			}

			this.real = c.real;
			this.img = c.img;

		}, Log2: function () {

			var c = new Complex(Math.ln(2), 0);
			var r = this.Ln();

			if (r instanceof cError) {
				return r;
			}

			c = this.Div(c);

			if (c instanceof cError) {
				return c;
			}

			this.real = c.real;
			this.img = c.img;

		}, Power: function (power) {

			if (this.real == 0 && this.img == 0) {
				if (power > 0) {
					this.real = this.img = 0;
					return true;
				} else {
					return false;
				}
			} else {

				var p = this.Abs(), phi;

				phi = Math.acos(this.real / p);
				if (this.img < 0) {
					phi = -phi;
				}

				p = Math.pow(p, power);
				phi *= power;

				this.real = Math.cos(phi) * p;
				this.img = Math.sin(phi) * p;

				return true;
			}

		}, Product: function (comp) {

			var a = this.real, b = this.img, c = comp.real, d = comp.img;

			this.real = a * c - b * d;
			this.img = a * d + b * c;

		}, SQRT: function () {

			if (this.real || this.img) {
				var abs = this.Abs(), arg = this.Arg();

				this.real = Math.sqrt(abs) * Math.cos(arg / 2);
				this.img = Math.sqrt(abs) * Math.sin(arg / 2);

			}

		}, Sub: function (comp) {

			this.real -= comp.real;
			this.img -= comp.img;

		}, Sum: function (comp) {

			this.real += comp.real;
			this.img += comp.img;

		}, isImagUnit: function (c) {
			return c == 'i' || c == 'j';
		}, parseComplexStr: function (s) {
			var match = XRegExp.exec(s, rg_complex_number), r, i, suf;
			if (match) {
				r = match["real"];
				i = match["img"];

				if (!(r || i)) {
					return new cError(cErrorType.not_numeric);
				}

				if (i) {
					suf = i[i.length - 1];
					i = i.substr(0, i.length - 1);
					if (i.length == 1 && (i[0] == "-" || i[0] == "+" )) {
						i = parseFloat(i + "1");
					} else {
						i = parseFloat(i);
					}
				} else {
					i = 0;
				}

				if (r) {
					r = parseFloat(r);
				} else {
					r = 0;
				}

				return new Complex(r, i, suf ? suf : "i");

			} else {
				return new cError(cErrorType.not_numeric);
			}
		}, ParseString: function (rStr) {

			var pStr = {pStr: rStr}, f = {f: undefined};

			if (rStr.length == 0) {
				this.real = 0;
				this.img = 0;
				this.suffix = "i";
				return this;
			}

			if (this.isImagUnit(pStr.pStr[0]) && rStr.length == 1) {
				this.real = 0;
				this.img = 1;
				this.suffix = pStr;
				return this;
			}

			if (!this.ParseDouble(pStr, f)) {
				return new cError(cErrorType.not_numeric);
			}

			switch (pStr.pStr[0] + "") {
				case '-':   // imag part follows
				case '+': {
					var r = f.f;

					if (this.isImagUnit(pStr.pStr[1])) {
						this.c = pStr.pStr[1];
						if (pStr.pStr[2] === undefined) {
							this.real = f.f;
							this.img = ( pStr.pStr[0] == '+' ) ? 1.0 : -1.0;
							return this;
						}
					} else if (this.ParseDouble(pStr, f) && this.isImagUnit(pStr.pStr[0])) {
						this.c = pStr.pStr;
						if (pStr.pStr[2] === undefined) {
							this.real = r;
							this.img = f.f;
							return this;
						}
					}
					break;
				}
				case 'j':
				case 'i':
					this.c = pStr;
					if (pStr.pStr[1] === undefined) {
						this.img = f.f;
						this.real = 0.0;
						return this;
					}
					break;
				case "undefined":     // only real-part
					this.real = f.f;
					this.img = 0.0;
					return this;
			}
			return new cError(cErrorType.not_numeric);
		}, ParseDouble: function (rp, rRet) {

			function isnum(c) {
				return c >= '0' && c <= '9';
			}


			function iscomma(c) {
				return c == '.' || c == ',';
			}


			function isexpstart(c) {
				return c == 'e' || c == 'E';
			}


			function isimagunit(c) {
				return c == 'i' || c == 'j';
			}

			var fInt = 0.0, fFrac = 0.0, fMult = 0.1, // multiplier to multiply digits with, when adding fractional ones
				nExp = 0, nMaxExp = 307, nDigCnt = 18, // max. number of digits to read in, rest doesn't matter
				State = {
					end: 0,
					sign: 1,
					intstart: 2,
					int: 3,
					ignoreintdigs: 4,
					frac: 5,
					ignorefracdigs: 6,
					expsign: 7,
					exp: 8
				}, eS = State.sign, bNegNum = false, bNegExp = false, p = rp.pStr, c, i = 0;

			while (eS) {
				c = p[i];
				switch (eS) {
					case State.sign:
						if (isnum(c)) {
							fInt = parseFloat(c);
							nDigCnt--;
							eS = State.int;
						} else if (c == '-') {
							bNegNum = true;
							eS = State.intstart;
						} else if (c == '+') {
							eS = State.intstart;
						} else if (iscomma(c)) {
							eS = State.frac;
						} else {
							return false;
						}
						break;
					case State.intstart:
						if (isnum(c)) {
							fInt = parseFloat(c);
							nDigCnt--;
							eS = State.int;
						} else if (iscomma(c)) {
							eS = State.frac;
						} else if (isimagunit(c)) {
							rRet.f = 0.0;
							return true;
						} else {
							return false;
						}
						break;
					case State.int:
						if (isnum(c)) {
							fInt *= 10.0;
							fInt += parseFloat(c);
							nDigCnt--;
							if (!nDigCnt) {
								eS = State.ignoreintdigs;
							}
						} else if (iscomma(c)) {
							eS = State.frac;
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.ignoreintdigs:
						if (isnum(c)) {
							nExp++;
						}     // just multiply num with 10... ;-)
						else if (iscomma(c)) {
							eS = State.frac;
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.frac:
						if (isnum(c)) {
							fFrac += parseFloat(c) * fMult;
							nDigCnt--;
							if (nDigCnt) {
								fMult *= 0.1;
							} else {
								eS = State.ignorefracdigs;
							}
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.ignorefracdigs:
						if (isexpstart(c)) {
							eS = State.expsign;
						} else if (!isnum(c)) {
							eS = State.end;
						}
						break;
					case State.expsign:
						if (isnum(c)) {
							nExp = parseFloat(c);
							eS = State.exp;
						} else if (c == '-') {
							bNegExp = true;
							eS = State.exp;
						} else if (c != '+') {
							eS = State.end;
						}
						break;
					case State.exp:
						if (isnum(c)) {
							nExp *= 10;
							nExp += parseFloat(c);
							if (nExp > nMaxExp) {
								return false;
							}
						} else {
							eS = State.end;
						}
						break;
					case State.end:     // to avoid compiler warning
						break;      // loop exits anyway
				}

				i++;
			}

			i--;        // set pointer back to last
			rp.pStr = p.substr(i);

			fInt += fFrac;
			var nLog10 = Math.log10(fInt);

			if (bNegExp) {
				nExp = -nExp;
			}

			if (nLog10 + nExp > nMaxExp) {
				return false;
			}

			fInt = fInt * Math.pow(10.0, nExp);

			if (bNegNum) {
				fInt = -fInt;
			}

			rRet.f = fInt;

			return true;
		}

	};

	cFormulaFunctionGroup['Engineering'] = cFormulaFunctionGroup['Engineering'] || [];
	cFormulaFunctionGroup['Engineering'].push(cBESSELI, cBESSELJ, cBESSELK, cBESSELY, cBIN2DEC, cBIN2HEX, cBIN2OCT,
		cBITAND, cBITLSHIFT, cBITOR, cBITRSHIFT, cBITXOR, cCOMPLEX, cCONVERT, cDEC2BIN, cDEC2HEX, cDEC2OCT, cDELTA,
		cERF, cERF_PRECISE, cERFC, cERFC_PRECISE, cGESTEP, cHEX2BIN, cHEX2DEC, cHEX2OCT, cIMABS, cIMAGINARY,
		cIMARGUMENT, cIMCONJUGATE, cIMCOS, cIMCOSH, cIMCOT, cIMCSC, cIMCSCH, cIMDIV, cIMEXP, cIMLN, cIMLOG10, cIMLOG2,
		cIMPOWER, cIMPRODUCT, cIMREAL, cIMSEC, cIMSECH, cIMSIN, cIMSINH, cIMSQRT, cIMSUB, cIMSUM, cIMTAN, cOCT2BIN,
		cOCT2DEC, cOCT2HEX);

	cFormulaFunctionGroup['NotRealised'] = cFormulaFunctionGroup['NotRealised'] || [];
	cFormulaFunctionGroup['NotRealised'].push(cBESSELI, cBESSELK, cBESSELY, cCONVERT);

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELI() {
		cBaseFunction.call(this, "BESSELI");
	}

	cBESSELI.prototype = Object.create(cBaseFunction.prototype);
	cBESSELI.prototype.constructor = cBESSELI;
	cBESSELI.prototype.argumentsMin = 2;
	cBESSELI.prototype.argumentsMax = 2;
	/*cBESSELI.prototype.Calculate = function ( arg ) {
		 var x = arg[0],
		 n = arg[1];

		 if ( x instanceof cArea || x instanceof cArea3D ) {
		 	x = x.cross( arguments[1] );
		 }
		 else if ( x instanceof cArray ) {
		 	x = x.getElementRowCol( 0, 0 );
		 }

		 if ( n instanceof cArea || n instanceof cArea3D ) {
		 	n = n.cross( arguments[1] );
		 }
		 else if ( n instanceof cArray ) {
		 	n = n.getElementRowCol( 0, 0 );
		 }

		 x = x.tocNumber();
		 n = n.tocNumber();

		 if ( x instanceof cError ) {
			 return this.value = x;
		 }
		 if ( n instanceof cError ) {
		 	return this.value = n;
		 }

		 x = x.getValue();
		 n = n.getValue();

		 if ( n < 0 ){
			 return this.value = new cError( cErrorType.not_numeric );
		 }
		 if(x < 0){
		 	x = Math.abs(x);
		 }
		 n = Math.floor(n);

		 this.value = BesselI( x, n );
		 return this.value;
	 };*/

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELJ() {
		cBaseFunction.call(this, "BESSELJ");
	}

	cBESSELJ.prototype = Object.create(cBaseFunction.prototype);
	cBESSELJ.prototype.constructor = cBESSELJ;
	cBESSELJ.prototype.argumentsMin = 2;
	cBESSELJ.prototype.argumentsMax = 2;
	cBESSELJ.prototype.Calculate = function ( arg ) {
		//результаты вычислений как в LO
		var oArguments = this._prepareArguments(arg, arguments[1], true);
		var argClone = oArguments.args;

		argClone[0] = argClone[0].tocNumber();
		argClone[1] = argClone[1].tocNumber();

		var argError;
		if (argError = this._checkErrorArg(argClone)) {
			return this.value = argError;
		}

		var calcFunc = function(argArray){
			var x = argArray[0];
			var n = argArray[1];

			if ( n < 0 ){
				return new cError( cErrorType.not_numeric );
			}
			if(x < 0){
				x = Math.abs(x);
			}
			n = Math.floor(n);

			return BesselJ( x, n );
		};

		return this.value = this._findArrayInNumberArguments(oArguments, calcFunc);
	};


	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELK() {
		cBaseFunction.call(this, "BESSELK");
	}

	cBESSELK.prototype = Object.create(cBaseFunction.prototype);
	cBESSELK.prototype.constructor = cBESSELK;

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELY() {
		cBaseFunction.call(this, "BESSELY");
	}

	cBESSELY.prototype = Object.create(cBaseFunction.prototype);
	cBESSELY.prototype.constructor = cBESSELY;

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBIN2DEC() {
		cBaseFunction.call(this, "BIN2DEC");
	}

	cBIN2DEC.prototype = Object.create(cBaseFunction.prototype);
	cBIN2DEC.prototype.constructor = cBIN2DEC;<commit_msg>add function for BESSELK formula<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */


"use strict";

(/**
 * @param {Window} window
 * @param {undefined} undefined
 */
	function (window, undefined) {
	var cErrorType = AscCommonExcel.cErrorType;
	var cNumber = AscCommonExcel.cNumber;
	var cString = AscCommonExcel.cString;
	var cError = AscCommonExcel.cError;
	var cArea = AscCommonExcel.cArea;
	var cArea3D = AscCommonExcel.cArea3D;
	var cArray = AscCommonExcel.cArray;
	var cUndefined = AscCommonExcel.cUndefined;
	var cBaseFunction = AscCommonExcel.cBaseFunction;
	var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup;

	var rtl_math_erf = AscCommonExcel.rtl_math_erf;

	var rg_validBINNumber = /^[01]{1,10}$/, rg_validDEC2BINNumber = /^-?[0-9]{1,3}$/, rg_validDEC2OCTNumber = /^-?[0-9]{1,9}$/, rg_validDEC2HEXNumber = /^-?[0-9]{1,12}$/, rg_validHEXNumber = /^[0-9A-F]{1,10}$/i, rg_validOCTNumber = /^[0-7]{1,10}$/, rg_complex_number = new XRegExp("^(?<real>[-+]?(?:\\d*(?:\\.\\d+)?(?:[Ee][+-]?\\d+)?))?(?<img>([-+]?(\\d*(?:\\.\\d+)?(?:[Ee][+-]?\\d+)?)?[ij])?)", "g");

	var NumberBase = {
		BIN: 2, OCT: 8, DEC: 10, HEX: 16
	};

	var f_PI_DIV_2 = Math.PI / 2;
	var f_PI_DIV_4 = Math.PI / 4;
	var f_2_DIV_PI = 2 / Math.PI;

	function BesselJ(x, N) {
		if (N < 0) {
			return new cError(cErrorType.not_numeric);
		}
		if (x === 0.0) {
			return new cNumber((N == 0) ? 1 : 0);
		}

		/*  The algorithm works only for x>0, therefore remember sign. BesselJ
		 with integer order N is an even function for even N (means J(-x)=J(x))
		 and an odd function for odd N (means J(-x)=-J(x)).*/
		var fSign = (N % 2 == 1 && x < 0) ? -1 : 1;
		var fX = Math.abs(x);

		var fMaxIteration = 9000000; //experimental, for to return in < 3 seconds
		var fEstimateIteration = fX * 1.5 + N;
		var bAsymptoticPossible = Math.pow(fX, 0.4) > N;
		if (fEstimateIteration > fMaxIteration) {
			if (bAsymptoticPossible) {
				return new cNumber(fSign * Math.sqrt(f_2_DIV_PI / fX) * Math.cos(fX - N * f_PI_DIV_2 - f_PI_DIV_4));
			} else {
				return new cError(cErrorType.not_numeric);
			}
		}

		var epsilon = 1.0e-15; // relative error
		var bHasfound = false, k = 0, u;

		// first used with k=1
		var m_bar, g_bar, g_bar_delta_u, g = 0, delta_u = 0, f_bar = -1;  // f_bar_k = 1/f_k, but only used for k=0

		if (N == 0) {
			u = 1;
			g_bar_delta_u = 0;
			g_bar = -2 / fX;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			k = 2;
		} else {
			u = 0;
			for (k = 1; k <= N - 1; k = k + 1) {
				m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
				g_bar_delta_u = -g * delta_u - m_bar * u; // alpha_k = 0.0
				g_bar = m_bar - 2 * k / fX + g;
				delta_u = g_bar_delta_u / g_bar;
				u = u + delta_u;
				g = -1 / g_bar;
				f_bar = f_bar * g;
			}
			// Step alpha_N = 1.0
			m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
			g_bar_delta_u = f_bar - g * delta_u - m_bar * u; // alpha_k = 1.0
			g_bar = m_bar - 2 * k / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			k = k + 1;
		}
		// Loop until desired accuracy, always alpha_k = 0.0
		do {
			m_bar = 2 * Math.fmod(k - 1, 2) * f_bar;
			g_bar_delta_u = -g * delta_u - m_bar * u;
			g_bar = m_bar - 2 * k / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasfound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasfound && k <= fMaxIteration);
		if (bHasfound) {
			return new cNumber(u * fSign);
		} else {
			return new cError(cErrorType.not_numeric);
		}// unlikely to happen
	}

	function BesselI(x, n) {
		var nMaxIteration = 20000, fXHalf = x / 2, fResult = 0, fEpsilon = 1.0E-30;
		if (n < 0) {
			return new cError(cErrorType.not_numeric);
		}

		/*  Start the iteration without TERM(n,0), which is set here.

		 TERM(n,0) = (x/2)^n / n!
		 */
		var nK = 0, fTerm = 1;
		// avoid overflow in Fak(n)
		/*for ( nK = 1; nK <= n; ++nK ) {
		 fTerm = fTerm / nK * fXHalf;
		 }*/

		fTerm = Math.pow(fXHalf, n) / Math.fact(n);

		fResult = fTerm;    // Start result with TERM(n,0).
		if (fTerm !== 0) {
			nK = 1;
			do {
				fTerm = Math.pow(fXHalf, n + 2 * nK) / ( Math.fact(nK) * Math.fact(n + nK) );

				/*  Calculation of TERM(n,k) from TERM(n,k-1):

				 (x/2)^(n+2k)
				 TERM(n,k)  =  --------------
				 k! (n+k)!

				 (x/2)^2 (x/2)^(n+2(k-1))
				 =  --------------------------
				 k (k-1)! (n+k) (n+k-1)!

				 (x/2)^2     (x/2)^(n+2(k-1))
				 =  --------- * ------------------
				 k(n+k)      (k-1)! (n+k-1)!

				 x^2/4
				 =  -------- TERM(n,k-1)
				 k(n+k)
				 */
//            fTerm = fTerm * fXHalf / nK * fXHalf / (nK + n);
				fResult = fResult + fTerm;
				nK++;
			} while ((Math.abs(fTerm) > Math.abs(fResult) * fEpsilon) && (nK < nMaxIteration));

		}
		return new cNumber(fResult);
	}

	function Besselk0(fNum) {
		var fRet, y;

		if (fNum <= 2) {
			var fNum2 = fNum * 0.5;
			y = fNum2 * fNum2;
			fRet = -Math.log10(fNum2) * BesselI(fNum, 0) + ( -0.57721566 + y * ( 0.42278420 +
				y * ( 0.23069756 + y * ( 0.3488590e-1 + y * ( 0.262698e-2 + y * ( 0.10750e-3 + y * 0.74e-5 ) ) ) ) ) );
		} else {
			y = 2 / fNum;
			fRet = Math.exp(-fNum) / Math.sqrt(fNum) * ( 1.25331414 + y * ( -0.7832358e-1 + y * ( 0.2189568e-1 +
				y * ( -0.1062446e-1 + y * ( 0.587872e-2 + y * ( -0.251540e-2 + y * 0.53208e-3 ) ) ) ) ) );
		}

		return new cNumber(fRet);
	}

	function Besselk1(fNum) {
		var fRet, y;

		if (fNum <= 2) {
			var fNum2 = fNum * 0.5;
			y = fNum2 * fNum2;
			fRet = Math.log10(fNum2) * BesselI(fNum, 1) + ( 1 + y * ( 0.15443144 + y * ( -0.67278579 +
				y * ( -0.18156897 + y * ( -0.1919402e-1 + y * ( -0.110404e-2 + y * ( -0.4686e-4 ) ) ) ) ) ) ) / fNum;
		} else {
			y = 2 / fNum;
			fRet = Math.exp(-fNum) / Math.sqrt(fNum) * ( 1.25331414 + y * ( 0.23498619 + y * ( -0.3655620e-1 +
				y * ( 0.1504268e-1 + y * ( -0.780353e-2 + y * ( 0.325614e-2 + y * ( -0.68245e-3 ) ) ) ) ) ) );
		}

		return new cNumber(fRet);
	}

	function BesselK(fNum, nOrder) {
		switch (nOrder) {
			case 0:
				return Besselk0(fNum);
			case 1:
				return Besselk1(fNum);
			default: {
				var fBkp;

				var fTox = 2 / fNum, fBkm = Besselk0(fNum), fBk = Besselk1(fNum);

				if (fBkm instanceof cError) {
					return fBkm;
				}
				if (fBk instanceof cError) {
					return fBk;
				}

				fBkm = fBkm.getValue();
				fBk = fBk.getValue();

				for (var n = 1; n < nOrder; n++) {
					fBkp = fBkm + n * fTox * fBk;
					fBkm = fBk;
					fBk = fBkp;
				}

				return new cNumber(fBk);
			}
		}
	}

	function Bessely0(fX) {
		if (fX <= 0) {
			return new cError(cErrorType.not_numeric);
		}
		var fMaxIteration = 9000000; // should not be reached
		if (fX > 5.0e+6) { // iteration is not considerable better then approximation
			return new cNumber(Math.sqrt(1 / Math.PI / fX) * (Math.sin(fX) - Math.cos(fX)));
		}
		var epsilon = 1.0e-15, EulerGamma = 0.57721566490153286060;
		var alpha = Math.log10(fX / 2) + EulerGamma;
		var u = alpha;

		var k = 1, m_bar = 0, g_bar_delta_u = 0, g_bar = -2 / fX;
		var delta_u = g_bar_delta_u / g_bar, g = -1 / g_bar, f_bar = -1 * g, sign_alpha = 1, km1mod2, bHasFound = false;
		k = k + 1;
		do {
			km1mod2 = Math.fmod(k - 1, 2);
			m_bar = (2 * km1mod2) * f_bar;
			if (km1mod2 == 0) {
				alpha = 0;
			} else {
				alpha = sign_alpha * (4 / k);
				sign_alpha = -sign_alpha;
			}
			g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u;
			g_bar = m_bar - (2 * k) / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasFound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasFound && k < fMaxIteration);
		if (bHasFound) {
			return new cNumber(u * f_2_DIV_PI);
		} else {
			return new cError(cErrorType.not_numeric);
		}
	}

// See #i31656# for a commented version of this implementation, attachment #desc6
// http://www.openoffice.org/nonav/issues/showattachment.cgi/63609/Comments%20to%20the%20implementation%20of%20the%20Bessel%20functions.odt
	function Bessely1(fX) {
		if (fX <= 0) {
			return new cError(cErrorType.not_numeric);
		}
		var fMaxIteration = 9000000; // should not be reached
		if (fX > 5e+6) { // iteration is not considerable better then approximation
			return new cNumber(-Math.sqrt(1 / Math.PI / fX) * (Math.sin(fX) + Math.cos(fX)));
		}
		var epsilon = 1.0e-15, EulerGamma = 0.57721566490153286060, alpha = 1 /
			fX, f_bar = -1, u = alpha, k = 1, m_bar = 0;
		alpha = 1 - EulerGamma - Math.log10(fX / 2);
		var g_bar_delta_u = -alpha, g_bar = -2 / fX, delta_u = g_bar_delta_u / g_bar;
		u = u + delta_u;
		var g = -1 / g_bar;
		f_bar = f_bar * g;
		var sign_alpha = -1, km1mod2, //will be (k-1) mod 2
			q, // will be (k-1) div 2
			bHasFound = false;
		k = k + 1;
		do {
			km1mod2 = Math.fmod(k - 1, 2);
			m_bar = (2 * km1mod2) * f_bar;
			q = (k - 1) / 2;
			if (km1mod2 == 0) { // k is odd
				alpha = sign_alpha * (1 / q + 1 / (q + 1));
				sign_alpha = -sign_alpha;
			} else {
				alpha = 0;
			}
			g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u;
			g_bar = m_bar - (2 * k) / fX + g;
			delta_u = g_bar_delta_u / g_bar;
			u = u + delta_u;
			g = -1 / g_bar;
			f_bar = f_bar * g;
			bHasFound = (Math.abs(delta_u) <= Math.abs(u) * epsilon);
			k = k + 1;
		} while (!bHasFound && k < fMaxIteration);
		if (bHasFound) {
			return new cNumber(-u * 2 / Math.PI);
		} else {
			return new cError(cErrorType.not_numeric);
		}
	}

	function BesselY(fNum, nOrder) {
		switch (nOrder) {
			case 0:
				return Bessely0(fNum);
			case 1:
				return Bessely1(fNum);
			default: {
				var fByp, fTox = 2 / fNum, fBym = Bessely0(fNum), fBy = Bessely1(fNum);

				if (fBym instanceof cError) {
					return fBym;
				}
				if (fBy instanceof cError) {
					return fBy;
				}

				fBym = fBym.getValue();
				fBy = fBy.getValue();

				for (var n = 1; n < nOrder; n++) {
					fByp = n * fTox * fBy - fBym;
					fBym = fBy;
					fBy = fByp;
				}

				return new cNumber(fBy);
			}
		}
	}

	function validBINNumber(n) {
		return rg_validBINNumber.test(n);
	}

	function validDEC2BINNumber(n) {
		return rg_validDEC2BINNumber.test(n);
	}

	function validDEC2OCTNumber(n) {
		return rg_validDEC2OCTNumber.test(n);
	}

	function validDEC2HEXNumber(n) {
		return rg_validDEC2HEXNumber.test(n);
	}

	function validHEXNumber(n) {
		return rg_validHEXNumber.test(n);
	}

	function validOCTNumber(n) {
		return rg_validOCTNumber.test(n);
	}

	function convertFromTo(src, from, to, charLim) {
		var res = parseInt(src, from).toString(to);
		if (charLim == undefined) {
			return new cString(res.toUpperCase());
		} else {
			charLim = parseInt(charLim);
			if (charLim >= res.length) {
				return new cString(( '0'.repeat(charLim - res.length) + res).toUpperCase());
			} else {
				return new cError(cErrorType.not_numeric);
			}
		}
	}

	function Complex(r, i, suffix) {
		if (arguments.length == 1) {
			return this.ParseString(arguments[0]);
		} else {
			this.real = r;
			this.img = i;
			this.suffix = suffix ? suffix : "i";
			return this;
		}
	}

	Complex.prototype = {

		constructor: Complex, toString: function () {
			var res = [];
			var hasImag = this.img != 0, hasReal = !hasImag || (this.real != 0);

			if (hasReal) {

				res.push(this.real);
			}
			if (hasImag) {
				if (this.img == 1) {
					if (hasReal) {
						res.push('+');
					}
				} else if (this.img == -1) {
					res.push("-");
				} else {
					this.img > 0 && hasReal ? res.push("+" + this.img) : res.push(this.img);
				}
				res.push(this.suffix ? this.suffix : "i");
			}
			return res.join("");
		}, Real: function () {
			return this.real;
		}, Imag: function () {
			return this.img;
		}, Abs: function () {
			return Math.sqrt(this.real * this.real + this.img * this.img);
		}, Arg: function () {
			if (this.real == 0.0 && this.img == 0.0) {
				return new cError(cErrorType.division_by_zero);
			}

			var phi = Math.acos(this.real / this.Abs());

			if (this.img < 0.0) {
				phi = -phi;
			}

			return phi;
		}, Conj: function () {
			var c = new Complex(this.real, -this.img, this.suffix);
			return c.toString();
		}, Cos: function () {
			if (this.img) {
				var a = Math.cos(this.real) * Math.cosh(this.img);
				this.img = -( Math.sin(this.real) * Math.sinh(this.img) );
				this.real = a;
			} else {
				this.real = Math.cos(this.real);
			}
		}, Tan: function () {
			if (this.img) {
				var a = Math.sin(2 * this.real) / (Math.cos(2 * this.real) + Math.cosh(2 * this.img));
				this.img = Math.sinh(2 * this.img) / (Math.cos(2 * this.real) + Math.cosh(2 * this.img));
				this.real = a;
			} else {
				this.real = Math.tan(this.real);
			}
		}, Cot: function () {
			if (this.img) {
				var a = Math.sin(2 * this.real) / (Math.cosh(2 * this.img) - Math.cos(2 * this.real));
				this.img = -(Math.sinh(2 * this.img) / (Math.cosh(2 * this.img) - Math.cos(2 * this.real)));
				this.real = a;
			} else {
				this.real = 1 / Math.tan(this.real);
			}
		}, Cosh: function () {
			if (this.img) {
				var a = Math.cosh(this.real) * Math.cos(this.img);
				this.img = Math.sinh(this.real) * Math.sin(this.img);
				this.real = a;
			} else {
				this.real = Math.cosh(this.real);
			}
		}, Csc: function () {
			if (this.img) {
				var a = (2 * Math.sin(this.real) * Math.cosh(this.img)) / (Math.cosh(2 * this.img) - Math.cos(2 *this.real));
				this.img = (-2 * Math.cos(this.real) * Math.sinh(this.img)) / (Math.cosh(2 * this.img) - Math.cos(2 *this.real));
				this.real = a;
			} else {
				this.real = 1 / Math.sin(this.real);
			}
		}, Csch: function () {
			if (this.img) {
				var a = (2 * Math.sinh(this.real) * Math.cos(this.img)) / (Math.cosh(2 * this.real) - Math.cos(2 *this.img));
				this.img = (-2 * Math.cosh(this.real) * Math.sin(this.img)) / (Math.cosh(2 * this.real) - Math.cos(2 *this.img));
				this.real = a;
			} else {
				this.real = 1 / Math.sinh(this.real);
			}
		}, Sec: function () {
			if (this.img) {
				var a = (2 * Math.cos(this.real) * Math.cosh(this.img)) / (Math.cosh(2 * this.img) + Math.cos(2 *this.real));
				this.img = (2 * Math.sin(this.real) * Math.sinh(this.img)) / (Math.cosh(2 * this.img) + Math.cos(2 *this.real));
				this.real = a;
			} else {
				this.real = 1 / Math.cos(this.real);
			}
		}, Sech: function () {
			if (this.img) {
				var a = (2 * Math.cosh(this.real) * Math.cos(this.img)) / (Math.cosh(2 * this.real) + Math.cos(2 *this.img));
				this.img = (-2 * Math.sinh(this.real) * Math.sin(this.img)) / (Math.cosh(2 * this.real) + Math.cos(2 *this.img));
				this.real = a;
			} else {
				this.real = 1 / Math.cosh(this.real);
			}
		}, Sin: function () {
			if (this.img) {
				var a = Math.sin(this.real) * Math.cosh(this.img);
				this.img = Math.cos(this.real) * Math.sinh(this.img);
				this.real = a;
			} else {
				this.real = Math.sin(this.real);
			}
		}, Sinh: function () {
			if (this.img) {
				var a = Math.sinh(this.real) * Math.cos(this.img);
				this.img = Math.cosh(this.real) * Math.sin(this.img);
				this.real = a;
			} else {
				this.real = Math.sinh(this.real);
			}
		}, Div: function (comp) {

			var a = this.real, b = this.img, c = comp.real, d = comp.img, f = 1 / (c * c + d * d);

			if (Math.abs(f) == Infinity) {
				return new cError(cErrorType.not_numeric);
			}

			return new Complex((a * c + b * d) * f, (b * c - a * d) * f, this.suffix);

		}, Exp: function () {

			var e = Math.exp(this.real), c = Math.cos(this.img), s = Math.sin(this.img);

			this.real = e * c;
			this.img = e * s;

		}, Ln: function () {

			var abs = this.Abs(), arg = this.Arg();

			if (abs == 0 || arg instanceof cError) {
				return new cError(cErrorType.not_numeric);
			}

			this.real = Math.ln(abs);
			this.img = arg;

		}, Log10: function () {

			var c = new Complex(Math.ln(10), 0);
			var r = this.Ln();

			if (r instanceof cError) {
				return r;
			}

			c = this.Div(c);

			if (c instanceof cError) {
				return c;
			}

			this.real = c.real;
			this.img = c.img;

		}, Log2: function () {

			var c = new Complex(Math.ln(2), 0);
			var r = this.Ln();

			if (r instanceof cError) {
				return r;
			}

			c = this.Div(c);

			if (c instanceof cError) {
				return c;
			}

			this.real = c.real;
			this.img = c.img;

		}, Power: function (power) {

			if (this.real == 0 && this.img == 0) {
				if (power > 0) {
					this.real = this.img = 0;
					return true;
				} else {
					return false;
				}
			} else {

				var p = this.Abs(), phi;

				phi = Math.acos(this.real / p);
				if (this.img < 0) {
					phi = -phi;
				}

				p = Math.pow(p, power);
				phi *= power;

				this.real = Math.cos(phi) * p;
				this.img = Math.sin(phi) * p;

				return true;
			}

		}, Product: function (comp) {

			var a = this.real, b = this.img, c = comp.real, d = comp.img;

			this.real = a * c - b * d;
			this.img = a * d + b * c;

		}, SQRT: function () {

			if (this.real || this.img) {
				var abs = this.Abs(), arg = this.Arg();

				this.real = Math.sqrt(abs) * Math.cos(arg / 2);
				this.img = Math.sqrt(abs) * Math.sin(arg / 2);

			}

		}, Sub: function (comp) {

			this.real -= comp.real;
			this.img -= comp.img;

		}, Sum: function (comp) {

			this.real += comp.real;
			this.img += comp.img;

		}, isImagUnit: function (c) {
			return c == 'i' || c == 'j';
		}, parseComplexStr: function (s) {
			var match = XRegExp.exec(s, rg_complex_number), r, i, suf;
			if (match) {
				r = match["real"];
				i = match["img"];

				if (!(r || i)) {
					return new cError(cErrorType.not_numeric);
				}

				if (i) {
					suf = i[i.length - 1];
					i = i.substr(0, i.length - 1);
					if (i.length == 1 && (i[0] == "-" || i[0] == "+" )) {
						i = parseFloat(i + "1");
					} else {
						i = parseFloat(i);
					}
				} else {
					i = 0;
				}

				if (r) {
					r = parseFloat(r);
				} else {
					r = 0;
				}

				return new Complex(r, i, suf ? suf : "i");

			} else {
				return new cError(cErrorType.not_numeric);
			}
		}, ParseString: function (rStr) {

			var pStr = {pStr: rStr}, f = {f: undefined};

			if (rStr.length == 0) {
				this.real = 0;
				this.img = 0;
				this.suffix = "i";
				return this;
			}

			if (this.isImagUnit(pStr.pStr[0]) && rStr.length == 1) {
				this.real = 0;
				this.img = 1;
				this.suffix = pStr;
				return this;
			}

			if (!this.ParseDouble(pStr, f)) {
				return new cError(cErrorType.not_numeric);
			}

			switch (pStr.pStr[0] + "") {
				case '-':   // imag part follows
				case '+': {
					var r = f.f;

					if (this.isImagUnit(pStr.pStr[1])) {
						this.c = pStr.pStr[1];
						if (pStr.pStr[2] === undefined) {
							this.real = f.f;
							this.img = ( pStr.pStr[0] == '+' ) ? 1.0 : -1.0;
							return this;
						}
					} else if (this.ParseDouble(pStr, f) && this.isImagUnit(pStr.pStr[0])) {
						this.c = pStr.pStr;
						if (pStr.pStr[2] === undefined) {
							this.real = r;
							this.img = f.f;
							return this;
						}
					}
					break;
				}
				case 'j':
				case 'i':
					this.c = pStr;
					if (pStr.pStr[1] === undefined) {
						this.img = f.f;
						this.real = 0.0;
						return this;
					}
					break;
				case "undefined":     // only real-part
					this.real = f.f;
					this.img = 0.0;
					return this;
			}
			return new cError(cErrorType.not_numeric);
		}, ParseDouble: function (rp, rRet) {

			function isnum(c) {
				return c >= '0' && c <= '9';
			}


			function iscomma(c) {
				return c == '.' || c == ',';
			}


			function isexpstart(c) {
				return c == 'e' || c == 'E';
			}


			function isimagunit(c) {
				return c == 'i' || c == 'j';
			}

			var fInt = 0.0, fFrac = 0.0, fMult = 0.1, // multiplier to multiply digits with, when adding fractional ones
				nExp = 0, nMaxExp = 307, nDigCnt = 18, // max. number of digits to read in, rest doesn't matter
				State = {
					end: 0,
					sign: 1,
					intstart: 2,
					int: 3,
					ignoreintdigs: 4,
					frac: 5,
					ignorefracdigs: 6,
					expsign: 7,
					exp: 8
				}, eS = State.sign, bNegNum = false, bNegExp = false, p = rp.pStr, c, i = 0;

			while (eS) {
				c = p[i];
				switch (eS) {
					case State.sign:
						if (isnum(c)) {
							fInt = parseFloat(c);
							nDigCnt--;
							eS = State.int;
						} else if (c == '-') {
							bNegNum = true;
							eS = State.intstart;
						} else if (c == '+') {
							eS = State.intstart;
						} else if (iscomma(c)) {
							eS = State.frac;
						} else {
							return false;
						}
						break;
					case State.intstart:
						if (isnum(c)) {
							fInt = parseFloat(c);
							nDigCnt--;
							eS = State.int;
						} else if (iscomma(c)) {
							eS = State.frac;
						} else if (isimagunit(c)) {
							rRet.f = 0.0;
							return true;
						} else {
							return false;
						}
						break;
					case State.int:
						if (isnum(c)) {
							fInt *= 10.0;
							fInt += parseFloat(c);
							nDigCnt--;
							if (!nDigCnt) {
								eS = State.ignoreintdigs;
							}
						} else if (iscomma(c)) {
							eS = State.frac;
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.ignoreintdigs:
						if (isnum(c)) {
							nExp++;
						}     // just multiply num with 10... ;-)
						else if (iscomma(c)) {
							eS = State.frac;
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.frac:
						if (isnum(c)) {
							fFrac += parseFloat(c) * fMult;
							nDigCnt--;
							if (nDigCnt) {
								fMult *= 0.1;
							} else {
								eS = State.ignorefracdigs;
							}
						} else if (isexpstart(c)) {
							eS = State.expsign;
						} else {
							eS = State.end;
						}
						break;
					case State.ignorefracdigs:
						if (isexpstart(c)) {
							eS = State.expsign;
						} else if (!isnum(c)) {
							eS = State.end;
						}
						break;
					case State.expsign:
						if (isnum(c)) {
							nExp = parseFloat(c);
							eS = State.exp;
						} else if (c == '-') {
							bNegExp = true;
							eS = State.exp;
						} else if (c != '+') {
							eS = State.end;
						}
						break;
					case State.exp:
						if (isnum(c)) {
							nExp *= 10;
							nExp += parseFloat(c);
							if (nExp > nMaxExp) {
								return false;
							}
						} else {
							eS = State.end;
						}
						break;
					case State.end:     // to avoid compiler warning
						break;      // loop exits anyway
				}

				i++;
			}

			i--;        // set pointer back to last
			rp.pStr = p.substr(i);

			fInt += fFrac;
			var nLog10 = Math.log10(fInt);

			if (bNegExp) {
				nExp = -nExp;
			}

			if (nLog10 + nExp > nMaxExp) {
				return false;
			}

			fInt = fInt * Math.pow(10.0, nExp);

			if (bNegNum) {
				fInt = -fInt;
			}

			rRet.f = fInt;

			return true;
		}

	};

	cFormulaFunctionGroup['Engineering'] = cFormulaFunctionGroup['Engineering'] || [];
	cFormulaFunctionGroup['Engineering'].push(cBESSELI, cBESSELJ, cBESSELK, cBESSELY, cBIN2DEC, cBIN2HEX, cBIN2OCT,
		cBITAND, cBITLSHIFT, cBITOR, cBITRSHIFT, cBITXOR, cCOMPLEX, cCONVERT, cDEC2BIN, cDEC2HEX, cDEC2OCT, cDELTA,
		cERF, cERF_PRECISE, cERFC, cERFC_PRECISE, cGESTEP, cHEX2BIN, cHEX2DEC, cHEX2OCT, cIMABS, cIMAGINARY,
		cIMARGUMENT, cIMCONJUGATE, cIMCOS, cIMCOSH, cIMCOT, cIMCSC, cIMCSCH, cIMDIV, cIMEXP, cIMLN, cIMLOG10, cIMLOG2,
		cIMPOWER, cIMPRODUCT, cIMREAL, cIMSEC, cIMSECH, cIMSIN, cIMSINH, cIMSQRT, cIMSUB, cIMSUM, cIMTAN, cOCT2BIN,
		cOCT2DEC, cOCT2HEX);

	cFormulaFunctionGroup['NotRealised'] = cFormulaFunctionGroup['NotRealised'] || [];
	cFormulaFunctionGroup['NotRealised'].push(cBESSELI, cBESSELY, cCONVERT);

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELI() {
		cBaseFunction.call(this, "BESSELI");
	}

	cBESSELI.prototype = Object.create(cBaseFunction.prototype);
	cBESSELI.prototype.constructor = cBESSELI;
	cBESSELI.prototype.argumentsMin = 2;
	cBESSELI.prototype.argumentsMax = 2;
	/*cBESSELI.prototype.Calculate = function ( arg ) {
		 var x = arg[0],
		 n = arg[1];

		 if ( x instanceof cArea || x instanceof cArea3D ) {
		 	x = x.cross( arguments[1] );
		 }
		 else if ( x instanceof cArray ) {
		 	x = x.getElementRowCol( 0, 0 );
		 }

		 if ( n instanceof cArea || n instanceof cArea3D ) {
		 	n = n.cross( arguments[1] );
		 }
		 else if ( n instanceof cArray ) {
		 	n = n.getElementRowCol( 0, 0 );
		 }

		 x = x.tocNumber();
		 n = n.tocNumber();

		 if ( x instanceof cError ) {
			 return this.value = x;
		 }
		 if ( n instanceof cError ) {
		 	return this.value = n;
		 }

		 x = x.getValue();
		 n = n.getValue();

		 if ( n < 0 ){
			 return this.value = new cError( cErrorType.not_numeric );
		 }
		 if(x < 0){
		 	x = Math.abs(x);
		 }
		 n = Math.floor(n);

		 this.value = BesselI( x, n );
		 return this.value;
	 };*/

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELJ() {
		cBaseFunction.call(this, "BESSELJ");
	}

	cBESSELJ.prototype = Object.create(cBaseFunction.prototype);
	cBESSELJ.prototype.constructor = cBESSELJ;
	cBESSELJ.prototype.argumentsMin = 2;
	cBESSELJ.prototype.argumentsMax = 2;
	cBESSELJ.prototype.Calculate = function ( arg ) {
		//результаты вычислений как в LO
		var oArguments = this._prepareArguments(arg, arguments[1], true);
		var argClone = oArguments.args;

		argClone[0] = argClone[0].tocNumber();
		argClone[1] = argClone[1].tocNumber();

		var argError;
		if (argError = this._checkErrorArg(argClone)) {
			return this.value = argError;
		}

		var calcFunc = function(argArray){
			var x = argArray[0];
			var n = argArray[1];

			if ( n < 0 ){
				return new cError( cErrorType.not_numeric );
			}
			if(x < 0){
				x = Math.abs(x);
			}
			n = Math.floor(n);

			return BesselJ( x, n );
		};

		return this.value = this._findArrayInNumberArguments(oArguments, calcFunc);
	};


	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELK() {
		cBaseFunction.call(this, "BESSELK");
	}

	cBESSELK.prototype = Object.create(cBaseFunction.prototype);
	cBESSELK.prototype.constructor = cBESSELK;
	cBESSELK.prototype.argumentsMin = 2;
	cBESSELK.prototype.argumentsMax = 2;
	cBESSELK.prototype.Calculate = function ( arg ) {
		//результаты вычислений как в LO
		var oArguments = this._prepareArguments(arg, arguments[1], true);
		var argClone = oArguments.args;

		argClone[0] = argClone[0].tocNumber();
		argClone[1] = argClone[1].tocNumber();

		var argError;
		if (argError = this._checkErrorArg(argClone)) {
			return this.value = argError;
		}

		var calcFunc = function(argArray){
			var x = argArray[0];
			var n = argArray[1];

			if ( n < 0 || x < 0){
				return new cError( cErrorType.not_numeric );
			}
			/*if(x < 0){
				x = Math.abs(x);
			}*/
			n = Math.floor(n);

			return BesselK( x, n );
		};

		return this.value = this._findArrayInNumberArguments(oArguments, calcFunc);
	};


	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBESSELY() {
		cBaseFunction.call(this, "BESSELY");
	}

	cBESSELY.prototype = Object.create(cBaseFunction.prototype);
	cBESSELY.prototype.constructor = cBESSELY;

	/**
	 * @constructor
	 * @extends {AscCommonExcel.cBaseFunction}
	 */
	function cBIN2DEC() {
		cBaseFunction.call(this, "BIN2DEC");
	}

	cBIN2DEC.prototype = Object.create(cBaseFunction.prototype);
	cBIN2DEC.prototype.constructor = cBIN2DEC;
{'fraction_non_alphanumeric': 0.16826390476357944, 'fraction_numerical': 0.029084386924334876, 'mean_word_length': 2.3087037468000933, 'pattern_counts': {'":': 2, '<': 62, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3826807', 'n_tokens_mistral': 28723, 'n_tokens_neox': 22911, 'n_words': 5370}