public static Helper Deserialize(BinaryReader reader)
            {
                Helper helper = new Helper();

                byte b = reader.ReadByte();

                helper.Sign           = (b & 0x80) != 0;
                helper.IsReady        = (b & 0x40) != 0;
                helper.AlwaysUseDelta = (b & 0x20) != 0;
                helper.Type           = (HelperType)(b & 0x1f);

                if (helper.Type == HelperType.Raw)
                {
                    return(helper);
                }

                if (helper.Type == HelperType.OneStep)
                {
                    helper.Delta = CountCompression.Deserialize(reader);
                    return(helper);
                }

                helper.DeltaBits = reader.ReadByte();
                helper.Delta     = (1UL << helper.DeltaBits) - 1;

                return(helper);
            }
            public void Serialize(BinaryWriter writer)
            {
                byte b = 0;

                if (Sign)
                {
                    b |= (byte)0x80;
                }

                if (IsReady)
                {
                    b |= (byte)0x40;
                }

                if (AlwaysUseDelta)
                {
                    b |= (byte)0x20;
                }

                b |= (byte)Type;

                writer.Write(b);

                if (Type == HelperType.Raw)
                {
                    return;
                }

                if (Type == HelperType.OneStep)
                {
                    CountCompression.Serialize(writer, Delta);
                    return;
                }

                writer.Write(DeltaBits);
            }
        public static void Compress(BinaryWriter writer, long[] values, int index, int count, Helper helper)
        {
            writer.Write(VERSION);

            if (count == 0)
            {
                return;
            }

            long value = values[index];

            CountCompression.Serialize(writer, (ulong)value);
            if (count == 1)
            {
                return;
            }

            Debug.Assert(count == helper.Count);
            if (!helper.IsReady)
            {
                helper.Prepare();
            }

            index++;
            count--;
            int maxIndex = index + count - 1;

            helper.Serialize(writer);

            if (helper.Type == HelperType.Raw)
            {
                while (index <= maxIndex)
                {
                    writer.Write(values[index++]);
                }

                return;
            }

            if (helper.Type == HelperType.OneStep)
            {
                return;
            }

            ulong maxDelta       = helper.Delta;
            bool  alwaysUseDelta = helper.AlwaysUseDelta;
            int   bitCount       = BitUtils.GetBitBounds(maxDelta);

            bool writeSign = helper.Type == HelperType.Delta;

            CommonArray common   = new CommonArray();
            int         sizeBits = helper.SizeBits > 0 ? helper.SizeBits : (1 + 1 + 64) * (count - 1);

            common.ByteArray = new byte[(int)Math.Ceiling(sizeBits / 8.0)];
            ulong[] data     = common.UInt64Array;
            int     bitIndex = 0;

            ulong delta;
            bool  sign; //false - positive, true - negative

            for (; index <= maxIndex; index++)
            {
                long newValue = values[index];
                if (newValue >= value)
                {
                    sign  = false;
                    delta = (ulong)(newValue - value);
                }
                else
                {
                    sign  = true;
                    delta = (ulong)(value - newValue);
                }

                if (alwaysUseDelta || delta <= maxDelta)
                {
                    if (!alwaysUseDelta)
                    {
                        SetFlag(data, bitIndex /*, true*/); //use delta
                        bitIndex++;
                    }

                    if (writeSign)
                    {
                        if (sign)
                        {
                            SetFlag(data, bitIndex /*, sign*/);
                        }
                        bitIndex++;
                    }

                    SetBits(data, bitIndex, delta, bitCount);
                    bitIndex += bitCount;
                }
                else
                {
                    //SetFlag(data, bitIndex, false); //don't use delta
                    bitIndex++;

                    SetValue(data, bitIndex, (ulong)newValue);
                    bitIndex += 64;
                }

                value = newValue;
            }

            int bytesCount = (int)Math.Ceiling(bitIndex / 8.0);

            CountCompression.Serialize(writer, (ulong)bytesCount);
            writer.Write(common.ByteArray, 0, bytesCount);
        }
        public static void Decompress(BinaryReader reader, Action <int, long> values, int count)
        {
            if (reader.ReadByte() != VERSION)
            {
                throw new Exception("Invalid delta compression version.");
            }

            if (count == 0)
            {
                return;
            }

            int index = 0;

            long value = (long)CountCompression.Deserialize(reader);

            values(index, value);
            if (count == 1)
            {
                return;
            }

            var helper = Helper.Deserialize(reader);

            index++;
            count--;
            int maxIndex = index + count - 1;

            if (helper.Type == HelperType.Raw)
            {
                while (index <= maxIndex)
                {
                    values(index++, reader.ReadInt64());
                }

                return;
            }

            if (helper.Type == HelperType.OneStep)
            {
                long step = (long)helper.Delta;

                if (helper.Sign == false)
                {
                    while (index <= maxIndex)
                    {
                        values(index++, (value += step));
                    }
                }
                else
                {
                    while (index <= maxIndex)
                    {
                        values(index++, (value -= step));
                    }
                }

                return;
            }

            bool  readSign       = helper.Type == HelperType.Delta;
            ulong maxDelta       = helper.Delta;
            bool  alwaysUseDelta = helper.AlwaysUseDelta;
            bool  sign           = helper.Sign;
            int   bitCount       = helper.DeltaBits;

            CommonArray common     = new CommonArray();
            int         bytesCount = (int)CountCompression.Deserialize(reader);

            common.ByteArray = reader.ReadBytes(bytesCount);
            ulong[] data     = common.UInt64Array;
            int     bitIndex = 0;

            for (; index <= maxIndex; index++)
            {
                long newValue;
                bool useDelta = alwaysUseDelta || GetFlag(data, bitIndex++);

                if (useDelta)
                {
                    if (readSign)
                    {
                        sign = GetFlag(data, bitIndex);
                        bitIndex++;
                    }

                    long delta = (long)GetBits(data, bitIndex, bitCount);
                    bitIndex += bitCount;

                    newValue = sign ? value - delta : value + delta;
                }
                else
                {
                    newValue  = (long)GetValue(data, bitIndex);
                    bitIndex += 64;
                }

                values(index, newValue);
                value = newValue;
            }
        }