csharp/AndreyAkinshin/perfolizer/src/Perfolizer/Perfolizer/Mathematics/Selectors/Rqq.cs

Rqq.cs
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Perfolizer.Mathematics.Common;

namespace Perfolizer.Mathematics.Selectors
{
    /// 
    /// Range Quantile Queries
    /// 
    /// Based on https://arxiv.org/pdf/0903.4726.pdf
    /// 
    /// 
    public clast Rqq
    {
        private enum NodeKind
        {
            Parent,
            Leaf,
            Fake
        }

        private enum BinaryFlag
        {
            LessThanMedian = 0,
            MoreThanMedian = 1,
            NotDefined = 2
        }

        private readonly double[] values;
        private readonly int[] rangeLeft, rangeRight;
        private readonly BinaryFlag[] b;
        private readonly int[] bSum;
        private readonly NodeKind[] kinds;
        private readonly int n, nodeCount, usedValues;

        public Rqq([NotNull] double[] data)
        {
            if (data == null)
                throw new ArgumentNullException(nameof(data));

            n = data.Length;
            int logN = (int) Math.Ceiling(Math.Log(n));
            int valuesN = n * logN * 2; // TODO: Optimize
            values = new double[valuesN];
            b = new BinaryFlag[valuesN];
            bSum = new int[valuesN];

            // TODO: Optimize using bit hacks
            nodeCount = 1;
            while (nodeCount < 2 * n)
                nodeCount *= 2;
            nodeCount -= 1;

            rangeLeft = new int[nodeCount];
            rangeRight = new int[nodeCount];
            for (int i = 0; i < n; i++)
                values[i] = data[i];

            kinds = new NodeKind[nodeCount];
            for (int i = 0; i < nodeCount; i++)
                kinds[i] = NodeKind.Fake;

            rangeLeft[0] = 0;
            rangeRight[0] = n - 1;
            kinds[0] = NodeKind.Leaf;
            int valueCount = n;
            var selector = new QuickSelectAdaptive();
            for (int node = 0; node < nodeCount; node++)
            {
                if (kinds[node] == NodeKind.Fake)
                    continue;

                int leftIndex = rangeLeft[node];
                int rightIndex = rangeRight[node];
                if (leftIndex == rightIndex)
                {
                    kinds[node] = NodeKind.Leaf;
                    b[leftIndex] = BinaryFlag.NotDefined;
                    continue;
                }

                double median = selector.Select(values, (rightIndex - leftIndex) / 2, leftIndex, rightIndex);

                int child1 = node * 2 + 1;
                int child2 = node * 2 + 2;
                kinds[node] = NodeKind.Parent;
                kinds[child1] = NodeKind.Leaf;
                kinds[child2] = NodeKind.Leaf;

                int child1Size = (rightIndex - leftIndex + 2) / 2;
                int child2Size = rightIndex - leftIndex + 1 - child1Size;
                rangeLeft[child1] = valueCount;
                rangeRight[child1] = valueCount + child1Size - 1;
                rangeLeft[child2] = valueCount + child1Size;
                rangeRight[child2] = valueCount + child1Size + child2Size - 1;
                valueCount += child1Size + child2Size;
                int child1Counter = 0;
                int child2Counter = 0;

                // Preprocessing values that are less than median (filling b[i])
                for (int i = leftIndex; i = n)
                throw new ArgumentException(nameof(l), $"l ({l}) should be between 0 and n-1 ({n - 1})");
            if (r < 0 || r >= n)
                throw new ArgumentException(nameof(r), $"r ({r}) should be between 0 and n-1 ({n - 1})");

            int node = 0;
            while (true)
            {
                int shift = rangeLeft[node];
                int GetOneCount(int x, int y) => y >= 0 ? bSum[shift + y] - (x == 0 ? 0 : bSum[shift + x - 1]) : 0;
                int GetZeroCount(int x, int y) => y >= x ? y - x + 1 - GetOneCount(x, y) : 0;
                switch (kinds[node])
                {
                    case NodeKind.Parent:
                    {
                        int zkl = GetZeroCount(l, r);
                        int nextNode, nextL, nextR, nextK;
                        if (zkl > k)
                        {
                            nextNode = node * 2 + 1; // Left child
                            nextL = GetZeroCount(0, l - 1);
                            nextR = GetZeroCount(0, r) - 1;
                            nextK = k;
                        }
                        else
                        {
                            nextNode = node * 2 + 2; // Right child
                            nextL = GetOneCount(0, l - 1);
                            nextR = GetOneCount(0, r) - 1;
                            nextK = k - zkl;
                        }

                        node = nextNode;
                        l = nextL;
                        r = nextR;
                        k = nextK;
                    }
                        break;
                    case NodeKind.Leaf:
                        return values[rangeLeft[node]];
                    case NodeKind.Fake:
                        throw new InvalidOperationException();
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        [NotNull]
        public string DumpTreeAscii(bool details = false)
        {
            using (var memoryStream = new MemoryStream())
            using (var sw = new StreamWriter(memoryStream))
            {
                DumpTreeAscii(sw, details);
                return Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int) memoryStream.Length).TrimEnd();
            }
        }

        public void DumpTreeAscii([NotNull] StreamWriter writer, bool details = false)
        {
            var valuesStr = values.Select(v => v.ToString(CultureInfo.InvariantCulture)).ToArray(); // TODO: precision

            // Calculate width for each node
            var width = new int[nodeCount];
            for (int node = 0; node < nodeCount; node++)
            {
                width[node] += rangeRight[node] - rangeLeft[node] + 2; // result += 
                if (kinds[node] != NodeKind.Fake)
                    for (int i = rangeLeft[node]; i = 0; node--)
            {
                if (kinds[node] == NodeKind.Parent)
                {
                    int child1 = node * 2 + 1;
                    int child2 = node * 2 + 2;
                    int totalWidth1 = paddingLeft[child1] + width[child1] + paddingRight[child1];
                    int totalWidth2 = paddingLeft[child2] + width[child2] + paddingRight[child2];
                    int childrenWidth = totalWidth1 + 1 + totalWidth2;

                    int padding = Math.Max(0, childrenWidth - width[node]);
                    paddingLeft[node] = paddingLeft[child1] + width[child1] +
                                        (paddingRight[child1] + 1 + paddingLeft[child2]) / 2 -
                                        width[node] / 2;
                    paddingRight[node] = padding - paddingLeft[node];

                    connectorDownLeft[node] = 1;
                    connectorDownRight[node] = width[node] - 2;
                    connectorUp[child1] = paddingLeft[node] + connectorDownLeft[node] - paddingLeft[child1];
                    connectorUp[child2] = paddingLeft[node] + connectorDownRight[node] -
                                          (totalWidth1 + 1) - paddingLeft[child2];
                }
            }

            int layer = -1;
            while (true)
            {
                layer++;
                int node2 = (1