/// <summary>
        /// Read Gaussian distribution.
        /// </summary>
        /// <param name="reader">Binary reader to read Gaussian distributions.</param>
        /// <param name="dimension">Dimension of the Gaussian distribution to read.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>Gaussian distribution.</returns>
        protected virtual Gaussian ReadGaussian(BinaryReader reader, int dimension, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(reader);
            Gaussian gaussian = new Gaussian();
            gaussian.Length = dimension;

            gaussian.Weight = Config.HasWeight ? reader.ReadSingle() : float.NaN;

            if (Config.HasMean)
            {
                if (!Config.HasVariance)
                {
                    throw new InvalidDataException(Helper.NeutralFormat("Variance is needed as Mean depends on that."));
                }

                Debug.Assert(Config.MeanBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
                gaussian.Mean = new double[gaussian.Length];
                for (int i = 0; i < gaussian.Length; i++)
                {
                    gaussian.Mean[i] = reader.ReadSingle();
                }
            }

            if (Config.HasVariance)
            {
                Debug.Assert(Config.VarianceBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
                gaussian.Variance = new double[gaussian.Length];
                for (int i = 0; i < gaussian.Length; i++)
                {
                    gaussian.Variance[i] = reader.ReadSingle();
                    gaussian.Variance[i] = 1.0f / gaussian.Variance[i]; // Revert back
                }
            }

            if (Config.HasMean)
            {
                for (int i = 0; i < gaussian.Length; i++)
                {
                    gaussian.Mean[i] *= gaussian.Variance[i];   // Revert back
                }
            }

            return gaussian;
        }
        /// <summary>
        /// Write out one Gaussian distribution to be 4-byte aligned.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussian">Gaussian distribution to write out.</param>
        /// <param name="streamOrder">Dynamic order of the stream of current Gaussian distribution.</param>
        /// <returns>Number of bytes written out.</returns>
        protected uint WriteFourBytesAlignedGaussian(DataWriter writer, Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussian);
            uint size = Write(writer, gaussian, streamOrder);

            if (size == 0)
            {
                throw new InvalidDataException(Helper.NeutralFormat("Zero length of Gaussian is not allowed."));
            }

            // Pad zero bytes if needed to align with 4-bytes
            if (size % sizeof(uint) > 0)
            {
                size += writer.Write(new byte[sizeof(uint) - (size % sizeof(uint))]);
            }

            Debug.Assert(size % sizeof(uint) == 0, "Data should be 4-byte aligned.");
            return size;
        }
        /// <summary>
        /// Read linear transform.
        /// </summary>
        /// <param name="reader">Binary reader to read linear transforms.</param>
        /// <param name="dimension">Dimension of the linear transform to read.</param>
        /// <param name="blockSizes">Size of each linear transform sub matrix to read.</param>
        /// <param name="streamOrder">The dynamic order of current linear transform to read.</param>
        /// <param name="meanXform">Transform for Gaussian mean.</param>
        /// <param name="varXform">Transform for Gaussian variance.</param>
        public virtual void ReadLinXform(BinaryReader reader, int dimension, List<int> blockSizes, DynamicOrder streamOrder, out LinXForm meanXform, out LinXForm varXform)
        {
            Helper.ThrowIfNull(reader);

            long orgPos = reader.BaseStream.Position;
            meanXform = null;
            varXform = null;

            if (Config.HasMeanXform)
            {
                meanXform = ReadFourBytesAlignedLinXform(reader, dimension, blockSizes, streamOrder, Config.HasMeanBias, Config.MeanBandWidth);
            }

            if (Config.HasVarXform)
            {
                varXform = ReadFourBytesAlignedLinXform(reader, dimension, blockSizes, streamOrder, Config.HasVarBias);
            }

            if (Encoder != null)
            {
                // ramp up the encoder
                long size = reader.BaseStream.Position - orgPos;
                reader.BaseStream.Position = orgPos;
                Encoder.WarmUpData(reader.ReadBytes((int)size), 1);
            }
        }
        /// <summary>
        /// Write out Gaussian distribution.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussian">Gaussian.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the Gaussian belongs.</param>
        /// <returns>Size of bytes written.</returns>
        protected virtual uint Write(DataWriter writer, Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussian);

            uint size = 0;
            if (Config.HasWeight)
            {
                size += writer.Write((float)gaussian.Weight);
            }

            Debug.Assert(Config.MeanBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
            Debug.Assert(Config.VarianceBits == sizeof(float) * 8, "Only 32-bit float value is supported here");

            for (int i = 0; Config.HasMean && i < gaussian.Length; i++)
            {
                Debug.Assert(Config.HasVariance, "Variance is needed to encode mean for runtime.");

                double invVar = CalculateInvertedVariance(gaussian.Variance[i], DefaultVarianceFloor);
                double mean = gaussian.Mean[i] * invVar;
                size += writer.Write((float)mean);
            }

            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                double invVar = CalculateInvertedVariance(gaussian.Variance[i], DefaultVarianceFloor);
                size += writer.Write((float)invVar);
            }

            return size;
        }
        /// <summary>
        /// Quantize means and variances in Gaussian distribution into value range of fixed point numerical representation.
        /// </summary>
        /// <param name="gaussian">Gaussian distribution to quantize.</param>
        /// <param name="streamOrder">Stream order of current Gaussian distribution to quantize.</param>
        /// <returns>Quantized Gaussian distribution.</returns>
        private Gaussian Quantize(Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(gaussian);
            Gaussian quantized = new Gaussian(gaussian.Weight, gaussian.Length);

            for (int i = 0; Config.HasMean && i < gaussian.Length; i++)
            {
                double mean = (double)gaussian.Mean[i] / MaxLogF0DownScaleFactor * MeanUpScaleAsShortFactor;
                quantized.Mean[i] = (short)Clip(short.MinValue, Math.Round(mean), short.MaxValue);
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                double invVar = CalculateInvertedVariance(gaussian.Variance[i], DefaultVarianceFloor);
                if (streamOrder != DynamicOrder.Static && i >= Config.StaticVectorSize)
                {
                    // Treat non-static features differently as it has bigger range
                    invVar /= byte.MaxValue;
                }
                else
                {
                    invVar *= StaticVarianceUpscaleFactor;
                }

                invVar = Math.Sqrt(invVar);
                quantized.Variance[i] = (byte)Clip(1, Math.Round(invVar), byte.MaxValue);
                Debug.Assert(quantized.Variance[i] >= 1);
            }

            return quantized;
        }
        /// <summary>
        /// Write out Gaussian.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussian">Gaussian.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the Gaussian belongs.</param>
        /// <returns>Size of bytes written.</returns>
        protected override uint Write(DataWriter writer, Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussian);
            uint size = 0;

            Gaussian quantized = gaussian;
            if (IsNeedQuantize)
            {
                quantized = Quantize(gaussian);
                QuantizedStatistic.Put(quantized, streamOrder);
            }

            if (Config.HasWeight)
            {
                size += writer.Write((float)quantized.Weight);
            }

            if (!Config.HasMean || !Config.HasVariance)
            {
                throw new InvalidDataException("Needs both mean and variance.");
            }

            for (int i = 0; i < quantized.Length; i++)
            {
                if ((i + 1) % Config.StaticVectorSize == 0)
                {
                    size += writer.Write((short)quantized.Mean[i]);
                }
                else if (i < Config.StaticVectorSize)
                {
                    size += writer.Write((byte)quantized.Mean[i]);
                }
                else
                {
                    size += writer.Write((sbyte)quantized.Mean[i]);
                }
            }

            for (int i = 0; i < gaussian.Length; i++)
            {
                size += writer.Write((byte)quantized.Variance[i]);
            }

            return size;
        }
        /// <summary>
        /// De-quantize Gaussian distribution of LSP model.
        /// </summary>
        /// <param name="gaussian">Gaussian distribution to de-quantize.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>De-quantized Gaussian distribution.</returns>
        private Gaussian Dequantize(Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(gaussian);
            Gaussian result = new Gaussian();
            result.Length = gaussian.Length;
            result.Weight = gaussian.Weight;
            result.Mean = new double[gaussian.Length];
            result.Variance = new double[gaussian.Length];

            Debug.Assert(Config.MeanBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            double accumulatedMean = 0.0f;
            for (int i = 0; i < gaussian.Length; i++)
            {
                if ((i + 1) % Config.StaticVectorSize == 0)
                {
                    // Gain
                    result.Mean[i] = (double)(gaussian.Mean[i] * GainMaximumMean) / short.MaxValue;
                }
                else if (i < Config.StaticVectorSize)
                {
                    accumulatedMean += (double)(gaussian.Mean[i] * _staticLsfMeanDownScaleTo256Factor) / short.MaxValue;
                    result.Mean[i] = accumulatedMean;
                }
                else
                {
                    result.Mean[i] = (double)(gaussian.Mean[i] * _nonStaticLsfMeanDownScaleFactor) / short.MaxValue;
                }
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            for (int i = 0; i < gaussian.Length; i++)
            {
                double value = (int)gaussian.Variance[i];
                value = (double)(value * value);
                if (i >= Config.StaticVectorSize)
                {
                    value *= byte.MaxValue;
                }

                if ((i + 1) % Config.StaticVectorSize == 0)
                {
                    // Gain
                    value /= GainUpScaleFactor;
                }
                else
                {
                    value *= LsfDownScaleFactor;
                }

                result.Variance[i] = 1.0f / value;
            }

            return result;
        }
        /// <summary>
        /// Write out Gaussians of one stream.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussians">Gaussians.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the Gaussian belongs.</param>
        /// <returns>Size of bytes written.</returns>
        public override uint Write(DataWriter writer, Gaussian[] gaussians, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussians);

            if (gaussians.Count(g => g.Length > 0) != 1)
            {
                string message = string.Format(CultureInfo.InvariantCulture,
                    "Multi-space distribution with [{0}] non-zero-length mixtures is not supported",
                    gaussians.Count(g => g.Length > 0));
                throw new InvalidDataException(message);
            }

            // Maximum dimension among Gaussian models in this stream
            int maxDimension = gaussians.Max(g => g.Length);

            uint size = 0;
            Debug.Assert(gaussians.Count(g => g.Length == maxDimension) == 1,
                "The number of meaningful MSD mixture number should be 1");
            for (int i = 0; i < gaussians.Length; i++)
            {
                if (gaussians[i].Length == maxDimension)
                {
                    Statistic.Put(gaussians[i], streamOrder);
                    size += WriteFourBytesAlignedGaussian(writer, gaussians[i], streamOrder);
                }
            }

            return size;
        }
        /// <summary>
        /// Read one linear transform from binary reader, which is to be 4-bytes aligned.
        /// </summary>
        /// <param name="reader">Binary reader.</param>
        /// <param name="dimension">The number of dimension of the linear transform to read.</param>
        /// <param name="blockSizes">The size of each linear transform sub matrix to read.</param>
        /// <param name="streamOrder">Dynamic stream order of the linear transform belonging to.</param>
        /// <param name="hasBias">Indicate transform whether has bias info.</param>
        /// <param name="bandWidth">Band width of linear transform matrix.</param>
        /// <returns>Retrieved linear transform.</returns>
        protected LinXForm ReadFourBytesAlignedLinXform(BinaryReader reader, int dimension, List<int> blockSizes, DynamicOrder streamOrder, bool hasBias, uint bandWidth = 0)
        {
            Helper.ThrowIfNull(reader);
            long basePosition = reader.BaseStream.Position;
            LinXForm linXform = ReadOneLinXform(reader, dimension, blockSizes, streamOrder, hasBias, bandWidth);

            // Skip padding bytes for 4-byte alignment
            int readSize = (int)(reader.BaseStream.Position - basePosition);
            if (readSize % sizeof(uint) > 0)
            {
                int padByteCount = sizeof(uint) - (readSize % sizeof(uint));
                reader.ReadBytes(padByteCount);
            }

            return linXform;
        }
            /// <summary>
            /// Puts a sample into this list.
            /// </summary>
            /// <param name="linXform">Linear transform.</param>
            /// <param name="streamOrder">Dynamic stream order.</param>
            public void Put(LinXForm linXform, DynamicOrder streamOrder)
            {
                Helper.ThrowIfNull(linXform);
                if (!_linXformStatistics.ContainsKey(streamOrder))
                {
                    _linXformStatistics.Add(streamOrder, new LinXformStatistic(linXform));
                }

                _linXformStatistics[streamOrder].Put(linXform);
            }
        /// <summary>
        /// Write out one linear transform to be 4-byte aligned.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="linXform">Linear transform to write out.</param>
        /// <param name="streamOrder">Dynamic order of the stream of current linear transform.</param>
        /// <param name="hasBias">Indicate transform whether has bias info.</param>
        /// <param name="bandWidth">Band width of linear transform matrix.</param>
        /// <returns>Number of bytes written out.</returns>
        protected uint WriteFourBytesAlignedLinXform(DataWriter writer, LinXForm linXform, DynamicOrder streamOrder, bool hasBias, uint bandWidth = 0)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(linXform);
            uint size = Write(writer, linXform, streamOrder, hasBias, bandWidth);

            if (size == 0)
            {
                throw new InvalidDataException(Helper.NeutralFormat("Zero length of linXform is not allowed."));
            }

            // Pad zero bytes if needed to align with 4-bytes
            if (size % sizeof(uint) > 0)
            {
                size += writer.Write(new byte[sizeof(uint) - (size % sizeof(uint))]);
            }

            Debug.Assert(size % sizeof(uint) == 0, "Data should be 4-byte aligned.");
            return size;
        }
        /// <summary>
        /// Read linear transform.
        /// </summary>
        /// <param name="reader">Binary reader to read linear transform.</param>
        /// <param name="dimension">Dimension of the linear transform to read.</param>
        /// <param name="blockSizes">Size of each linear transform sub matrix to read.</param>
        /// <param name="streamOrder">The dynamic order of current linear transform to read.</param>
        /// <param name="hasBias">Indicate transform whether has bias info.</param>
        /// <param name="bandWidth">Band width of linear transform matrix.</param>
        /// <returns>Linear transform.</returns>
        protected virtual LinXForm ReadOneLinXform(BinaryReader reader, int dimension, List<int> blockSizes, DynamicOrder streamOrder, bool hasBias, uint bandWidth)
        {
            Helper.ThrowIfNull(reader);
            LinXForm linXform = new LinXForm();
            linXform.VecSize = dimension;

            if (hasBias)
            {
                Debug.Assert(Config.BiasBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
                linXform.Bias = new float[dimension];
                for (int i = 0; i < linXform.VecSize; i++)
                {
                    linXform.Bias[i] = reader.ReadSingle();
                }
            }

            Debug.Assert(Config.MatrixBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
            for (int i = 0; i < blockSizes.Count; i++)
            {
                float[,] block = new float[blockSizes[i], blockSizes[i]];
                for (int j = 0; j < blockSizes[i]; j++)
                {
                    for (int k = 0; k < blockSizes[i]; k++)
                    {
                        if (k < j - bandWidth || k > j + bandWidth)
                        {
                            block[j, k] = 0;
                        }
                        else
                        {
                            block[j, k] = reader.ReadSingle();
                        }
                    }
                }

                linXform.Blocks.Add(block);
            }

            return linXform;
        }
        /// <summary>
        /// Write out linear transform.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="linXform">Linear transform.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the linear transform belongs.</param>
        /// <param name="hasBias">Indicate transform whether has bias info.</param>
        /// <param name="bandWidth">Band width of linear transform matrix.</param>
        /// <returns>Size of bytes written.</returns>
        protected virtual uint Write(DataWriter writer, LinXForm linXform, DynamicOrder streamOrder, bool hasBias, uint bandWidth)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(linXform);

            uint size = 0;

            Debug.Assert(Config.BiasBits == sizeof(float) * 8, "Only 32-bit float value is supported here");
            Debug.Assert(Config.MatrixBits == sizeof(float) * 8, "Only 32-bit float value is supported here");

            for (int i = 0; hasBias && i < linXform.VecSize; i++)
            {
                size += writer.Write(linXform.Bias[i]);
            }

            for (int i = 0; i < linXform.Blocks.Count; i++)
            {
                for (int j = 0; j < linXform.Blocks[i].GetLength(0); j++)
                {
                    for (int k = 0; k < linXform.Blocks[i].GetLength(1); k++)
                    {
                        if (k >= j - bandWidth && k <= j + bandWidth)
                        {
                            size += writer.Write(linXform.Blocks[i][j, k]);
                        }
                    }
                }
            }

            return size;
        }
        /// <summary>
        /// Read one Gaussian distribution from binary reader, which is to be 4-bytes aligned.
        /// </summary>
        /// <param name="reader">Binary reader.</param>
        /// <param name="dimension">The number of dimension of the Gaussian distribution to read.</param>
        /// <param name="streamOrder">Dynamic stream order of the Gaussian distribution belonging to.</param>
        /// <returns>Retrieved Gaussian distribution.</returns>
        protected Gaussian ReadFourBytesAlignedGaussian(BinaryReader reader, int dimension, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(reader);
            long basePosition = reader.BaseStream.Position;
            Gaussian gaussian = ReadGaussian(reader, dimension, streamOrder);

            // Skip padding bytes for 4-byte alignment
            int readSize = (int)(reader.BaseStream.Position - basePosition);
            if (readSize % sizeof(uint) > 0)
            {
                int padByteCount = sizeof(uint) - (readSize % sizeof(uint));
                reader.ReadBytes(padByteCount);
            }

            return gaussian;
        }
        /// <summary>
        /// Write out Gaussian.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussian">Gaussian.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the Gaussian belongs.</param>
        /// <returns>Size of bytes written.</returns>
        protected override uint Write(DataWriter writer, Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussian);
            uint size = 0;

            Gaussian quantized = gaussian;
            if (IsNeedQuantize)
            {
                quantized = Quantize(gaussian, streamOrder);
                QuantizedStatistic.Put(quantized, streamOrder);
            }

            Debug.Assert(Config.MeanBits == sizeof(short) * 8, "Only 16-bit short value is supported here");
            for (int i = 0; Config.HasMean && i < gaussian.Length; i++)
            {
                size += writer.Write((short)quantized.Mean[i]);
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                size += writer.Write((byte)quantized.Variance[i]);
            }

            return size;
        }
            /// <summary>
            /// Puts a sample into this list.
            /// </summary>
            /// <param name="gaussian">Gaussian distribution.</param>
            /// <param name="streamOrder">Dynamic stream order.</param>
            public void Put(Gaussian gaussian, DynamicOrder streamOrder)
            {
                Helper.ThrowIfNull(gaussian);
                if (!GuassianStatistics.ContainsKey(streamOrder))
                {
                    GuassianStatistics.Add(streamOrder, new GaussianStatistic(gaussian.Length));
                }

                GuassianStatistics[streamOrder].Put(gaussian);
            }
        /// <summary>
        /// Read Gaussian distribution.
        /// </summary>
        /// <param name="reader">Binary reader to read Gaussian distributions.</param>
        /// <param name="dimension">Dimension of the Gaussian distribution to read.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>Gaussian distribution.</returns>
        protected override Gaussian ReadGaussian(BinaryReader reader, int dimension, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(reader);
            Gaussian gaussian = new Gaussian();
            gaussian.Length = dimension;

            Debug.Assert(Config.MeanBits == sizeof(short) * 8, "Only 16-bit short value is supported here");
            gaussian.Mean = new double[gaussian.Length];
            for (int i = 0; i < gaussian.Length; i++)
            {
                gaussian.Mean[i] = reader.ReadInt16();
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            gaussian.Variance = new double[gaussian.Length];
            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                gaussian.Variance[i] = (int)reader.ReadByte();
            }

            if (IsNeedQuantize)
            {
                gaussian = Dequantize(gaussian, streamOrder);
            }

            return gaussian;
        }
        /// <summary>
        /// Write out Gaussian.
        /// </summary>
        /// <param name="writer">Binary writer.</param>
        /// <param name="gaussian">Gaussian.</param>
        /// <param name="streamOrder">The dynamic order of stream, to which the Gaussian belongs.</param>
        /// <returns>Size of bytes written.</returns>
        protected override uint Write(DataWriter writer, Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussian);
            uint size = 0;

            Gaussian quantized = gaussian;
            if (IsNeedQuantize)
            {
                quantized = Quantize(gaussian, streamOrder);
                QuantizedStatistic.Put(quantized, streamOrder);
            }

            if (Config.HasWeight)
            {
                if (quantized.Weight == 0.0f)
                {
                    throw new InvalidDataException(Helper.NeutralFormat("Zero weight of LogF0 is found."));
                }

                size += writer.Write((float)quantized.Weight);
            }

            Debug.Assert(Config.MeanBits == sizeof(short) * 8, "Only 16-bit short value is supported here");
            for (int i = 0; Config.HasMean && i < gaussian.Length; i++)
            {
                size += writer.Write((short)quantized.Mean[i]);
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                size += writer.Write((byte)quantized.Variance[i]);
            }

            return size;
        }
        public virtual uint Write(DataWriter writer, Gaussian[] gaussians, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(gaussians);

            DataWriter orgWriter = writer;
            MemoryStream gaussiansBuf = null;
            if (EnableCompress)
            {
                gaussiansBuf = new MemoryStream();
                writer = new DataWriter(gaussiansBuf);
            }

            uint size = 0;
            for (int i = 0; i < gaussians.Length; i++)
            {
                Statistic.Put(gaussians[i], streamOrder);
                size += WriteFourBytesAlignedGaussian(writer, gaussians[i], streamOrder);
            }

            if (EnableCompress)
            {
                size = orgWriter.Write(Encoder.Encode(gaussiansBuf.ToArray()));
                if (size % sizeof(uint) != 0)
                {
                    size += orgWriter.Write(new byte[sizeof(uint) - (size % sizeof(uint))]);
                }

                writer = orgWriter;
            }

            return size;
        }
        /// <summary>
        /// De-quantize Gaussian distribution of LogF0 model.
        /// </summary>
        /// <param name="gaussian">Gaussian distribution to de-quantize.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>De-quantized Gaussian distribution.</returns>
        private Gaussian Dequantize(Gaussian gaussian, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(gaussian);
            Gaussian result = new Gaussian(gaussian.Weight, gaussian.Length);
            Debug.Assert(Config.MeanBits == sizeof(short) * 8, "Only 16-bit short value is supported here");
            for (int i = 0; i < gaussian.Length; i++)
            {
                result.Mean[i] = (double)(gaussian.Mean[i] * MaxLogF0DownScaleFactor) / MeanUpScaleAsShortFactor;
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            for (int i = 0; Config.HasVariance && i < gaussian.Length; i++)
            {
                double value = (int)gaussian.Variance[i];
                if (i >= Config.StaticVectorSize && streamOrder != DynamicOrder.Static)
                {
                    value = (double)(value * value);
                    value *= byte.MaxValue;
                }

                result.Variance[i] = 1.0f / value;
            }

            return result;
        }
        /// <summary>
        /// Read Gaussian distributions.
        /// </summary>
        /// <param name="reader">Binary reader to read Gaussian distributions.</param>
        /// <param name="dimension">Dimension of the Gaussian distribution to read.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>Gaussian distributions.</returns>
        public virtual Gaussian[] ReadGaussians(BinaryReader reader, int dimension, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(reader);

            long orgPos = reader.BaseStream.Position;

            Gaussian[] gaussians = new Gaussian[Config.MixtureCount];
            for (uint i = 0; i < Config.MixtureCount; i++)
            {
                gaussians[i] = ReadFourBytesAlignedGaussian(reader, dimension, streamOrder);
            }

            if (Encoder != null)
            {
                // ramp up the encoder
                long size = reader.BaseStream.Position - orgPos;
                reader.BaseStream.Position = orgPos;
                Encoder.WarmUpData(reader.ReadBytes((int)size), 1);
            }

            return gaussians;
        }
        /// <summary>
        /// Read Gaussian distribution.
        /// </summary>
        /// <param name="reader">Binary reader to read Gaussian distributions.</param>
        /// <param name="dimension">Dimension of the Gaussian distribution to read.</param>
        /// <param name="streamOrder">The dynamic order of current Gaussian distribution to read.</param>
        /// <returns>Gaussian distribution.</returns>
        protected override Gaussian ReadGaussian(BinaryReader reader, int dimension, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(reader);
            Gaussian gaussian = new Gaussian();
            gaussian.Length = dimension;
            gaussian.Weight = Config.HasWeight ? reader.ReadSingle() : float.NaN;

            Debug.Assert(Config.MeanBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            gaussian.Mean = new double[gaussian.Length];
            for (int i = 0; i < gaussian.Length; i++)
            {
                if ((i + 1) % Config.StaticVectorSize == 0)
                {
                    // Gain
                    gaussian.Mean[i] = reader.ReadInt16();
                }
                else if (i < Config.StaticVectorSize)
                {
                    gaussian.Mean[i] = reader.ReadByte();
                }
                else
                {
                    gaussian.Mean[i] = reader.ReadSByte();
                }
            }

            Debug.Assert(Config.VarianceBits == sizeof(byte) * 8, "Only 8-bit byte value is supported here");
            gaussian.Variance = new double[gaussian.Length];
            for (int i = 0; i < gaussian.Length; i++)
            {
                gaussian.Variance[i] = reader.ReadByte();
            }

            if (IsNeedQuantize)
            {
                gaussian = Dequantize(gaussian, streamOrder);
            }

            return gaussian;
        }
        public virtual uint Write(DataWriter writer, LinXForm meanXform, LinXForm varXform, DynamicOrder streamOrder)
        {
            Helper.ThrowIfNull(writer);
            Helper.ThrowIfNull(meanXform);
            Helper.ThrowIfNull(varXform);

            DataWriter orgWriter = writer;
            MemoryStream linXformsBuf = null;
            if (EnableCompress)
            {
                linXformsBuf = new MemoryStream();
                writer = new DataWriter(linXformsBuf);
            }

            uint size = 0;
            if (Config.HasMeanXform)
            {
                MeanStatistic.Put(meanXform, streamOrder);
                size += WriteFourBytesAlignedLinXform(writer, meanXform, streamOrder, Config.HasMeanBias, Config.MeanBandWidth);
            }

            if (Config.HasVarXform)
            {
                VarStatistic.Put(varXform, streamOrder);
                size += WriteFourBytesAlignedLinXform(writer, varXform, streamOrder, Config.HasVarBias);
            }

            if (EnableCompress)
            {
                size = orgWriter.Write(Encoder.Encode(linXformsBuf.ToArray()));
                if (size % sizeof(uint) != 0)
                {
                    size += orgWriter.Write(new byte[sizeof(uint) - (size % sizeof(uint))]);
                }

                writer = orgWriter;
            }

            return size;
        }