/// <summary>
        /// Compress the hybrid estimator.
        /// </summary>
        /// <param name="inPlace"></param>
        /// <returns></returns>
        public IHybridEstimator <TEntity, int, TCount> Compress(bool inPlace)
        {
            IHybridEstimator <TEntity, int, TCount> self = this;
            var res = FullExtract().Compress(_configuration);

            if (inPlace)
            {
                self.Rehydrate(res);
                return(this);
            }
            IHybridEstimator <TEntity, int, TCount> estimator = new HybridEstimator <TEntity, TId, TCount>(
                res.StrataEstimator.BlockSize,
                res.StrataEstimator.StrataCount,
                _configuration);

            if (res.BitMinwiseEstimator != null)
            {
                estimator.Initialize(
                    res.BitMinwiseEstimator.Capacity + res.ItemCount,
                    res.BitMinwiseEstimator.BitSize,
                    res.BitMinwiseEstimator.HashCount);
            }
            estimator.Rehydrate(res);
            return(estimator);
        }
        /// <summary>
        /// Create a hybrid estimator
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <typeparam name="TId">The type of the entity identifier</typeparam>
        /// <typeparam name="TCount">The type of occurence count.</typeparam>
        /// <param name="configuration">Bloom filter configuration</param>
        /// <param name="precalculatedEstimator"></param>
        /// <param name="failedDecodeCount">Number of times decoding has failed based upon the provided estimator.</param>
        /// <returns></returns>
        public IHybridEstimatorData <int, TCount> Extract <TEntity, TId, TCount>(
            IInvertibleBloomFilterConfiguration <TEntity, TId, int, TCount> configuration,
            HybridEstimator <TEntity, TId, TCount> precalculatedEstimator,
            byte failedDecodeCount = 0)
            where TCount : struct
            where TId : struct
        {
            if (precalculatedEstimator == null)
            {
                return(null);
            }
            //after two failed attempts, don't fold.
            if (failedDecodeCount > 2)
            {
                return(precalculatedEstimator.Extract());
            }
            var data       = precalculatedEstimator.FullExtract();
            var strata     = GetRecommendedStrata(configuration, data.ItemCount, failedDecodeCount);
            var blockSize  = GetRecommendedBlockSize(configuration, data.ItemCount, failedDecodeCount);
            var factors    = configuration.FoldingStrategy?.GetAllFoldFactors(precalculatedEstimator.BlockSize);
            var foldFactor = blockSize > 0L ?
                             (uint)factors?
                             .OrderByDescending(f => f)
                             //for estimators: capacity is the block size.
                             .Where(f => f > 1 && precalculatedEstimator.BlockSize / f > blockSize)
                             .Skip(failedDecodeCount)
                             .FirstOrDefault() :
                             0L;

            if (failedDecodeCount > 1)
            {
                //after more than 1 failed attempt, go for the lowest fold factor.
                foldFactor = (uint)factors.OrderBy(f => f).FirstOrDefault(f => f > 1 && precalculatedEstimator.BlockSize / f > blockSize);
            }
            if (foldFactor > 1)
            {
                data = data.Fold(configuration, (uint)foldFactor);
            }
            data.StrataEstimator.LowerStrata(strata);
            if (failedDecodeCount > 1)
            {
                data.StrataEstimator.DecodeCountFactor = Math.Pow(2, failedDecodeCount);
            }
            return(data.ToEstimatorData());
        }
        /// <summary>
        /// Create an estimator that matches the given <paramref name="data"/> estimator.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity</typeparam>
        /// <typeparam name="TId">The type of the identifier</typeparam>
        /// <typeparam name="TCount">The type of the occurence count</typeparam>
        /// <param name="data">The estimator data to match</param>
        /// <param name="configuration">The Bloom filter configuration</param>
        /// <param name="setSize">The (estimated) size of the set to add to the estimator.</param>
        /// <returns>An estimator</returns>
        public IHybridEstimator <TEntity, int, TCount> CreateMatchingEstimator <TEntity, TId, TCount>(
            IHybridEstimatorData <int, TCount> data,
            IInvertibleBloomFilterConfiguration <TEntity, TId, int, TCount> configuration,
            long setSize)
            where TCount : struct
            where TId : struct
        {
            var estimator = new HybridEstimator <TEntity, TId, TCount>(
                data.StrataEstimator.BlockSize,
                data.StrataEstimator.StrataCount,
                configuration,
                fixedBlockSize: true)
            {
                DecodeCountFactor = data.StrataEstimator.DecodeCountFactor
            };

            if (data.BitMinwiseEstimator != null)
            {
                estimator.Initialize(setSize, data.BitMinwiseEstimator.BitSize, data.BitMinwiseEstimator.HashCount);
            }
            return(estimator);
        }
        /// <summary>
        /// Create a hybrid estimator
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <typeparam name="TId">The type of the entity identifier</typeparam>
        /// <typeparam name="TCount">The type of occurence count.</typeparam>
        /// <param name="configuration">Bloom filter configuration</param>
        /// <param name="setSize">Number of elements in the set that is added.</param>
        /// <param name="failedDecodeCount">Number of times decoding has failed based upon the provided estimator.</param>
        /// <returns></returns>
        public HybridEstimator <TEntity, TId, TCount> Create <TEntity, TId, TCount>(
            IInvertibleBloomFilterConfiguration <TEntity, TId, int, TCount> configuration,
            long setSize,
            byte failedDecodeCount = 0)
            where TCount : struct
            where TId : struct
        {
            var minwiseHashCount = GetRecommendedMinwiseHashCount(configuration, setSize, failedDecodeCount);
            var strata           = GetRecommendedStrata(configuration, setSize, failedDecodeCount);
            var capacity         = GetRecommendedBlockSize(configuration, setSize, failedDecodeCount);
            var bitSize          = GetRecommendedBitSize(configuration, setSize, failedDecodeCount);
            var result           = new HybridEstimator <TEntity, TId, TCount>(
                capacity,
                strata,
                configuration);

            result.Initialize(setSize, bitSize, minwiseHashCount);
            if (failedDecodeCount > 1)
            {
                result.DecodeCountFactor = Math.Pow(2, failedDecodeCount);
            }
            return(result);
        }