private XInt Product(int n)
 {
     var m = n / 2;
     if (m == 0) return this.currentN += 2;
     if (n == 2) return (this.currentN += 2) * (this.currentN += 2);
     return this.Product(n - m) * this.Product(m);
 }
        private XInt Swing(long n)
        {
            var s = this.gN - 1 + ((n - this.gN + 1) % 4);
            bool oddN = (this.gN & 1) != 1;

            for (; this.gN <= s; this.gN++)
            {
                if (oddN = !oddN) this.f *= this.gN;
                else this.f = (this.f * 4) / this.gN;
            }

            if (oddN) for (; this.gN <= n; this.gN += 4)
            {
                    var m = ((this.gN + 1) * (this.gN + 3)) << 1;
                    var d = (this.gN * (this.gN + 2)) >> 3;

                    this.f = (this.f * m) / d;
            }
            else for (; this.gN <= n; this.gN += 4)
            {
                    var m = (this.gN * (this.gN + 2)) << 1;
                    var d = ((this.gN + 1) * (this.gN + 3)) >> 3;

                    this.f = (this.f * m) / d;
             }

            return this.f;
        }
        static XInt Swing(int n)
        {
            var w = XInt.One;

            if (n > 1)
            {
                n = n + 2;
                var s = new XInt[n + 1];

                s[0] = s[1] = XInt.Zero;
                s[2] = w;

                for (var m = 3; m <= n; m++)
                {
                    s[m] = s[m - 2];
                    for (var k = m; k >= 2; k--)
                    {
                        s[k] += s[k - 2];
                        if ((k & 1) == 1) // if k is odd
                        {
                            s[k] += s[k - 1];
                        }
                    }
                }
                w = s[n];
            }
            return w;
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                          this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            if (n < 2)
            {
                return XInt.One;
            }

            long h = n / 2;
            var q = h * h;
            var r = (n & 1) == 1 ? 2 * q * n : 2 * q;
            var f = new XInt(r);

            for (var d = 1; d < n - 2; d += 2)
            {
                f *= q -= d;
            }

            return f;
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                    this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            int r = n % 8, s = n / 8 + 1;
            var rf = (long)(new int[] { 1, 1, 2, 6, 24, 120, 720, 5040 })[r];

            if (n < 8)
            {
                return rf;
            }

            var factors = new XInt[s];
            factors[s - 1] = rf;

            Parallel.For(0, s - 1, i =>
            {
                factors[i] = CPpoly(i * 8 + r + 1);
            });

            return XMath.Product(factors, 0, s);
        }
        // assuming n = 2k
        private XInt MiddleBinomial(int n)
        {
            if (n < 50) return new XInt(Binomial[n / 2]);

            int k = n / 2, pc = 0, pp = 0;
            var rootN = XMath.FloorSqrt(n);

            var bigPrimes = this.sieve.GetPrimorial(k + 1, n);
            var smallPrimes = this.sieve.GetPrimorial(k / 2 + 1, n / 3);

            var primes = this.sieve.GetPrimeCollection(rootN + 1, n / 5);
            var primeList = new int[primes.NumberOfPrimes];

            foreach (var prime in primes.Where(prime => (n / prime & 1) == 1))
            {
                primeList[pc++] = prime;
            }
            var prodPrimes = XMath.Product(primeList, 0, pc);

            primes = this.sieve.GetPrimeCollection(1, rootN);
            var primePowers = new XInt[primes.NumberOfPrimes];

            var exp = 0;
            foreach (var prime in primes.Where(prime => (exp = ExpSum(prime, n)) > 0))
            {
                primePowers[pp++] = XInt.Pow(prime, exp);
            }

            var powerPrimes = XMath.Product(primePowers, 0, pp);

            return bigPrimes * smallPrimes * prodPrimes * powerPrimes;
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                    this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            if (n < 2) return XInt.One;

            var p = XInt.One;
            var r = XInt.One;
            this.currentN = XInt.One;

            int h = 0, shift = 0, high = 1;
            var log2N = XMath.FloorLog2(n);

            while (h != n)
            {
                shift += h;
                h = n >> log2N--;
                var len = high;
                high = (h - 1) | 1;
                len = (high - len) / 2;

                if (len > 0)
                {
                    p *= this.Product(len);
                    r *= p;
                }
            }

            return r << shift;
        }
        static XInt OddSwing(int n, XInt oddFactNdiv4)
        {
            var len = (n - 1) / 4;
            if ((n % 4) != 2) len++;

            //-- if type(n, odd) then high = n else high = n-1.
            var high = n - ((n + 1) & 1);

            return Product(high, len) / oddFactNdiv4;
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new ArithmeticException(
                  this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            this.oddFactNdiv4 = this.oddFactNdiv2 = XInt.One;
            return this.OddFactorial(n) << (n - XMath.BitCount(n));
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                    this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            this.gN = 1;
            this.f = XInt.One;
            return this.RecFactorial(n);
        }
        public XInt Factorial(int n)
        {
            if (n < 20) { return XMath.Factorial(n); }

            var log2N = XMath.FloorLog2(n);
            var j = log2N;
            var hN = n;

            this.primeList = new int[log2N][];
            this.listLength = new int[log2N];
            this.bound = new int[log2N];
            this.tower = new int[log2N + 1];

            while (true)
            {
                this.tower[j] = hN;
                if (hN == 1) break;
                this.bound[--j] = hN / 3;
                var pLen = hN < 4 ? 6 : (int)(2.0 * (XMath.FloorSqrt(hN)
                         + (double) hN / (XMath.Log2(hN) - 1)));
                this.primeList[j] = new int[pLen];
                hN >>= 1;
            }

            this.tower[0] = 2;
            this.PrimeFactors(n);

            var init = this.listLength[0] == 0 ? 1 : 3;
            var oddFactorial = new XInt(init);

            var results = new XInt[log2N];
            Parallel.For(1, log2N, i =>
                results[i] = XMath.Product(this.primeList[i], 0, this.listLength[i])
            );

            for (var i = 1; i < log2N; i++)
            {
                oddFactorial = XInt.Pow(oddFactorial, 2);
                oddFactorial = oddFactorial * results[i];
            }

            return oddFactorial << (n - XMath.BitCount(n));
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                    this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            if (n < 7)
            {
                return (XInt)(new int[] { 1, 1, 2, 6, 24, 120, 720 })[n];
            }

            int i = 1, loop = n / 2;
            var f = new XInt[loop + (n & 1)];

            f[0] = loop;
            if ((n & 1) == 1)
            {
                f[loop] = n;
            }

            XInt s = loop, t;

            for (var inc = loop - 1; inc > 0; inc--)
            {
                s += inc;
                t = s;

                while (t.IsEven())
                {
                    loop++;
                    t /= 2;
                }

                f[i++] = t;
            }

            return SplitProduct(f) << loop;
        }
        private static XInt Swing(int n)
        {
            int z;

            switch (n % 4)
            {
                case 1: z = n / 2 + 1; break;
                case 2: z = 2; break;
                case 3: z = 2 * (n / 2 + 2); break;
                default: z = 1; break;
            }

            var b = new XInt(z);
            z = 2 * (n - ((n + 1) & 1));

            for (var i = 1; i <= n / 4; i++, z -= 4)
            {
                b = (b * z) / i;
            }

            return b;
        }
        public XInt Factorial(int n)
        {
            if (n < 0)
            {
                throw new System.ArgumentOutOfRangeException(
                    this.Name + ": " + nameof(n) + " >= 0 required, but was " + n);
            }

            var s = new XInt[n + 1];
            s[0] = XInt.One;

            for (var m = 1; m <= n; m++)
            {
                s[m] = XInt.Zero;
                for (var k = m; k >= 1; k--)
                {
                    for (var i = 1; i <= k; i++)
                    {
                        s[i] += s[i - 1];
                    }
                }
            }
            return s[n];
        }
        private static XInt CPpoly(XInt x)
        {
            // Implemented after Fredrik Johansson's arb-function
            // rising_fmprb_ui_bsplit_eight (please speak aloud).
            // x(x+1)...(x+7) = (28+98x+63x^2+14x^3+x^4)^2-16(7+2x)^2

            // t = x^2, v = x^3, u = x^4
            var t = x * x;
            var v = x * t;
            var u = t * t;

            // u = (28 + 98x + 63x^2 + 14x^3 + x^4)^2
            u += v * 14u;
            u += t * 63u;
            u += x * 98u;
            u += 28;
            u *= u;

            // 16 (7+2x)^2 = 784 + 448x + 64x^2
            u -= 784u;
            u -= x * 448u;
            u -= t << 6;
            return u ;
        }
            readonly XInt num; // Numerator

            #endregion Fields

            #region Constructors

            public Rational(long _num, long _den)
            {
                long g = Gcd(_num, _den);
                this.num = new XInt(_num / g);
                this.den = new XInt(_den / g);
            }
 public Rational(XInt _num, XInt _den)
 {
     //  If (and only if) the arithmetic supports a
     //  *real* fast Gcd this would lead to a speed up:
     this.num = _num;
     this.den = _den;
 }
        private XInt OddFactorial(int n)
        {
            if (n < Smallfact)
            {
                return SmallOddFactorial[n];
            }

            var sqrOddFact = this.OddFactorial(n / 2);
            XInt oddFact;

            if (n < Smallswing)
            {
                oddFact = XInt.Pow(sqrOddFact, 2) * SmallOddSwing[n];
            }
            else
            {
                var ndiv4 = n / 4;
                var oddFactNd4 = ndiv4 < Smallfact ? SmallOddFactorial[ndiv4] : this.oddFactNdiv4;
                this.oddSwingTask = Task.Factory.StartNew<XInt>( () => OddSwing(n, oddFactNd4));

                sqrOddFact = XInt.Pow(sqrOddFact, 2);
                oddFact = sqrOddFact * this.oddSwingTask.Result;
            }

            this.oddFactNdiv4 = this.oddFactNdiv2;
            this.oddFactNdiv2 = oddFact;
            return oddFact;
        }
 public Rational(XInt _num, XInt _den)
 {
     // If (and only if) the arithmetic supports a
     // *real* fast Gcd this would lead to a speed up:
     // XInt cd = XInt.Gcd(_num, _den);
     // num = new XInt(_num / cd);
     // den = new XInt(_den / cd);
     this.num = _num;
     this.den = _den;
 }
            readonly XInt num; // Numerator

            #endregion Fields

            #region Constructors

            public Rational(long _num, long _den)
            {
                long cd = Gcd(_den, _num);
                this.num = new XInt(_num / cd);
                this.den = new XInt(_den / cd);
            }
        static public XInt Product(long[] seq, int start, int len)
        {
            if (len <= ThresholdProductSerial)
            {
                var rprod = new XInt(seq[start]);

                for (var i = start + 1; i < start + len; i++)
                {
                    rprod *= seq[i];
                }
                return rprod;
            }
            else
            {
                var halfLen = len / 2;
                var rprod = XInt.Zero;
                var lprod = XInt.Zero;

                Parallel.Invoke(
                    () => { rprod = Product(seq, start, halfLen); },
                    () => { lprod = Product(seq, start + halfLen, len - halfLen); }
                );
                return lprod * rprod;
            }
        }
            public readonly XInt Value; // class { get; set; }

            #endregion Fields

            #region Constructors

            public CachedPrimorial(int highBound, XInt val)
            {
                this.High = highBound;
                this.Value = val;
            }