public static bool UnitTest()
        {
            FourierPoint a, b;
            Random       random = new Random(1001);

            byte[]     ra = new byte[32];
            BigInteger p  = (BigInteger.One << 128) - (BigInteger.One << 54) + BigInteger.One;

            for (int i = 20 * 1000; --i >= 0;)
            {
                random.NextBytes(ra);
                ra[ra.Length - 1] = 0;
                a = new FourierPoint(BitConverter.ToUInt64(ra, 0), BitConverter.ToUInt64(ra, 8));
                b = new FourierPoint(BitConverter.ToUInt64(ra, 16), BitConverter.ToUInt64(ra, 24));
                BigInteger ba = new BigInteger(ra.Take(16).Concat(Enumerable.Repeat((byte)0, 1)).ToArray());
                BigInteger bb = new BigInteger(ra.Skip(16).Take(16).Concat(Enumerable.Repeat((byte)0, 1)).ToArray());
                FourierPoint.mulmod(ref a, ref b);
                ba = (ba * bb) % p;
                byte[] rbb1 = ba.ToByteArray();
                Array.Resize(ref rbb1, 16);
                byte[] rbb2 = new byte[16];
                BitConverter.GetBytes(a.Low).CopyTo(rbb2, 0);
                BitConverter.GetBytes(a.High).CopyTo(rbb2, 8);
                for (int k = 16; --k >= 0;)
                {
                    if (rbb1[k] != rbb2[k])
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
        private static void ifft(this FourierPoint[] list)
        {
            int n      = list.Length;
            var handle = GCHandle.Alloc(list, GCHandleType.Pinned);

            try
            {
                IntPtr listAddress = handle.AddrOfPinnedObject();
                for (int k = 1; k < n; k <<= 1)
                {
                    FourierPoint.butterflyCore(listAddress, 0, k);
                    FourierPoint wpower = FourierPoint.One;
                    for (int j = k << 1, x = 0; j < n; x++, j += k << 1)
                    {
                        int pos = 0;
                        while ((x & (1 << pos)) != 0)
                        {
                            pos++;
                        }
                        FourierPoint.mulmod(ref wpower, ref FourierPoint.RootsInverters[pos]);
                        FourierPoint.inverseFFTCore(listAddress, j, k, ref wpower);
                    }
                }
            }
            finally
            {
                handle.Free();
            }
        }
        public static bool UnitTest(int seed)
        {
            Random random = new Random(seed);
            int    bits   = 17;
            int    n      = 1 << bits;

            FourierPoint[] a0    = new FourierPoint[n];
            FourierPoint[] a1    = new FourierPoint[n];
            byte[]         bytes = new byte[16];
            for (int i = n; --i >= 0;)
            {
                random.NextBytes(bytes);
                a0[i] = a1[i] = new FourierPoint(BitConverter.ToUInt64(bytes, 0), BitConverter.ToUInt64(bytes, 8));
            }
            a0.FFT(false);
            a0.IFFT(false);
            //var scale = FourierPoint.PowMod(FourierPoint.HalfOne, new FourierPoint((ulong)bits, 0));
            for (int i = n; --i >= 0;)
            {
                //a0[i] *= scale;
                a0[i] >>= bits;
            }
            for (int i = n; --i >= 0;)
            {
                if (a0[i] != a1[i])
                {
                    return(false);
                }
            }
            return(true);
        }
 public static void remodP(ref FourierPoint a)
 {
     if (a.High == ulong.MaxValue && a.Low >= (1023UL << 54) + 1UL)
     {
         a.Low -= (1023UL << 54) + 1UL;
         a.High = 0;
     }
 }
        public override bool Equals(object obj)
        {
            if (!(obj is FourierPoint))
            {
                return(false);
            }
            FourierPoint other = (FourierPoint)obj;

            return(this == other);
        }
        private static void GroupDigits(ulong[] number, int bitsPerDigit, FourierPoint[] result)
        {
            long numberOfDigits = Math.Min(result.Length, (number.LongLength * 64 + bitsPerDigit - 1) / bitsPerDigit);
            long position       = numberOfDigits * bitsPerDigit;

            for (long i = numberOfDigits; --i >= 0;)
            {
                position -= bitsPerDigit;
                result[i] = new FourierPoint(ReadBits(number, position, bitsPerDigit), 0UL);
            }
        }
        public static void Multiply(ulong[] input1, ulong[] input2, ulong[] result, int n)
        {
            // AsmX64Operations.FastestMultiplication(input1, input2, result, n);
            // return;

            int fftBitsPerDigit, fftLog2Size;

            GetFFTParameters(n, out fftBitsPerDigit, out fftLog2Size);
            long fftN = 1L << fftLog2Size;

            //double directComputations = (double)n * n;
            //double karatsubaComputations = 4.7 * Math.Pow(n, LOG2_3);
            //double fftComputations = 7.2 * (3.0 * fftLog2Size + 4.0) * fftN;

            FourierPoint[] number1 = new FourierPoint[fftN];
            GroupDigits(input1, fftBitsPerDigit, number1);
            FourierPoint[] number2 = new FourierPoint[fftN];
            GroupDigits(input2, fftBitsPerDigit, number2);

            //FourierPoint scale = FourierPoint.PowMod(FourierPoint.HalfOne, new FourierPoint((ulong)fftLog2Size, 0UL));
            number1.FFT(false);
            number2.FFT(false);
            for (int i = number1.Length; --i >= 0;)
            {
                FourierPoint point = number1[i];
                FourierPoint.mulmod(ref point, ref number2[i]);
                //FourierPoint.mulmod(ref point, ref scale);
                point    >>= fftLog2Size;
                number1[i] = point;
            }
            number1.IFFT(false);

            var number1Handle = GCHandle.Alloc(number1, GCHandleType.Pinned);

            try
            {
                FourierPoint.PropagateCarries(number1Handle.AddrOfPinnedObject(), fftBitsPerDigit, number1.Length);
            }
            finally
            {
                number1Handle.Free();
            }

            UngroupDigits(number1, fftBitsPerDigit, result);
        }
        static FourierPoint()
        {
            FourierPoint p = PrimeModulo;

            p.Low -= 1;
            FourierPoint halfp = new FourierPoint((p.Low >> 1) | (p.High << 63), p.High >> 1);
            var          fp1   = PowMod(Generator, p);
            var          fp2   = PowMod(Generator, halfp);

            remodP(ref fp1);
            remodP(ref fp2);
            var fp3 = PowMod(Root, new FourierPoint(1UL << 53, 0));

            if (fp1 != One || fp2 != MinusOne || fp3 != MinusOne)
            {
                throw new InvalidOperationException("Generator does not pass unit test.");
            }
            Roots = new FourierPoint[54];
            FourierPoint[] inverters = new FourierPoint[Roots.Length];
            Roots[Roots.Length - 1]     = Root;
            inverters[Roots.Length - 1] = FourierPoint.PowMod(Root, new FourierPoint(PrimeModulo.Low - 2, PrimeModulo.High));
            for (int i = Roots.Length - 1; --i >= 0;)
            {
                Roots[i] = Roots[i + 1] * Roots[i + 1];
                remodP(ref Roots[i]);
                inverters[i] = inverters[i + 1] * inverters[i + 1];
                remodP(ref inverters[i]);
            }
            RootsMultipliers = new FourierPoint[Roots.Length - 1];
            RootsInverters   = new FourierPoint[Roots.Length - 1];
            for (int i = 0; i < RootsMultipliers.Length; i++)
            {
                RootsMultipliers[i] = PrimeModulo - Roots[i] * Roots[i + 1];
                RootsInverters[i]   = PrimeModulo - inverters[i] * inverters[i + 1];
            }
        }
 public static extern void PowMod(ref FourierPoint a, ref FourierPoint power);
 public static extern void inverseFFTCore(IntPtr number, int startIndex, int n, ref FourierPoint wpower);
 public static extern void submod(ref FourierPoint a, ref FourierPoint b);
 public static void ShiftRightDirect(ref FourierPoint x, int shift)
 {
     x = new FourierPoint((x.Low >> shift) | (x.High << (64 - shift)), x.High >> shift);
 }
 public static extern void butterfly(ref FourierPoint a, ref FourierPoint b);
 public static FourierPoint PowMod(FourierPoint a, FourierPoint power)
 {
     PowMod(ref a, ref power);
     return(a);
 }