public int Evaluate(UInt128 n)
        {
            this.n = n;
            var sum = 0;

            sqrtn = (long)IntegerMath.FloorSquareRoot(n);
            kmax  = (int)IntegerMath.FloorLog(n, 2);
            imax  = (long)IntegerMath.FloorPower(n, 1, 5) * C1 / C2;
            xmax  = DownToOdd(imax != 0 ? Xi(imax) : sqrtn);
            xmed  = DownToOdd(Math.Min((long)(IntegerMath.FloorPower(n, 2, 7) * C3 / C4), xmax));
            var dmax = (long)IntegerMath.Min(n / IntegerMath.Square((UInt128)xmed) + 1, n);

            mobius   = new MobiusOddRangeAdditive((xmax + 2) | 1, threads);
            divisors = new DivisorOddRangeAdditive((dmax + 2) | 1, threads);
            xi       = new long[imax + 1];
            mx       = new long[imax + 1];

            // Initialize xi.
            for (var i = 1; i <= imax; i++)
            {
                xi[i] = Xi(i);
            }

            values = new sbyte[mobiusBatchSize >> 1];
            m      = new int[mobiusBatchSize >> 1];
            m0     = 0;
            dsums  = new ulong[divisorBatchSize >> 1];
            d1     = d2 = 1;

            // Process small x values.
            for (var x = (long)1; x <= xmed; x += mobiusBatchSize)
            {
                var xfirst = x;
                var xlast  = Math.Min(xmed, xfirst + mobiusBatchSize - 2);
                m0   = mobius.GetValuesAndSums(xfirst, xlast + 2, values, m, m0);
                sum += Pi2Small(xfirst, xlast);
                UpdateMx(xfirst, xlast);
            }

            // Process medium x values.
#if true
            for (var x = xmed + 2; x <= xmax; x += mobiusBatchSize)
            {
                var xfirst = x;
                var xlast  = Math.Min(xmax, xfirst + mobiusBatchSize - 2);
                m0   = mobius.GetValuesAndSums(xfirst, xlast + 2, values, m, m0);
                sum += Pi2Medium(xfirst, xlast);
                UpdateMx(xfirst, xlast);
            }
#else
            for (var x = xmax; x > xmed; x -= mobiusBatchSize)
            {
                var xlast  = x;
                var xfirst = Math.Max(xmed + 2, xlast - mobiusBatchSize + 2);
                m0   = mobius.GetValuesAndSums(xfirst, xlast + 2, values, m, m0);
                sum += Pi2Medium(xfirst, xlast);
                UpdateMx(xfirst, xlast);
            }
#endif

            // Process large x values.
            sum += Pi2Large();

            // Adjust for final parity of F2.
            sum -= IntegerMath.Mertens(kmax);

            // Compute final result.
            sum &= 3;
            Debug.Assert((sum & 1) == 0);
            sum >>= 1;
            return((sum + (n >= 2 ? 1 : 0)) % 2);
        }