public void WhenRawEstimateIsSmallerThanAllArrayValuesCorrectBiasIsUsed()
        {
            // The bias of the first array element should be used
            double corrected = BiasCorrection.CorrectBias(10.5, 4);

            Assert.Equal(10.5 - 10, corrected);
        }
        public void WhenRawEstimateIsLargerThanAllArrayValuesCorrectBiasIsUsed()
        {
            // The bias of the last array element should be used
            double corrected = BiasCorrection.CorrectBias(78.0, 4);

            Assert.Equal(78.0 - -1.7606, corrected);
        }
        public void WhenRawEstimateIsBetweenArrayValuesCorrectBiasIsUsed()
        {
            double corrected = BiasCorrection.CorrectBias(11.1, 4);

            // The bias should be between 10 and 9.717, but much closer to 10
            Assert.Equal(1.1394700139470011, corrected);
        }
        public ulong Count()
        {
            // If only a few elements have been seen, return the exact count
            if (this.directCount != null)
            {
                return (ulong) this.directCount.Count;
            }

            double zInverse = 0;
            double v = 0;

            if (this.isSparse)
            {
                // calc c and Z's inverse
                foreach (KeyValuePair<ushort, byte> kvp in this.lookupSparse)
                {
                    byte sigma = kvp.Value;
                    zInverse += Math.Pow(2, -sigma);
                }
                v = this.m - this.lookupSparse.Count;
                zInverse += (this.m - this.lookupSparse.Count);
            }
            else
            {
                // calc c and Z's inverse
                for (var i = 0; i < this.m; i++)
                {
                    byte sigma = this.lookupDense[i];
                    zInverse += Math.Pow(2, -sigma);
                    if (sigma == 0)
                    {
                        v++;
                    }
                }
            }

            double e = this.alphaM*this.m*this.m/zInverse;
            if (e <= 5.0*this.m)
            {
                e = BiasCorrection.CorrectBias(e, this.bitsPerIndex);
            }

            double h;
            if (v > 0)
            {
                // LinearCounting estimate
                h = this.m*Math.Log(this.m/v);
            }
            else
            {
                h = e;
            }

            if (h <= this.subAlgorithmSelectionThreshold)
            {
                return (ulong) Math.Round(h);
            }
            return (ulong) Math.Round(e);
        }
        public void WhenCorrectedEstimateIsBelowZeroZeroIsReturned()
        {
            double corrected = BiasCorrection.CorrectBias(5, 4);

            Assert.Equal(0, corrected);
        }
        public void WhenRawEstimateIsInArrayCorrectBiasIsUsed()
        {
            double corrected = BiasCorrection.CorrectBias(12.207, 4);

            Assert.Equal(12.207 - 9.207, corrected);
        }