@@ -511,3 +511,68 @@ unittest
511511 assert (isNaN(sumOfLog2s([- real .infinity])));
512512 assert (sumOfLog2s([ 0.25 , 0.25 , 0.25 , 0.125 ]) == - 9 );
513513}
514+
515+ /+ +
516+ Quickly computes factorial using extended
517+ precision floating point type $(MREF mir,bignum,fp).
518+
519+ Params:
520+ count = number of product members
521+ start = initial member value (optional)
522+ Returns: `(count + start - 1)! / (start - 1)!`
523+ +/
524+ auto factorial
525+ (size_t coefficientSize = 128 , Exp = sizediff_t )
526+ (ulong count, ulong start = 1 )
527+ if (coefficientSize % (size_t .sizeof * 8 ) == 0 && coefficientSize >= (size_t .sizeof * 8 ))
528+ in (start)
529+ {
530+ import mir.utility: _expect;
531+ import mir.bignum.fp: Fp;
532+ alias R = Fp! (coefficientSize, Exp);
533+ R prod = 1LU;
534+ import mir.checkedint: addu, mulu;
535+
536+ if (count)
537+ {
538+ ulong tempProd = start;
539+ while (-- count)
540+ {
541+ bool overflow;
542+ ulong nextTempProd = mulu(tempProd, ++ start, overflow);
543+ if (_expect(! overflow, true ))
544+ {
545+ tempProd = nextTempProd;
546+ continue ;
547+ }
548+ else
549+ {
550+ prod *= R(tempProd);
551+ tempProd = start;
552+ }
553+ }
554+ prod *= R(tempProd);
555+ }
556+
557+ return prod;
558+ }
559+
560+ // /
561+ version (mir_test)
562+ @safe pure nothrow @nogc
563+ unittest
564+ {
565+ import mir.bignum.fp: Fp;
566+ import mir.math.common: approxEqual;
567+ import mir.math.numeric: prod;
568+ import mir.ndslice.topology: iota;
569+
570+ static assert (is (typeof (factorial(33 )) == Fp! 128 ));
571+ static assert (is (typeof (factorial! 256 (33 )) == Fp! 256 ));
572+ static assert (cast (double ) factorial(33 ) == 8.68331761881188649551819440128e+36 );
573+
574+ assert (cast (double ) factorial(0 ) == 1 );
575+ assert (cast (double ) factorial(0 , 100 ) == 1 );
576+ assert (cast (double ) factorial(1 , 100 ) == 100 );
577+ assert (approxEqual(cast (double ) factorial(100 , 1000 ), iota([100 ], 1000 ).prod! double ));
578+ }
0 commit comments