//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2010 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using BigInteger = System.Numerics.BigInteger;
// ReSharper disable CheckNamespace
namespace IStation.Numerics
// ReSharper restore CheckNamespace
{
public partial class SpecialFunctions
{
const int FactorialMaxArgument = 170;
static readonly double[] _factorialCache = new double[171]
{
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 1.21645100408832E+17, 2.43290200817664E+18, 5.109094217170944E+19, 1.1240007277776077E+21, 2.5852016738884978E+22, 6.2044840173323941E+23, 1.5511210043330986E+25, 4.0329146112660565E+26, 1.0888869450418352E+28, 3.0488834461171384E+29, 8.8417619937397008E+30, 2.6525285981219103E+32, 8.2228386541779224E+33, 2.6313083693369352E+35, 8.6833176188118859E+36, 2.9523279903960412E+38, 1.0333147966386144E+40, 3.7199332678990118E+41, 1.3763753091226343E+43, 5.2302261746660104E+44, 2.0397882081197442E+46, 8.1591528324789768E+47, 3.3452526613163803E+49, 1.4050061177528798E+51, 6.0415263063373834E+52, 2.6582715747884485E+54, 1.1962222086548019E+56, 5.5026221598120885E+57, 2.5862324151116818E+59, 1.2413915592536073E+61, 6.0828186403426752E+62, 3.0414093201713376E+64, 1.5511187532873822E+66, 8.0658175170943877E+67, 4.2748832840600255E+69, 2.3084369733924138E+71, 1.2696403353658276E+73, 7.1099858780486348E+74, 4.0526919504877221E+76, 2.3505613312828789E+78, 1.3868311854568986E+80, 8.3209871127413916E+81, 5.0758021387722484E+83, 3.1469973260387939E+85, 1.9826083154044401E+87, 1.2688693218588417E+89, 8.2476505920824715E+90, 5.4434493907744307E+92, 3.6471110918188683E+94, 2.4800355424368305E+96, 1.711224524281413E+98, 1.197857166996989E+100, 8.5047858856786218E+101, 6.1234458376886077E+103, 4.4701154615126834E+105, 3.3078854415193856E+107, 2.4809140811395391E+109, 1.8854947016660498E+111, 1.4518309202828584E+113, 1.1324281178206295E+115, 8.9461821307829729E+116, 7.1569457046263779E+118, 5.7971260207473655E+120, 4.7536433370128398E+122, 3.9455239697206569E+124, 3.314240134565352E+126, 2.8171041143805494E+128, 2.4227095383672724E+130, 2.1077572983795269E+132, 1.8548264225739836E+134, 1.6507955160908452E+136, 1.4857159644817607E+138, 1.3520015276784023E+140, 1.24384140546413E+142, 1.1567725070816409E+144, 1.0873661566567424E+146, 1.0329978488239052E+148, 9.916779348709491E+149, 9.6192759682482062E+151, 9.426890448883242E+153, 9.3326215443944096E+155, 9.3326215443944102E+157, 9.4259477598383536E+159, 9.6144667150351211E+161, 9.9029007164861754E+163, 1.0299016745145622E+166, 1.0813967582402903E+168, 1.1462805637347078E+170, 1.2265202031961373E+172, 1.3246418194518284E+174, 1.4438595832024928E+176, 1.5882455415227421E+178, 1.7629525510902437E+180, 1.9745068572210728E+182, 2.2311927486598123E+184, 2.5435597334721862E+186, 2.9250936934930141E+188, 3.3931086844518965E+190, 3.969937160808719E+192, 4.6845258497542883E+194, 5.5745857612076033E+196, 6.6895029134491239E+198, 8.09429852527344E+200, 9.8750442008335976E+202, 1.2146304367025325E+205, 1.5061417415111404E+207, 1.8826771768889254E+209, 2.3721732428800459E+211, 3.0126600184576582E+213, 3.8562048236258025E+215, 4.9745042224772855E+217, 6.4668554892204716E+219, 8.4715806908788174E+221, 1.1182486511960039E+224, 1.4872707060906852E+226, 1.9929427461615181E+228, 2.6904727073180495E+230, 3.6590428819525472E+232, 5.0128887482749898E+234, 6.9177864726194859E+236, 9.6157231969410859E+238, 1.346201247571752E+241, 1.8981437590761701E+243, 2.6953641378881614E+245, 3.8543707171800706E+247, 5.5502938327393013E+249, 8.0479260574719866E+251, 1.1749972043909099E+254, 1.7272458904546376E+256, 2.5563239178728637E+258, 3.8089226376305671E+260, 5.7133839564458505E+262, 8.6272097742332346E+264, 1.3113358856834518E+267, 2.0063439050956811E+269, 3.0897696138473489E+271, 4.7891429014633912E+273, 7.4710629262828905E+275, 1.1729568794264138E+278, 1.8532718694937338E+280, 2.9467022724950369E+282, 4.714723635992059E+284, 7.5907050539472148E+286, 1.2296942187394488E+289, 2.0044015765453015E+291, 3.2872185855342945E+293, 5.423910666131586E+295, 9.0036917057784329E+297, 1.5036165148649983E+300, 2.5260757449731969E+302, 4.2690680090047027E+304, 7.257415615307994E+306
};
///
/// Computes the factorial function x -> x! of an integer number > 0. The function can represent all number up
/// to 22! exactly, all numbers up to 170! using a double representation. All larger values will overflow.
///
/// A value value! for value > 0
///
/// If you need to multiply or divide various such factorials, consider using the logarithmic version
/// instead so you can add instead of multiply and subtract instead of divide, and
/// then exponentiate the result using . This will also circumvent the problem that
/// factorials become very large even for small parameters.
///
///
public static double Factorial(int x)
{
if (x < 0)
{
throw new ArgumentOutOfRangeException(nameof(x), "Value must be positive (and not zero).");
}
if (x < _factorialCache.Length)
{
return _factorialCache[x];
}
return double.PositiveInfinity;
}
///
/// Computes the factorial of an integer.
///
public static BigInteger Factorial(BigInteger x)
{
if (x < 0)
{
throw new ArgumentOutOfRangeException(nameof(x), "Value must be positive (and not zero).");
}
if (x == 0)
{
return BigInteger.One;
}
BigInteger r = x;
while (--x > 1)
{
r *= x;
}
return r;
}
///
/// Computes the logarithmic factorial function x -> ln(x!) of an integer number > 0.
///
/// A value value! for value > 0
public static double FactorialLn(int x)
{
if (x < 0)
{
throw new ArgumentOutOfRangeException(nameof(x), "Value must be positive (and not zero).");
}
if (x <= 1)
{
return 0d;
}
if (x < _factorialCache.Length)
{
return Math.Log(_factorialCache[x]);
}
return GammaLn(x + 1.0);
}
///
/// Computes the binomial coefficient: n choose k.
///
/// A nonnegative value n.
/// A nonnegative value h.
/// The binomial coefficient: n choose k.
public static double Binomial(int n, int k)
{
if (k < 0 || n < 0 || k > n)
{
return 0.0;
}
return Math.Floor(0.5 + Math.Exp(FactorialLn(n) - FactorialLn(k) - FactorialLn(n - k)));
}
///
/// Computes the natural logarithm of the binomial coefficient: ln(n choose k).
///
/// A nonnegative value n.
/// A nonnegative value h.
/// The logarithmic binomial coefficient: ln(n choose k).
public static double BinomialLn(int n, int k)
{
if (k < 0 || n < 0 || k > n)
{
return double.NegativeInfinity;
}
return FactorialLn(n) - FactorialLn(k) - FactorialLn(n - k);
}
///
/// Computes the multinomial coefficient: n choose n1, n2, n3, ...
///
/// A nonnegative value n.
/// An array of nonnegative values that sum to .
/// The multinomial coefficient.
/// if is .
/// If or any of the are negative.
/// If the sum of all is not equal to .
public static double Multinomial(int n, int[] ni)
{
if (n < 0)
{
throw new ArgumentException("Value must be positive.", nameof(n));
}
if (ni == null)
{
throw new ArgumentNullException(nameof(ni));
}
int sum = 0;
double ret = FactorialLn(n);
for (int i = 0; i < ni.Length; i++)
{
if (ni[i] < 0)
{
throw new ArgumentException("Value must be positive.", "ni[" + i + "]");
}
ret -= FactorialLn(ni[i]);
sum += ni[i];
}
// Before returning, check that the sum of all elements was equal to n.
if (sum != n)
{
throw new ArgumentException("The chosen parameter set is invalid (probably some value is out of range).", nameof(ni));
}
return Math.Floor(0.5 + Math.Exp(ret));
}
}
}