@@ -578,3 +578,62 @@ unittest
578578 assert (cast (double ) factorial(1 , 100 ) == 100 );
579579 assert (approxEqual(cast (double ) factorial(100 , 1000 ), iota([100 ], 1000 ).prod! double ));
580580}
581+
582+ /+ +
583+ Quickly computes binomial coefficient using extended
584+ precision floating point type $(MREF mir,bignum,fp).
585+
586+ Params:
587+ DivisionType = the type used for a single division at the final.
588+ By defualt, uses `real` precision. TODO: full `Fp` precision.
589+ n = number elements in the set
590+ k = number elements in the subset
591+ Returns: n choose k
592+ Complexity: O(min(k, n - k))
593+ +/
594+ template binomialCoefficient (DivisionType = void )
595+ if (is (DivisionType == void ) || __traits(isFloating, DivisionType))
596+ {
597+ auto binomialCoefficient
598+ (size_t coefficientSize = 128 , Exp = sizediff_t )
599+ (ulong n, uint k)
600+ if (coefficientSize % (size_t .sizeof * 8 ) == 0 && coefficientSize >= (size_t .sizeof * 8 ))
601+ in (k <= n)
602+ {
603+ import mir.bignum.fp: Fp;
604+
605+ alias R = Fp! (coefficientSize, Exp);
606+
607+ // TODO: For DivisionType == void
608+ // use full precision division for void when Fp division is ready
609+ static if (is (DivisionType == void ))
610+ alias T = real ;
611+ else
612+ alias T = DivisionType;
613+
614+ if (k > n - k)
615+ k = cast (uint )(n - k);
616+
617+ auto a = factorial! (coefficientSize, Exp)(k, n - k + 1 );
618+ auto b = factorial! (coefficientSize, Exp)(k);
619+ auto exponent = a.exponent - b.exponent;
620+ a.exponent = b.exponent = - coefficientSize;
621+ auto ret = R(cast (T) a / cast (T) b);
622+ ret.exponent += exponent;
623+ return ret;
624+ }
625+ }
626+
627+ // /
628+ version (mir_test)
629+ @safe pure nothrow @nogc
630+ unittest
631+ {
632+ import mir.bignum.fp: Fp;
633+ import mir.math.common: approxEqual;
634+
635+ static assert (is (typeof (binomialCoefficient! double (30 , 18 )) == Fp! 128 ), typeof (binomialCoefficient! double (30 , 18 )).stringof);
636+ static assert (cast (double ) binomialCoefficient(30 , 18 ) == 86493225 );
637+
638+ assert (approxEqual(cast (double ) binomialCoefficient(100 , 40 ), 1.374623414580281150126736972e+28 ));
639+ }
0 commit comments