All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mariuszgromada.math.mxparser.mathcollection.SpecialFunctions Maven / Gradle / Ivy

Go to download

mXparser is a super easy, rich, fast and highly flexible math expression parser library (parser and evaluator of mathematical expressions / formulas provided as plain text / string). Software delivers easy to use API for JAVA, Android and C# .NET/MONO (Common Language Specification compliant: F#, Visual Basic, C++/CLI). *** If you find the software useful donation is something you might consider: https://mathparser.org/donate/ *** Scalar Scientific Calculator, Charts and Scripts, Scalar Lite: https://play.google.com/store/apps/details?id=org.mathparser.scalar.lite *** Scalar Pro: https://play.google.com/store/apps/details?id=org.mathparser.scalar.pro *** ScalarMath.org: https://scalarmath.org/ *** MathSpace.pl: https://mathspace.pl/ ***

There is a newer version: 6.1.0
Show newest version
/*
 * @(#)SpecialFunctions.java        4.4.3    2022-05-28
 *
 * MathParser.org-mXparser DUAL LICENSE AGREEMENT as of date 2022-05-22
 * The most up-to-date license is available at the below link:
 * - https://mathparser.org/mxparser-license
 *
 * AUTHOR: Copyright 2010 - 2022 Mariusz Gromada - All rights reserved
 * PUBLISHER: INFIMA - https://payhip.com/infima
 *
 * SOFTWARE means source code and/or binary form and/or documentation.
 * PRODUCT: MathParser.org-mXparser SOFTWARE
 * LICENSE: DUAL LICENSE AGREEMENT
 *
 * BY INSTALLING, COPYING, OR OTHERWISE USING THE PRODUCT, YOU AGREE TO BE
 * BOUND BY ALL OF THE TERMS AND CONDITIONS OF THE DUAL LICENSE AGREEMENT.
 *
 * AUTHOR & PUBLISHER provide the PRODUCT under the DUAL LICENSE AGREEMENT
 * model designed to meet the needs of both non-commercial use as well as
 * commercial use.
 *
 * NON-COMMERCIAL USE means any use or activity where a fee is not charged
 * and the purpose is not the sale of a good or service, and the use or
 * activity is not intended to produce a profit. NON-COMMERCIAL USE examples:
 *
 * 1. Free Open-Source Software ("FOSS").
 * 2. Non-commercial use in research, scholarly and education.
 *
 * COMMERCIAL USE means any use or activity where a fee is charged or the
 * purpose is the sale of a good or service, or the use or activity is
 * intended to produce a profit. COMMERCIAL USE examples:
 *
 * 1. OEMs (Original Equipment Manufacturers).
 * 2. ISVs (Independent Software Vendors).
 * 3. VARs (Value Added Resellers).
 * 4. Other distributors that combine and distribute commercially licensed
 *    software.
 *
 * IN CASE YOU WANT TO USE THE PRODUCT COMMERCIALLY, YOU MUST PURCHASE THE
 * APPROPRIATE LICENSE FROM "INFIMA" ONLINE STORE, STORE ADDRESS:
 *
 * 1. https://mathparser.org/order-commercial-license
 * 2. https://payhip.com/infima
 *
 * NON-COMMERCIAL LICENSE
 *
 * Redistribution and use of the PRODUCT in source and/or binary forms,
 * with or without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain unmodified content of the
 *    entire MathParser.org-mXparser DUAL LICENSE AGREEMENT, including
 *    definition of NON-COMMERCIAL USE, definition of COMMERCIAL USE,
 *    NON-COMMERCIAL LICENSE conditions, COMMERCIAL LICENSE conditions, and
 *    the following DISCLAIMER.
 * 2. Redistributions in binary form must reproduce the entire content of
 *    MathParser.org-mXparser DUAL LICENSE AGREEMENT in the documentation
 *    and/or other materials provided with the distribution, including
 *    definition of NON-COMMERCIAL USE, definition of COMMERCIAL USE,
 *    NON-COMMERCIAL LICENSE conditions, COMMERCIAL LICENSE conditions, and
 *    the following DISCLAIMER.
 *
 * COMMERCIAL LICENSE
 *
 *  1. Before purchasing a commercial license, AUTHOR & PUBLISHER allow you
 *     to download, install and use up to three copies of the PRODUCT to
 *     perform integration tests, confirm the quality of the PRODUCT and
 *     its suitability. The testing period should be limited to fourteen
 *     days. Tests should be performed under the conditions of test
 *     environments. The purpose of the tests must not be to generate profit.
 *  2. Provided that you purchased a license from "INFIMA" online store
 *     (store address: https://mathparser.org/order-commercial-license or
 *     https://payhip.com/infima), and you comply with all below terms and
 *     conditions, and you have acknowledged and understood the following
 *     DISCLAIMER, AUTHOR & PUBLISHER grant you a nonexclusive license
 *     including the following rights:
 *  3. The license has been granted only to you, i.e., the person or entity
 *     that made the purchase, who is identified and confirmed by the data
 *     provided during the purchase.
 *  4. In case you purchased a license in the "ONE-TIME PURCHASE" model,
 *     the license has been granted only for the PRODUCT version specified
 *     in the purchase. The upgrade policy gives you additional rights and
 *     is described in the dedicated section below.
 *  5. In case you purchased a license in the "SUBSCRIPTION" model, you can
 *     install and use any version of the PRODUCT, but only during the
 *     subscription validity period.
 *  6. In case you purchased a "SINGLE LICENSE" you can install and use the
 *     PRODUCT from one workstation.
 *  7. Additional copies of the PRODUCT can be installed and used from more
 *     than one workstation; however, this number is limited to the number
 *     of workstations purchased as per order.
 *  8. In case you purchased a "SITE LICENSE ", the PRODUCT can be installed
 *     and used from all workstations located at your premises.
 *  9. You may incorporate the unmodified PRODUCT into your own products
 *     and software.
 * 10. If you purchased a license with the "SOURCE CODE" option, you may
 *     modify the PRODUCT's source code and incorporate the modified source
 *     code into your own products and/or software.
 * 11. Provided that the license validity period has not expired, you may
 *     distribute your product and/or software with the incorporated
 *     PRODUCT royalty-free.
 * 12. You may make copies of the PRODUCT for backup and archival purposes.
 * 13. AUTHOR & PUBLISHER reserve all rights not expressly granted to you
 *     in this agreement.
 *
 * ADDITIONAL CLARIFICATION ON WORKSTATION
 *
 * A workstation is a device, a remote device, or a virtual device, used by
 * you, your employees, or other entities to whom you have commissioned the
 * tasks. For example, the number of workstations may refer to the number
 * of software developers, engineers, architects, scientists, and other
 * professionals who use the PRODUCT on your behalf. The number of
 * workstations is not the number of copies of your end-product that you
 * distribute to your end-users.
 *
 * By purchasing the COMMERCIAL LICENSE, you only pay for the number of
 * workstations, while the number of copies of your final product
 * (delivered to your end-users) is not limited.
 *
 * UPGRADE POLICY
 *
 * The PRODUCT is versioned according to the following convention:
 *
 *    [MAJOR].[MINOR].[PATCH]
 *
 * 1. COMMERCIAL LICENSE holders can install and use the updated version
 *    for bug fixes free of charge, i.e. if you have purchased a license
 *    for the [MAJOR].[MINOR] version (e.g.: 5.0), you can freely install
 *    all the various releases specified in the [PATCH] version (e.g.: 5.0.2).
 *    The license terms remain unchanged after the update.
 * 2. COMMERCIAL LICENSE holders for [MAJOR].[MINOR] version (e.g.: 5.0)
 *    can install and use the updated version [MAJOR].[MINOR + 1] free of
 *    charge, i.e., plus one release in the [MINOR] range (e.g.: 5.1). The
 *    license terms remain unchanged after the update.
 * 3. COMMERCIAL LICENSE holders who wish to upgrade their version, but are
 *    not eligible for the free upgrade, can claim a discount when
 *    purchasing the upgrade. For this purpose, please contact us via e-mail.
 *
 * DISCLAIMER
 *
 * THIS PRODUCT IS PROVIDED BY AUTHOR & PUBLISHER "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR OR PUBLISHER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS PRODUCT, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE VIEWS AND CONCLUSIONS CONTAINED IN THE PRODUCT AND DOCUMENTATION ARE
 * THOSE OF THE AUTHORS AND SHOULD NOT BE INTERPRETED AS REPRESENTING
 * OFFICIAL POLICIES, EITHER EXPRESSED OR IMPLIED, OF AUTHOR OR PUBLISHER.
 *
 * CONTACT
 *
 * - e-mail: [email protected]
 * - website: https://mathparser.org
 * - source code: https://github.com/mariuszgromada/MathParser.org-mXparser
 * - online store: https://mathparser.org/order-commercial-license
 * - online store: https://payhip.com/infima
 */
package org.mariuszgromada.math.mxparser.mathcollection;

import org.mariuszgromada.math.mxparser.mXparser;

/**
 * SpecialFunctions - special (non-elementary functions).
 *
 * @author         Mariusz Gromada
* [email protected]
* MathSpace.pl
* MathParser.org - mXparser project page
* mXparser on GitHub
* mXparser on SourceForge
* mXparser on Bitbucket
* mXparser on CodePlex
* Janet Sudoku - project web page
* Janet Sudoku on GitHub
* Janet Sudoku on CodePlex
* Janet Sudoku on SourceForge
* Janet Sudoku on BitBucket
* Scalar Free
* Scalar Pro
* ScalarMath.org
* * @version 4.3.0 */ public final class SpecialFunctions { /** * Exponential integral function Ei(x) * @param x Point at which function will be evaluated. * @return Exponential integral function Ei(x) */ public static double exponentialIntegralEi(double x) { if (Double.isNaN(x)) return Double.NaN; if (x < -5.0) return continuedFractionEi(x); if (x == 0.0) return -Double.MAX_VALUE; if (x < 6.8) return powerSeriesEi(x); if (x < 50.0) return argumentAdditionSeriesEi(x); return continuedFractionEi(x); } /** * Constants for Exponential integral function Ei(x) calculation */ private static final double EI_DBL_EPSILON = Math.ulp(1.0); private static final double EI_EPSILON = 10.0 * EI_DBL_EPSILON; /** * Supporting function * while Exponential integral function Ei(x) calculation */ private static double continuedFractionEi(double x) { double Am1 = 1.0; double A0 = 0.0; double Bm1 = 0.0; double B0 = 1.0; double a = Math.exp(x); double b = -x + 1.0; double Ap1 = b * A0 + a * Am1; double Bp1 = b * B0 + a * Bm1; int j = 1; a = 1.0; while (Math.abs(Ap1 * B0 - A0 * Bp1) > EI_EPSILON * Math.abs(A0 * Bp1)) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; if (Math.abs(Bp1) > 1.0) { Am1 = A0 / Bp1; A0 = Ap1 / Bp1; Bm1 = B0 / Bp1; B0 = 1.0; } else { Am1 = A0; A0 = Ap1; Bm1 = B0; B0 = Bp1; } a = -j * j; b += 2.0; Ap1 = b * A0 + a * Am1; Bp1 = b * B0 + a * Bm1; j += 1; } return (-Ap1 / Bp1); } /** * Supporting function * while Exponential integral function Ei(x) calculation */ private static double powerSeriesEi(double x) { double xn = -x; double Sn = -x; double Sm1 = 0.0; double hsum = 1.0; final double g = MathConstants.EULER_MASCHERONI; double y = 1.0; double factorial = 1.0; if (x == 0.0) return -Double.MAX_VALUE; while (Math.abs(Sn - Sm1) > EI_EPSILON * Math.abs(Sm1)) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; Sm1 = Sn; y += 1.0; xn *= (-x); factorial *= y; hsum += (1.0 / y); Sn += hsum * xn / factorial; } return (g + Math.log(Math.abs(x)) - Math.exp(x) * Sn); } /** * Supporting function * while Exponential integral function Ei(x) calculation */ private static double argumentAdditionSeriesEi(double x) { final int k = (int) (x + 0.5); int j = 0; final double xx = k; final double dx = x - xx; double xxj = xx; final double edx = Math.exp(dx); double Sm = 1.0; double Sn = (edx - 1.0) / xxj; double term = Double.MAX_VALUE; double factorial = 1.0; double dxj = 1.0; while (Math.abs(term) > EI_EPSILON * Math.abs(Sn)) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; j++; factorial *= j; xxj *= xx; dxj *= (-dx); Sm += (dxj / factorial); term = (factorial * (edx * Sm - 1.0)) / xxj; Sn += term; } return Coefficients.EI[k - 7] + Sn * Math.exp(xx); } /** * Logarithmic integral function li(x) * @param x Point at which function will be evaluated. * @return Logarithmic integral function li(x) */ public static final double logarithmicIntegralLi(double x) { if (Double.isNaN(x)) return Double.NaN; if (x < 0) return Double.NaN; if (x == 0) return 0; if (x == 2) return MathConstants.LI2; return exponentialIntegralEi( MathFunctions.ln(x) ); } /** * Offset logarithmic integral function Li(x) * @param x Point at which function will be evaluated. * @return Offset logarithmic integral function Li(x) */ public static final double offsetLogarithmicIntegralLi(double x) { if (Double.isNaN(x)) return Double.NaN; if (x < 0) return Double.NaN; if (x == 0) return -MathConstants.LI2; return logarithmicIntegralLi(x) - MathConstants.LI2; } /** * Calculates the error function * @param x Point at which function will be evaluated. * @return Error function erf(x) */ public static final double erf(double x) { if (Double.isNaN(x)) return Double.NaN; if (x == 0) return 0; if (x == Double.POSITIVE_INFINITY) return 1.0; if (x == Double.NEGATIVE_INFINITY) return -1.0; return erfImp(x, false); } /** * Calculates the complementary error function. * @param x Point at which function will be evaluated. * @return Complementary error function erfc(x) */ public static final double erfc(double x) { if (Double.isNaN(x)) return Double.NaN; if (x == 0) return 1; if (x == Double.POSITIVE_INFINITY) return 0.0; if (x == Double.NEGATIVE_INFINITY) return 2.0; return erfImp(x, true); } /** * Calculates the inverse error function evaluated at x. * @param x Point at which function will be evaluated. * @return Inverse error function erfInv(x) */ public static final double erfInv(double x) { if (x == 0.0) return 0; if (x >= 1.0) return Double.POSITIVE_INFINITY; if (x <= -1.0) return Double.NEGATIVE_INFINITY; double p, q, s; if (x < 0) { p = -x; q = 1 - p; s = -1; } else { p = x; q = 1 - x; s = 1; } return erfInvImpl(p, q, s); } /** * Calculates the inverse error function evaluated at x. * @param x * @param invert * @return */ private static final double erfImp(double z, boolean invert) { if (z < 0) { if (!invert) return -erfImp(-z, false); if (z < -0.5) return 2 - erfImp(-z, true); return 1 + erfImp(-z, false); } double result; if (z < 0.5) { if (z < 1e-10) result = (z*1.125) + (z*0.003379167095512573896158903121545171688); else result = (z*1.125) + (z*Evaluate.polynomial(z, Coefficients.erfImpAn) / Evaluate.polynomial(z, Coefficients.erfImpAd)); } else if ((z < 110) || ((z < 110) && invert)) { invert = !invert; double r, b; if(z < 0.75) { r = Evaluate.polynomial(z - 0.5, Coefficients.erfImpBn) / Evaluate.polynomial(z - 0.5, Coefficients.erfImpBd); b = 0.3440242112F; } else if (z < 1.25) { r = Evaluate.polynomial(z - 0.75, Coefficients.erfImpCn) / Evaluate.polynomial(z - 0.75, Coefficients.erfImpCd); b = 0.419990927F; } else if (z < 2.25) { r = Evaluate.polynomial(z - 1.25, Coefficients.erfImpDn) / Evaluate.polynomial(z - 1.25, Coefficients.erfImpDd); b = 0.4898625016F; } else if (z < 3.5) { r = Evaluate.polynomial(z - 2.25, Coefficients.erfImpEn) / Evaluate.polynomial(z - 2.25, Coefficients.erfImpEd); b = 0.5317370892F; } else if (z < 5.25) { r = Evaluate.polynomial(z - 3.5, Coefficients.erfImpFn) / Evaluate.polynomial(z - 3.5, Coefficients.erfImpFd); b = 0.5489973426F; } else if (z < 8) { r = Evaluate.polynomial(z - 5.25, Coefficients.erfImpGn) / Evaluate.polynomial(z - 5.25, Coefficients.erfImpGd); b = 0.5571740866F; } else if (z < 11.5) { r = Evaluate.polynomial(z - 8, Coefficients.erfImpHn) / Evaluate.polynomial(z - 8, Coefficients.erfImpHd); b = 0.5609807968F; } else if (z < 17) { r = Evaluate.polynomial(z - 11.5, Coefficients.erfImpIn) / Evaluate.polynomial(z - 11.5, Coefficients.erfImpId); b = 0.5626493692F; } else if (z < 24) { r = Evaluate.polynomial(z - 17, Coefficients.erfImpJn) / Evaluate.polynomial(z - 17, Coefficients.erfImpJd); b = 0.5634598136F; } else if (z < 38) { r = Evaluate.polynomial(z - 24, Coefficients.erfImpKn) / Evaluate.polynomial(z - 24, Coefficients.erfImpKd); b = 0.5638477802F; } else if (z < 60) { r = Evaluate.polynomial(z - 38, Coefficients.erfImpLn) / Evaluate.polynomial(z - 38, Coefficients.erfImpLd); b = 0.5640528202F; } else if (z < 85) { r = Evaluate.polynomial(z - 60, Coefficients.erfImpMn) / Evaluate.polynomial(z - 60, Coefficients.erfImpMd); b = 0.5641309023F; } else { r = Evaluate.polynomial(z - 85, Coefficients.erfImpNn) / Evaluate.polynomial(z - 85, Coefficients.erfImpNd); b = 0.5641584396F; } double g = MathFunctions.exp(-z*z)/z; result = (g*b) + (g*r); } else { result = 0; invert = !invert; } if (invert) result = 1 - result; return result; } /** * Calculates the complementary inverse error function evaluated at x. * @param z Point at which function will be evaluated. * @return Inverse of complementary inverse error function erfcInv(x) */ public static final double erfcInv(double z) { if (z <= 0.0) return Double.POSITIVE_INFINITY; if (z >= 2.0) return Double.NEGATIVE_INFINITY; double p, q, s; if (z > 1) { q = 2 - z; p = 1 - q; s = -1; } else { p = 1 - z; q = z; s = 1; } return erfInvImpl(p, q, s); } /** * The implementation of the inverse error function. * @param p * @param q * @param s * @return */ private static final double erfInvImpl(double p, double q, double s) { double result; if (p <= 0.5) { final float y = 0.0891314744949340820313f; double g = p*(p + 10); double r = Evaluate.polynomial(p, Coefficients.ervInvImpAn) / Evaluate.polynomial(p, Coefficients.ervInvImpAd); result = (g*y) + (g*r); } else if (q >= 0.25) { final float y = 2.249481201171875f; double g = MathFunctions.sqrt(-2 * MathFunctions.ln(q)); double xs = q - 0.25; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpBn) / Evaluate.polynomial(xs, Coefficients.ervInvImpBd); result = g/(y + r); } else { double x = MathFunctions.sqrt(-MathFunctions.ln(q)); if (x < 3) { final float y = 0.807220458984375f; double xs = x - 1.125; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpCn) / Evaluate.polynomial(xs, Coefficients.ervInvImpCd); result = (y*x) + (r*x); } else if (x < 6) { final float y = 0.93995571136474609375f; double xs = x - 3; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpDn) / Evaluate.polynomial(xs, Coefficients.ervInvImpDd); result = (y*x) + (r*x); } else if (x < 18) { final float y = 0.98362827301025390625f; double xs = x - 6; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpEn) / Evaluate.polynomial(xs, Coefficients.ervInvImpEd); result = (y*x) + (r*x); } else if (x < 44) { final float y = 0.99714565277099609375f; double xs = x - 18; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpFn) / Evaluate.polynomial(xs, Coefficients.ervInvImpFd); result = (y*x) + (r*x); } else { final float y = 0.99941349029541015625f; double xs = x - 44; double r = Evaluate.polynomial(xs, Coefficients.ervInvImpGn) / Evaluate.polynomial(xs, Coefficients.ervInvImpGd); result = (y*x) + (r*x); } } return s*result; } /** * Gamma function for the integers * @param n Integer number * @return Returns Gamma function for the integers. */ private static final double gammaInt(long n) { if (n == 0) return MathConstants.EULER_MASCHERONI; if (n == 1) return 1; if (n == 2) return 1; if (n == 3) return 1.0*2.0; if (n == 4) return 1.0*2.0*3.0; if (n == 5) return 1.0*2.0*3.0*4.0; if (n == 6) return 1.0*2.0*3.0*4.0*5.0; if (n == 7) return 1.0*2.0*3.0*4.0*5.0*6.0; if (n == 8) return 1.0*2.0*3.0*4.0*5.0*6.0*7.0; if (n == 9) return 1.0*2.0*3.0*4.0*5.0*6.0*7.0*8.0; if (n == 10) return 1.0*2.0*3.0*4.0*5.0*6.0*7.0*8.0*9.0; if (n >= 11) return MathFunctions.factorial(n-1); if (n <= -1) { long r = -n; double factr = MathFunctions.factorial(r); double sign = -1; if (r % 2 == 0) sign = 1; return sign / (r * factr) - (1.0 / r) * gammaInt(n + 1); } return Double.NaN; } /** * Real valued Gamma function * * @param x Argument value * @return Returns gamma function value. */ public static final double gamma(double x) { if (Double.isNaN(x)) return Double.NaN; if (x == Double.POSITIVE_INFINITY) return Double.POSITIVE_INFINITY; if (x == Double.NEGATIVE_INFINITY) return Double.NaN; double xabs = MathFunctions.abs(x); double xint = Math.round(xabs); if ( MathFunctions.abs(xabs-xint) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON ) { long n = (long)xint; if (x < 0) n = -n; return gammaInt(n); } return lanchosGamma(x); } /** * Gamma function implementation based on * Lanchos approximation algorithm * * @param x Function parameter * @return Gamma function value (Lanchos approx). */ public static final double lanchosGamma(double x) { if (Double.isNaN(x)) return Double.NaN; double xabs = MathFunctions.abs(x); double xint = Math.round(xabs); if (x > BinaryRelations.DEFAULT_COMPARISON_EPSILON) { if ( MathFunctions.abs(xabs-xint) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON ) return MathFunctions.factorial(xint-1); } else if (x < -BinaryRelations.DEFAULT_COMPARISON_EPSILON) { if ( MathFunctions.abs(xabs-xint) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON ) return Double.NaN; } else return Double.NaN; if(x < 0.5) return MathConstants.PI / (Math.sin(MathConstants.PI * x) * lanchosGamma(1-x)); int g = 7; x -= 1; double a = Coefficients.lanchosGamma[0]; double t = x+g+0.5; for(int i = 1; i < Coefficients.lanchosGamma.length; i++){ a += Coefficients.lanchosGamma[i] / (x+i); } return Math.sqrt(2*MathConstants.PI) * Math.pow(t, x+0.5) * Math.exp(-t) * a; } /** * Real valued log gamma function. * @param x Argument value * @return Returns log value from gamma function. */ public static double logGamma(double x) { if (Double.isNaN(x)) return Double.NaN; if (x == Double.POSITIVE_INFINITY) return Double.POSITIVE_INFINITY; if (x == Double.NEGATIVE_INFINITY) return Double.NaN; if (MathFunctions.isInteger(x)) { if (x >= 0) return Math.log( Math.abs( gammaInt( (long)(Math.round(x) ) ) ) ); else return Math.log( Math.abs( gammaInt( -(long)(Math.round(-x) ) ) ) ); } double p, q, w, z; if (x < -34.0) { q = -x; w = logGamma(q); p = Math.floor(q); if (Math.abs(p-q) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; z = q - p; if (z > 0.5) { p += 1.0; z = p - q; } z = q * Math.sin( Math.PI * z ); if (Math.abs(z) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; z = MathConstants.LNPI - Math.log(z) - w; return z; } if (x < 13.0) { z = 1.0; while (x >= 3.0) { x -= 1.0; z *= x; } while (x < 2.0) { if( Math.abs(x) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON ) return Double.NaN; z /= x; x += 1.0; } if (z < 0.0) z = -z; if (x == 2.0) return Math.log(z); x -= 2.0; p = x * Evaluate.polevl( x, Coefficients.logGammaB, 5 ) / Evaluate.p1evl( x, Coefficients.logGammaC, 6); return Math.log(z) + p; } if (x > 2.556348e305) return Double.NaN; q = (x - 0.5) * Math.log(x) - x + 0.91893853320467274178; if (x > 1.0e8) return q; p = 1.0/(x*x); if (x >= 1000.0) q += (( 7.9365079365079365079365e-4 * p - 2.7777777777777777777778e-3 ) * p + 0.0833333333333333333333 ) / x; else q += Evaluate.polevl( p, Coefficients.logGammaA, 4 ) / x; return q; } /** * Signum from the real valued gamma function. * @param x Argument value * @return Returns signum of the gamma(x) */ public static final double sgnGamma(double x) { if (Double.isNaN(x)) return Double.NaN; if (x == Double.POSITIVE_INFINITY) return 1; if (x == Double.NEGATIVE_INFINITY) return Double.NaN; if (x > 0) return 1; if (MathFunctions.isInteger(x)) return MathFunctions.sgn( gammaInt( -(long)(Math.round(-x) ) ) ); x = -x; double fx = Math.floor(x); double div2remainder = Math.floor(fx % 2); if (div2remainder == 0) return -1; else return 1; } /** * Regularized lower gamma function 'P' * @param s Argument value * @param x Argument value * @return Value of the regularized lower gamma function 'P'. */ public static final double regularizedGammaLowerP(double s, double x) { if (Double.isNaN(x)) return Double.NaN; if (Double.isNaN(s)) return Double.NaN; if (MathFunctions.almostEqual(x, 0)) return 0; if (MathFunctions.almostEqual(s, 0)) return 1 + SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI; if (MathFunctions.almostEqual(s, 1)) return 1 - Math.exp(-x); if (x < 0) return Double.NaN; if (s < 0) return regularizedGammaLowerP(s + 1, x) + ( Math.pow(x, s) * Math.exp(-x) ) / ( s * gamma(s) ); final double epsilon = 0.000000000000001; final double bigNumber = 4503599627370496.0; final double bigNumberInverse = 2.22044604925031308085e-16; double ax = (s * Math.log(x)) - x - logGamma(s); if (ax < -709.78271289338399) { return 1; } if (x <= 1 || x <= s) { double r2 = s; double c2 = 1; double ans2 = 1; do { r2 = r2 + 1; c2 = c2 * x / r2; ans2 += c2; } while ((c2 / ans2) > epsilon); return Math.exp(ax) * ans2 / s; } int c = 0; double y = 1 - s; double z = x + y + 1; double p3 = 1; double q3 = x; double p2 = x + 1; double q2 = z * x; double ans = p2 / q2; double error; do { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; c++; y += 1; z += 2; double yc = y * c; double p = (p2 * z) - (p3 * yc); double q = (q2 * z) - (q3 * yc); if (q != 0) { double nextans = p / q; error = Math.abs((ans - nextans) / nextans); ans = nextans; } else { // zero div, skip error = 1; } // shift p3 = p2; p2 = p; q3 = q2; q2 = q; // normalize fraction when the numerator becomes large if (Math.abs(p) > bigNumber) { p3 *= bigNumberInverse; p2 *= bigNumberInverse; q3 *= bigNumberInverse; q2 *= bigNumberInverse; } } while (error > epsilon); return 1 - (Math.exp(ax) * ans); } /** * Incomplete lower gamma function * @param s Argument value * @param x Argument value * @return Value of the incomplete lower gamma function. */ public static final double incompleteGammaLower(double s, double x) { return gamma(s) * regularizedGammaLowerP(s, x); } /** * Regularized upper gamma function 'Q' * @param s Argument value * @param x Argument value * @return Value of the regularized upper gamma function 'Q'. */ public static final double regularizedGammaUpperQ(double s, double x) { if (Double.isNaN(x)) return Double.NaN; if (Double.isNaN(s)) return Double.NaN; if (MathFunctions.almostEqual(x, 0)) return 1; if (MathFunctions.almostEqual(s, 0)) return -SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI; if (MathFunctions.almostEqual(s, 1)) return Math.exp(-x); if (x < 0) return Double.NaN; if (s < 0) return regularizedGammaUpperQ(s + 1, x) - ( Math.pow(x, s) * Math.exp(-x) ) / ( s * gamma(s) ); double ax = s * Math.log(x) - x - logGamma(s); if (ax < -709.78271289338399) { return 0; } double t; final double igammaepsilon = 0.000000000000001; final double igammabignumber = 4503599627370496.0; final double igammabignumberinv = 2.22044604925031308085 * 0.0000000000000001; ax = Math.exp(ax); double y = 1 - s; double z = x + y + 1; double c = 0; double pkm2 = 1; double qkm2 = x; double pkm1 = x + 1; double qkm1 = z * x; double ans = pkm1 / qkm1; do { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; c = c + 1; y = y + 1; z = z + 2; double yc = y * c; double pk = pkm1 * z - pkm2 * yc; double qk = qkm1 * z - qkm2 * yc; if (qk != 0) { double r = pk / qk; t = Math.abs((ans - r) / r); ans = r; } else { t = 1; } pkm2 = pkm1; pkm1 = pk; qkm2 = qkm1; qkm1 = qk; if (Math.abs(pk) > igammabignumber) { pkm2 = pkm2 * igammabignumberinv; pkm1 = pkm1 * igammabignumberinv; qkm2 = qkm2 * igammabignumberinv; qkm1 = qkm1 * igammabignumberinv; } } while (t > igammaepsilon); return ans * ax; } /** * Incomplete upper gamma function * @param s Argument value * @param x Argument value * @return Value of the incomplete upper gamma function. */ public static final double incompleteGammaUpper(double s, double x) { return gamma(s) * regularizedGammaUpperQ(s, x); } /** * Digamma function as the logarithmic derivative of the Gamma special function * @param x Argument value * @return Approximated value of the digamma function. */ public static final double diGamma(double x) { final double c = 12.0; final double d1 = -0.57721566490153286; final double d2 = 1.6449340668482264365; final double s = 1e-6; final double s3 = 1.0/12.0; final double s4 = 1.0/120.0; final double s5 = 1.0/252.0; final double s6 = 1.0/240.0; final double s7 = 1.0/132.0; if (Double.isNaN(x)) return Double.NaN; if (x == Double.NEGATIVE_INFINITY) return Double.NaN; if (x <= 0) if (MathFunctions.isInteger(x)) return Double.NaN; // Use inversion formula for negative numbers. if (x < 0) return diGamma(1.0 - x) + (MathConstants.PI/Math.tan(-Math.PI*x)); if (x <= s) return d1 - (1/x) + (d2*x); double result = 0; while (x < c) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; result -= 1/x; x++; } if (x >= c) { double r = 1/x; result += Math.log(x) - (0.5*r); r *= r; result -= r*(s3 - (r*(s4 - (r*(s5 - (r*(s6 - (r*s7)))))))); } return result; } private static final int doubleWidth = 53; private static final double doublePrecision = Math.pow(2, -doubleWidth); /** * Log Beta special function * @param x Argument value * @param y Argument value * @return Return logBeta special function (for positive x and positive y) */ public static double logBeta(double x, double y) { if (Double.isNaN(x)) return Double.NaN; if (Double.isNaN(y)) return Double.NaN; if ( (x <= 0) || (y <= 0) ) return Double.NaN; double lgx = logGamma(x); if (Double.isNaN(lgx)) lgx = Math.log( Math.abs( gamma(x) ) ); double lgy = logGamma(y); if (Double.isNaN(lgy)) lgy = Math.log( Math.abs( gamma(y) ) ); double lgxy = logGamma(x+y); if (Double.isNaN(lgy)) lgxy = Math.log( Math.abs( gamma(x+y) ) ); if ( (!Double.isNaN(lgx)) && (!Double.isNaN(lgy)) && (!Double.isNaN(lgxy)) ) return (lgx + lgy - lgxy); else return Double.NaN; } /** * Beta special function * @param x Argument value * @param y Argument value * @return Return Beta special function (for positive x and positive y) */ public static double beta(double x, double y) { if (Double.isNaN(x)) return Double.NaN; if (Double.isNaN(y)) return Double.NaN; if ( (x <= 0) || (y <= 0) ) return Double.NaN; if ( (x > 99) || (y > 99) ) return Math.exp(logBeta(x, y)); return gamma(x)*gamma(y) / gamma(x+y); } /** * Log Incomplete Beta special function * @param a Argument value * @param b Argument value * @param x Argument value * @return Return incomplete Beta special function * for positive a and positive b and x between 0 and 1 * */ public static double incompleteBeta(double a, double b, double x) { if (Double.isNaN(a)) return Double.NaN; if (Double.isNaN(b)) return Double.NaN; if (Double.isNaN(x)) return Double.NaN; if (x < -BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; if (x > 1+BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; if ( (a <= 0) || (b <= 0) ) return Double.NaN; if (MathFunctions.almostEqual(x, 0)) return 0; if (MathFunctions.almostEqual(x, 1)) return beta(a, b); boolean aEq0 = MathFunctions.almostEqual(a, 0); boolean bEq0 = MathFunctions.almostEqual(b, 0); boolean aIsInt = MathFunctions.isInteger(a); boolean bIsInt = MathFunctions.isInteger(b); long aInt = 0, bInt = 0; if (aIsInt) aInt = (long)MathFunctions.integerPart(a); if (bIsInt) bInt = (long)MathFunctions.integerPart(b); long n; if (aEq0 && bEq0) return Math.log( x / (1-x) ); if (aEq0 && bIsInt) { n = bInt; if (n >= 1) { if (n == 1) return Math.log(x); if (n == 2) return Math.log(x) + x; double v = Math.log(x); for (long i = 1; i <= n-1; i++) v -= MathFunctions.binomCoeff(n-1, i) * Math.pow(-1, i) * ( Math.pow(x, i) / i ); return v; } if (n <= -1) { if (n == -1) return Math.log( x / (1-x) ) + 1/(1-x) - 1; if (n == -2) return Math.log( x / (1-x) ) - 1/x - 1/(2*x*x); double v = -Math.log(x / (1-x)); for (long i = 1; i <= -n-1; i++) v -= Math.pow(x, -i) / i; return v; } } if (aIsInt && bEq0) { n = aInt; if (n >= 1) { if (n == 1) return -Math.log(1-x); if (n == 2) return -Math.log(1-x) - x; double v = -Math.log(1-x); for (long i = 1; i <= n-1; i++) v -= Math.pow(x, i) / i; return v; } if (n <= -1) { if (n == -1) return Math.log( x / (1-x) ) - 1/x; double v = -Math.log(x / (1-x)); for (long i = 1; i <= -n; i++) v += Math.pow(1-x, -i) / i; for (long i = 1; i <= -n; i++) v -= Math.pow( MathFunctions.factorial(i-1) , 2) / i; return v; } } if(aIsInt) { n = aInt; if (MathFunctions.almostEqual(b, 1)) { if (n <= -1) return -( 1/(-n) ) * Math.pow(x, n); } } return regularizedBeta(a, b, x)*beta(a, b); } /** * Regularized incomplete Beta special function * @param a Argument value * @param b Argument value * @param x Argument value * @return Return incomplete Beta special function * for positive a and positive b and x between 0 and 1 */ public static double regularizedBeta(double a, double b, double x) { if (Double.isNaN(a)) return Double.NaN; if (Double.isNaN(b)) return Double.NaN; if (Double.isNaN(x)) return Double.NaN; if ( (a <= 0) || (b <= 0) ) return Double.NaN; if (x < -BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; if (x > 1+BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; if (MathFunctions.almostEqual(x, 0)) return 0; if (MathFunctions.almostEqual(x, 1)) return 1; double bt = (x == 0.0 || x == 1.0) ? 0.0 : Math.exp(logGamma(a + b) - logGamma(a) - logGamma(b) + (a*Math.log(x)) + (b*Math.log(1.0 - x))); boolean symmetryTransformation = x >= (a + 1.0)/(a + b + 2.0); /* Continued fraction representation */ double eps = doublePrecision; double fpmin = Math.nextUp(0.0)/eps; if (symmetryTransformation) { x = 1.0 - x; double swap = a; a = b; b = swap; } double qab = a + b; double qap = a + 1.0; double qam = a - 1.0; double c = 1.0; double d = 1.0 - (qab*x/qap); if (Math.abs(d) < fpmin) { d = fpmin; } d = 1.0/d; double h = d; for (int m = 1, m2 = 2; m <= 50000; m++, m2 += 2) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; double aa = m*(b - m)*x/((qam + m2)*(a + m2)); d = 1.0 + (aa*d); if (Math.abs(d) < fpmin) { d = fpmin; } c = 1.0 + (aa/c); if (Math.abs(c) < fpmin) { c = fpmin; } d = 1.0/d; h *= d*c; aa = -(a + m)*(qab + m)*x/((a + m2)*(qap + m2)); d = 1.0 + (aa*d); if (Math.abs(d) < fpmin) { d = fpmin; } c = 1.0 + (aa/c); if (Math.abs(c) < fpmin) { c = fpmin; } d = 1.0/d; double del = d*c; h *= del; if (Math.abs(del - 1.0) <= eps) { return symmetryTransformation ? 1.0 - (bt*h/a) : bt*h/a; } } return symmetryTransformation ? 1.0 - (bt*h/a) : bt*h/a; } /* * halleyIteration epsilon */ private static final double GSL_DBL_EPSILON = 2.2204460492503131e-16; /** * Halley iteration used in Lambert-W approximation * @param x Point at which Halley iteration will be calculated * @param wInitial Starting point * @param maxIter Maximum number of iteration * @return Halley iteration value if succesfull, otherwise Double.NaN */ private static final double halleyIteration(double x, double wInitial, int maxIter) { double w = wInitial; double tol = 1; double t = 0, p, e; for (int i = 0; i < maxIter; i++) { if (mXparser.isCurrentCalculationCancelled()) return Double.NaN; e = Math.exp(w); p = w + 1.0; t = w * e - x; if (w > 0) t = (t / p) / e; else t /= e * p - 0.5 * (p + 1.0) * t / p; w -= t; tol = GSL_DBL_EPSILON * Math.max(Math.abs(w), 1.0 / (Math.abs(p) * e)); if (Math.abs(t) < tol) return w; } double perc = Math.abs(t / tol); if (perc >= 0.5 && perc <= 1.5) return w; return Double.NaN; } /** * Private method used in Lambert-W approximation - near zero * @param r * @return Ner zero approximation */ private static final double seriesEval(double r) { double t8 = Coefficients.lambertWqNearZero[8] + r * (Coefficients.lambertWqNearZero[9] + r * (Coefficients.lambertWqNearZero[10] + r * Coefficients.lambertWqNearZero[11])); double t5 = Coefficients.lambertWqNearZero[5] + r * (Coefficients.lambertWqNearZero[6] + r * (Coefficients.lambertWqNearZero[7] + r * t8)); double t1 = Coefficients.lambertWqNearZero[1] + r * (Coefficients.lambertWqNearZero[2] + r * (Coefficients.lambertWqNearZero[3] + r * (Coefficients.lambertWqNearZero[4] + r * t5))); return Coefficients.lambertWqNearZero[0] + r * t1; } /** * W0 - Principal branch of Lambert-W function * @param x * @return Approximation of principal branch of Lambert-W function */ private static final double lambertW0(double x) { if (Math.abs(x) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return 0; if (Math.abs(x + MathConstants.EXP_MINUS_1) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return -1; if (Math.abs(x - 1) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return MathConstants.OMEGA; if (Math.abs(x - MathConstants.E) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return 1; if (Math.abs(x + MathConstants.LN_SQRT2) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return -2 * MathConstants.LN_SQRT2; if (x < -MathConstants.EXP_MINUS_1) return Double.NaN; double q = x + MathConstants.EXP_MINUS_1; if (q < 1.0e-03) return seriesEval(Math.sqrt(q)); final int MAX_ITER = 100; double w; if (x < 1) { final double p = Math.sqrt(2.0 * MathConstants.E * q); w = -1.0 + p * (1.0 + p * (-1.0 / 3.0 + p * 11.0 / 72.0)); } else { w = Math.log(x); if (x > 3.0) w -= Math.log(w); } return halleyIteration(x, w, MAX_ITER); } /** * Minus 1 branch of Lambert-W function * Analytical approximations for real values of the Lambert W-function - D.A. Barry * Mathematics and Computers in Simulation 53 (2000) 95–103 * @param x * @return Approxmiation of minus 1 branch of Lambert-W function */ private static final double lambertW1(double x) { if (x >= -BinaryRelations.DEFAULT_COMPARISON_EPSILON) return Double.NaN; if (x < -MathConstants.EXP_MINUS_1) return Double.NaN; if (Math.abs(x + MathConstants.EXP_MINUS_1) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return -1; /* * Analytical approximations for real values of the Lambert W-function - D.A. Barry * Mathematics and Computers in Simulation 53 (2000) 95–103 */ double M1 = 0.3361; double M2 = -0.0042; double M3 = -0.0201; double s = -1 - Math.log(-x); return -1.0 - s - (2.0/M1) * ( 1.0 - 1.0 / ( 1.0 + ( (M1 * Math.sqrt(s/2.0)) / (1.0 + M2 * s * Math.exp(M3 * Math.sqrt(s)) ) ) ) ); } /** * Real-valued Lambert-W function approximation. * @param x Point at which function will be approximated * @param branch Branch id, 0 for principal branch, -1 for the other branch * @return Principal branch for x greater or equal than -1/e, otherwise Double.NaN. * Minus 1 branch for x greater or equal than -1/e and lower than 0, otherwise Double.NaN. */ public static final double lambertW(double x, double branch) { if (Double.isNaN(x)) return Double.NaN; if (Double.isNaN(branch)) return Double.NaN; if (Math.abs(branch) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return lambertW0(x); if (Math.abs(branch + 1) <= BinaryRelations.DEFAULT_COMPARISON_EPSILON) return lambertW1(x); return Double.NaN; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy