/// <summary>
        /// Deserializes to an array of nullable <see lang="double"/> from the specified reader.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns>An array of nullable <see lang="double"/>.</returns>
        public static double?[] DeserializeToNullableDoubles(BinaryReader reader)
        {
            // version not in use yet.
            reader.ReadByte();
            var numOfItems = (int)SerializationUtils.ReadUInt32FromBase128(reader);

            if (numOfItems == 0)
            {
                return(EmptyNullableDoubleArray);
            }

            var result = new double?[numOfItems];

            var bitBinaryReader = new BitBinaryReader(reader);
            var previousState   = new DoubleValueState(0, -1, -1);

            for (int i = 0; i < numOfItems; ++i)
            {
                var newState = ReadDouble(bitBinaryReader, previousState);
                result[i]     = double.IsNaN(newState.Value) ? (double?)null : newState.Value;
                previousState = newState;
            }

            return(result);
        }
        public void VerifyEncoding_largecorpus()
        {
            string input  = TestResources.RFC5_Text;
            int    len    = input.Length;
            var    data   = new MemoryStream();
            var    writer = new BitBinaryWriter(data);
            var    reader = new BitBinaryReader(data);

            var compressor = new DynamicHuffman <char>(OriginalNYT);

            for (int i = 0; i < len; ++i)
            {
                compressor.WriteCode(input[i], writer.Write, writer.Write);
            }
            writer.Flush();
            data.Position = 0;

            var decompressor = new DynamicHuffman <char>(OriginalNYT);

            for (int i = 0; i < len; ++i)
            {
                Assert.AreEqual(input[i], decompressor.GetSymbol(reader.ReadBoolean, reader.ReadChar));
            }
            Assert.AreEqual(data.Position, data.Length);
        }
        public void BaseStream_NotBitStream()
        {
            var data   = new byte[] { 0x23, 0x72, 0x64, 0xe3, 0x11, 0xd1, 0x4a, 0x9c, 0xb6, 0x02 };
            var stream = new MemoryStream(data);
            var reader = new BitBinaryReader(stream);

            Assert.AreNotEqual(stream, reader.BaseStream);
        }
        private static unsafe void DeserializeValues(BinaryReader reader, double *values, int count)
        {
            var bitBinaryReader = new BitBinaryReader(reader);
            var previousState   = new DoubleValueState(0, -1, -1);

            for (int i = 0; i < count; ++i)
            {
                var newState = ReadDouble(bitBinaryReader, previousState);
                values[i]     = newState.Value;
                previousState = newState;
            }
        }
        public void BinaryWriterCompatible_WithoutBits()
        {
            var stream = new MemoryStream();
            var writer = new BinaryWriter(stream);
            var reader = new BitBinaryReader(stream);

            foreach (TestValue value in _testValues)
            {
                value.Write(writer);
            }

            writer.Flush();
            stream.Position = 0;

            foreach (TestValue value in _testValues)
            {
                value.Read(reader);
            }
        }
        private static DoubleValueState ReadDouble(BitBinaryReader reader, DoubleValueState state)
        {
            var firstBit = reader.ReadBit();

            if (!firstBit)
            {
                return(state);
            }

            var  secondBit = reader.ReadBit();
            long meaningFulBits;

            if (!secondBit)
            {
                var numBitsToRead = BitAggregateMagic.NumBitsInLongInteger - state.LeadingZeros - state.TrailingZeros;
                meaningFulBits = reader.ReadBits(numBitsToRead);
            }
            else
            {
                // a new block position was started since the number starts with "11".
                state.LeadingZeros = (sbyte)reader.ReadBits(NumBitsToEncodeNumLeadingZeros);
                var numBitsToRead = (sbyte)reader.ReadBits(NumBitsToEncodeNumMeaningfulBits);
                if (numBitsToRead == 0)
                {
                    // The block size is 64 bits which becomes 0 in writing into 6 bits - overflow.
                    // If the block size were indeed 0 bits, the xor value would be 0, and the actual value would be identical to the prior value,
                    // so we would have bailed out early on since firstBit would be 0.
                    numBitsToRead = (sbyte)BitAggregateMagic.NumBitsInLongInteger;
                }

                state.TrailingZeros = (sbyte)(BitAggregateMagic.NumBitsInLongInteger - state.LeadingZeros - numBitsToRead);
                meaningFulBits      = reader.ReadBits(numBitsToRead);
            }

            var xor = meaningFulBits << state.TrailingZeros;

            state.Value = BitConverter.Int64BitsToDouble(xor ^ BitConverter.DoubleToInt64Bits(state.Value));
            return(state);
        }
        public void VerifyEncoding_astrachan_()
        {
            string input  = "astrachan_";
            var    data   = new MemoryStream();
            var    writer = new BitBinaryWriter(data);
            var    reader = new BitBinaryReader(data);

            var compressor = new DynamicHuffman <char>(OriginalNYT);

            for (int i = 0; i < input.Length; ++i)
            {
                compressor.WriteCode(input[i], writer.Write, writer.Write);
            }
            writer.Flush();
            data.Position = 0;

            var decompressor = new DynamicHuffman <char>(OriginalNYT);

            for (int i = 0; i < input.Length; ++i)
            {
                Assert.AreEqual(input[i], decompressor.GetSymbol(reader.ReadBoolean, reader.ReadChar));
            }
            Assert.AreEqual(data.Position, data.Length);
        }
        /// <summary>
        /// Deserializes for one timestamp - v2.
        /// </summary>
        /// <param name="version">The serialization version.</param>
        /// <param name="reader">The bit reader.</param>
        /// <param name="definition">The definition.</param>
        /// <param name="values">The values.</param>
        /// <param name="dataPointIndex">Index of the data point.</param>
        /// <param name="currentBlockLeadingZeros">The current block leading zeros.</param>
        /// <param name="currentBlockTrailingZeros">The current block trailing zeros.</param>
        /// <param name="priorValidValues">The prior valid values.</param>
        private static void DeserializeForOneTimestampV2AndAbove(byte version, BitBinaryReader reader, TimeSeriesDefinition <MetricIdentifier> definition, List <List <double?> > values, int dataPointIndex, sbyte[] currentBlockLeadingZeros, sbyte[] currentBlockTrailingZeros, double[] priorValidValues)
        {
            var numSamplingTypes = values.Count;

            for (var s = 0; s < numSamplingTypes; s++)
            {
                if (dataPointIndex == 0)
                {
                    // very first value of the series
                    priorValidValues[s] = reader.BinaryReader.ReadDouble();
                    values[s].Add(GetNullableDouble(priorValidValues[s]));
                }
                else
                {
                    var firstBit = reader.ReadBit();
                    if (!firstBit)
                    {
                        // first bit is 0
                        values[s].Add(GetNullableDouble(priorValidValues[s]));
                    }
                    else
                    {
                        var secondBit = reader.ReadBit();

                        long meaningfulBits;
                        if (!secondBit)
                        {
                            // 2nd bit is 0 while the first is 1.
                            if (currentBlockLeadingZeros[s] < 0)
                            {
                                throw new Exception("The block has not been set so it is a bug in serialization on server");
                            }

                            var numBitsToRead = BitAggregateMagic.NumBitsInLongInteger - currentBlockLeadingZeros[s] - currentBlockTrailingZeros[s];

                            meaningfulBits = reader.ReadBits(numBitsToRead);
                        }
                        else
                        {
                            // a new block position was started since the number starts with "11".
                            currentBlockLeadingZeros[s] = (sbyte)reader.ReadBits(GetNumBitsToEncodeNumLeadingZeros(version));
                            var numBitsToRead = (sbyte)reader.ReadBits(NumBitsToEncodeNumMeaningfulBits);
                            if (numBitsToRead == 0)
                            {
                                // The block size is 64 bits which becomes 0 in writing into 6 bits - overflow.
                                // If the block size were indeed 0 bits, the xor value would be 0, and the actual value would be identical to the prior value,
                                // so we would not have reached here since firstBit would be 0.
                                numBitsToRead = (sbyte)BitAggregateMagic.NumBitsInLongInteger;
                            }

                            currentBlockTrailingZeros[s] = (sbyte)(BitAggregateMagic.NumBitsInLongInteger - currentBlockLeadingZeros[s] - numBitsToRead);

                            meaningfulBits = reader.ReadBits(numBitsToRead);
                        }

                        long xor = meaningfulBits << currentBlockTrailingZeros[s];
                        priorValidValues[s] = BitConverter.Int64BitsToDouble(xor ^ BitConverter.DoubleToInt64Bits(priorValidValues[s]));
                        values[s].Add(GetNullableDouble(priorValidValues[s]));
                    }
                }
            }
        }
        /// <summary>
        /// Deserializes one series.
        /// </summary>
        /// <param name="version">The serialization version.</param>
        /// <param name="reader">The reader.</param>
        /// <param name="definition">The definition.</param>
        /// <param name="numSamplingTypesRequested">The number of sampling types.</param>
        /// <returns>An <see cref="DeserializedRawData"/> object.</returns>
        private static DeserializedRawData DeserializeOneSeries(byte version, BinaryReader reader, TimeSeriesDefinition <MetricIdentifier> definition, int numSamplingTypesRequested)
        {
            // Read the number of data points.
            var totalNumberOfDatapoints = SerializationUtils.ReadInt32FromBase128(reader);

            if (totalNumberOfDatapoints < 0)
            {
                // Failed to query the series. We reuse the same byte(s) for totalNumberOfDatapoints for error codes.
                return(new DeserializedRawData(0, 0, null, (TimeSeriesErrorCode)totalNumberOfDatapoints));
            }

            // Read the number of missing data points or those with null values so that we can get the total size of data points next.
            // in V2 and beyond, totalNumberMissingDatapoints is always to set to 0 since each missing data point in a very sparse series just uses a single bit for padding,
            // and scaling will be done on the server side.
            uint totalNumberMissingDatapoints = 0;
            uint scalingFactor = 1;

            if (version == 1)
            {
                totalNumberMissingDatapoints = SerializationUtils.ReadUInt32FromBase128(reader);

                // We want to apply the scaling factor on the client so that server can return metric values of type long with variable length encoding.
                scalingFactor = SerializationUtils.ReadUInt32FromBase128(reader);
            }

            var numberDatapointsToDeserialize = totalNumberOfDatapoints - (int)totalNumberMissingDatapoints;

            var numSamplingTypes = definition?.SamplingTypes.Length ?? numSamplingTypesRequested;

            var values = new List <List <double?> >(numSamplingTypes);

            for (int index = 0; index < numSamplingTypes; index++)
            {
                values.Add(new List <double?>(totalNumberOfDatapoints));
            }

            // We use deltas/differences as compared with the prior data point.
            // Although deltas/differences don't help values of type double, it will when we encode double type in the future.
            var priorValidValues = new double[numSamplingTypes];
            var sampleTyesWithMetricValueTypeLong = new bool[numSamplingTypes];

            // Used for implementing the Gorilla algorithm
            sbyte[] currentBlockLeadingZeros  = new sbyte[numSamplingTypes];
            sbyte[] currentBlockTrailingZeros = new sbyte[numSamplingTypes];

            for (int index = 0; index < numSamplingTypes; index++)
            {
                priorValidValues[index]          = 0;
                currentBlockLeadingZeros[index]  = -1;
                currentBlockTrailingZeros[index] = -1;

                if (definition == null || IsMetricValueTypeLong(definition.SamplingTypes[index], definition.SeriesResolutionInMinutes, definition.AggregationType))
                {
                    sampleTyesWithMetricValueTypeLong[index] = true;
                }
            }

            var sparseData = totalNumberMissingDatapoints > 0;
            var bitReader  = new BitBinaryReader(reader);

            for (int d = 0; d < numberDatapointsToDeserialize; ++d)
            {
                if (version == 1)
                {
                    DeserializeForOneTimestampV1(reader, definition, sparseData, values, sampleTyesWithMetricValueTypeLong, priorValidValues, scalingFactor);
                }
                else
                {
                    DeserializeForOneTimestampV2AndAbove(version, bitReader, definition, values, d, currentBlockLeadingZeros, currentBlockTrailingZeros, priorValidValues);
                }
            }

            // Fill the remaining missing data points at the tail of the series.
            if (sparseData)
            {
                if (values[0].Count < totalNumberOfDatapoints)
                {
                    var numNullsToFill = totalNumberOfDatapoints - values[0].Count;
                    FillNulls((uint)numNullsToFill, values);
                }
            }

            // Start time can be adjusted for distinct count metric or when rollup serivce is enabled.
            var deltaOfStartTimeInMinutes = SerializationUtils.ReadInt32FromBase128(reader);

            // resolution window can be adjusted for distinct count metric or when rollup serivce is enabled.
            var deltaOfResolutionWindowInMinutes = SerializationUtils.ReadInt32FromBase128(reader);

            return(new DeserializedRawData(deltaOfStartTimeInMinutes, deltaOfResolutionWindowInMinutes, values, TimeSeriesErrorCode.Success));
        }