public Int128 Evaluate(UInt128 n)
        {
            if (n == 0)
                return 0;

            this.n = n;
            u = (long)IntegerMath.Max(IntegerMath.FloorPower(n, 2, 3) * C1 / C2, IntegerMath.CeilingSquareRoot(n));

            imax = (int)(n / (ulong)u);
            mobius = new MobiusRangeAdditive(u + 1, threads);
            var batchSize = Math.Min(u, maximumBatchSize);
            mu = new sbyte[maximumSmallBatchSize];
            m = new int[batchSize];
            mx = new Int128[imax + 1];
            r = new int[imax + 1];
            var lmax = 0;
            for (var i = 1; i <= imax; i += 2)
            {
                if (wheelInclude[(i % wheelSize) >> 1])
                    r[lmax++] = i;
            }
            Array.Resize(ref r, lmax);

            niLarge = new UInt128[imax + 1];
            niSmall = new long[imax + 1];
            var buckets = Math.Max(1, threads);
            var costs = new double[buckets];
            var bucketListsLarge = Enumerable.Range(0, buckets).Select(i => new List<int>()).ToArray();
            var bucketListsSmall = Enumerable.Range(0, buckets).Select(i => new List<int>()).ToArray();
            for (var l = 0; l < lmax; l++)
            {
                var i = r[l];
                var ni = n / (uint)i;
                var large = ni > largeLimit;
                var cost = Math.Sqrt((double)n / i) * (large ? C7 : 1);
                var addto = 0;
                var mincost = costs[0];
                for (var bucket = 0; bucket < buckets; bucket++)
                {
                    if (costs[bucket] < mincost)
                    {
                        mincost = costs[bucket];
                        addto = bucket;
                    }
                }
                niLarge[i] = ni;
                if (large)
                    bucketListsLarge[addto].Add(i);
                else
                {
                    niSmall[i] = (long)ni;
                    bucketListsSmall[addto].Add(i);
                }
                costs[addto] += cost;
            }
            bucketsLarge = bucketListsLarge.Select(bucket => bucket.ToArray()).ToArray();
            bucketsSmall = bucketListsSmall.Select(bucket => bucket.ToArray()).ToArray();

            var m0 = 0;
            var xmed = Math.Min((long)IntegerMath.FloorRoot(n, 2) * C5 / C6, u);
            for (var x = (long)1; x <= xmed; x += maximumSmallBatchSize)
            {
                var xstart = x;
                var xend = Math.Min(xstart + maximumSmallBatchSize - 1, xmed);
                m0 = mobius.GetValuesAndSums(xstart, xend + 1, mu, m, m0);
                ProcessBatch(xstart, xend);
            }
            for (var x = xmed + 1; x <= u; x += maximumBatchSize)
            {
                var xstart = x;
                var xend = Math.Min(xstart + maximumBatchSize - 1, u);
                m0 = mobius.GetSums(xstart, xend + 1, m, m0);
                ProcessBatch(xstart, xend);
            }
            ComputeMx();
            return mx[1];
        }
        public Int128 Evaluate(UInt128 n)
        {
            if (n == 0)
            {
                return(0);
            }

            this.n = n;
            u      = (long)IntegerMath.Max(IntegerMath.FloorPower(n, 2, 3) * C1 / C2, IntegerMath.CeilingSquareRoot(n));

            imax   = (int)(n / (ulong)u);
            mobius = new MobiusRangeAdditive(u + 1, threads);
            var batchSize = Math.Min(u, maximumBatchSize);

            mu = new sbyte[maximumSmallBatchSize];
            m  = new int[batchSize];
            mx = new Int128[imax + 1];
            r  = new int[imax + 1];
            var lmax = 0;

            for (var i = 1; i <= imax; i += 2)
            {
                if (wheelInclude[(i % wheelSize) >> 1])
                {
                    r[lmax++] = i;
                }
            }
            Array.Resize(ref r, lmax);

            niLarge = new UInt128[imax + 1];
            niSmall = new long[imax + 1];
            var buckets          = Math.Max(1, threads);
            var costs            = new double[buckets];
            var bucketListsLarge = Enumerable.Range(0, buckets).Select(i => new List <int>()).ToArray();
            var bucketListsSmall = Enumerable.Range(0, buckets).Select(i => new List <int>()).ToArray();

            for (var l = 0; l < lmax; l++)
            {
                var i       = r[l];
                var ni      = n / (uint)i;
                var large   = ni > largeLimit;
                var cost    = Math.Sqrt((double)n / i) * (large ? C7 : 1);
                var addto   = 0;
                var mincost = costs[0];
                for (var bucket = 0; bucket < buckets; bucket++)
                {
                    if (costs[bucket] < mincost)
                    {
                        mincost = costs[bucket];
                        addto   = bucket;
                    }
                }
                niLarge[i] = ni;
                if (large)
                {
                    bucketListsLarge[addto].Add(i);
                }
                else
                {
                    niSmall[i] = (long)ni;
                    bucketListsSmall[addto].Add(i);
                }
                costs[addto] += cost;
            }
            bucketsLarge = bucketListsLarge.Select(bucket => bucket.ToArray()).ToArray();
            bucketsSmall = bucketListsSmall.Select(bucket => bucket.ToArray()).ToArray();

            var m0   = 0;
            var xmed = Math.Min((long)IntegerMath.FloorRoot(n, 2) * C5 / C6, u);

            for (var x = (long)1; x <= xmed; x += maximumSmallBatchSize)
            {
                var xstart = x;
                var xend   = Math.Min(xstart + maximumSmallBatchSize - 1, xmed);
                m0 = mobius.GetValuesAndSums(xstart, xend + 1, mu, m, m0);
                ProcessBatch(xstart, xend);
            }
            for (var x = xmed + 1; x <= u; x += maximumBatchSize)
            {
                var xstart = x;
                var xend   = Math.Min(xstart + maximumBatchSize - 1, u);
                m0 = mobius.GetSums(xstart, xend + 1, m, m0);
                ProcessBatch(xstart, xend);
            }
            ComputeMx();
            return(mx[1]);
        }