/// <summary>
        /// Gets the closest decoded value.
        /// </summary>
        /// <returns>The closest decoded value.</returns>
        /// <param name="moduleBitCount">Module bit count.</param>
        private static int GetClosestDecodedValue(int[] moduleBitCount)
        {
            int bitCountSum = PDF417Common.GetBitCountSum(moduleBitCount);

            float[] bitCountRatios = new float[PDF417Common.BARS_IN_MODULE];
            for (int i = 0; i < bitCountRatios.Length; i++)
            {
                bitCountRatios[i] = moduleBitCount[i] / (float)bitCountSum;
            }
            float bestMatchError = float.MaxValue;
            int   bestMatch      = PDF417Common.INVALID_CODEWORD;

            for (int j = 0; j < RATIOS_TABLE.Length; j++)
            {
                float error = 0.0f;
                for (int k = 0; k < PDF417Common.BARS_IN_MODULE; k++)
                {
                    float diff = RATIOS_TABLE[j][k] - bitCountRatios[k];
                    error += diff * diff;
                }
                if (error < bestMatchError)
                {
                    bestMatchError = error;
                    bestMatch      = PDF417Common.SYMBOL_TABLE[j];
                }
            }
            return(bestMatch);
        }
        /// <summary>
        /// Gets the closest decoded value.
        /// </summary>
        /// <returns>The closest decoded value.</returns>
        /// <param name="moduleBitCount">Module bit count.</param>
        private static int getClosestDecodedValue(int[] moduleBitCount)
        {
            var bitCountSum    = PDF417Common.getBitCountSum(moduleBitCount);
            var bitCountRatios = new double[PDF417Common.BARS_IN_MODULE];

            for (int i = 0; i < bitCountRatios.Length; i++)
            {
                bitCountRatios[i] = moduleBitCount[i] / (double)bitCountSum;
            }
            var result = RATIOS_TREE.NearestNeighbors(bitCountRatios, 1);

            result.MoveNext();
            return(result.Current);
        }
        /// <summary>
        /// Samples the bit counts.
        /// </summary>
        /// <returns>The bit counts.</returns>
        /// <param name="moduleBitCount">Module bit count.</param>
        private static int[] sampleBitCounts(int[] moduleBitCount)
        {
            float bitCountSum = PDF417Common.getBitCountSum(moduleBitCount);

            int[] result          = new int[PDF417Common.BARS_IN_MODULE];
            int   bitCountIndex   = 0;
            int   sumPreviousBits = 0;

            for (int i = 0; i < PDF417Common.MODULES_IN_CODEWORD; i++)
            {
                float sampleIndex =
                    bitCountSum / (2 * PDF417Common.MODULES_IN_CODEWORD) +
                    (i * bitCountSum) / PDF417Common.MODULES_IN_CODEWORD;
                if (sumPreviousBits + moduleBitCount[bitCountIndex] <= sampleIndex)
                {
                    sumPreviousBits += moduleBitCount[bitCountIndex];
                    bitCountIndex++;
                }
                result[bitCountIndex]++;
            }
            return(result);
        }
        /// <summary>
        /// Gets the decoded codeword value.
        /// </summary>
        /// <returns>The decoded codeword value.</returns>
        /// <param name="moduleBitCount">Module bit count.</param>
        private static int getDecodedCodewordValue(int[] moduleBitCount)
        {
            int decodedValue = getBitValue(moduleBitCount);

            return(PDF417Common.getCodeword(decodedValue) == PDF417Common.INVALID_CODEWORD ? PDF417Common.INVALID_CODEWORD : decodedValue);
        }
        /// <summary>
        /// Detects the codeword.
        /// </summary>
        /// <returns>The codeword.</returns>
        /// <param name="image">Image.</param>
        /// <param name="minColumn">Minimum column.</param>
        /// <param name="maxColumn">Max column.</param>
        /// <param name="leftToRight">If set to <c>true</c> left to right.</param>
        /// <param name="startColumn">Start column.</param>
        /// <param name="imageRow">Image row.</param>
        /// <param name="minCodewordWidth">Minimum codeword width.</param>
        /// <param name="maxCodewordWidth">Max codeword width.</param>
        private static Codeword detectCodeword(BitMatrix image,
                                               int minColumn,
                                               int maxColumn,
                                               bool leftToRight,
                                               int startColumn,
                                               int imageRow,
                                               int minCodewordWidth,
                                               int maxCodewordWidth)
        {
            startColumn = adjustCodewordStartColumn(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
            // we usually know fairly exact now how long a codeword is. We should provide minimum and maximum expected length
            // and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels.
            // min and maxCodewordWidth should not be used as they are calculated for the whole barcode an can be inaccurate
            // for the current position
            int[] moduleBitCount = getModuleBitCount(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
            if (moduleBitCount == null)
            {
                return(null);
            }
            int endColumn;
            int codewordBitCount = ZXing.Common.Detector.MathUtils.sum(moduleBitCount);

            if (leftToRight)
            {
                endColumn = startColumn + codewordBitCount;
            }
            else
            {
                for (int i = 0; i < (moduleBitCount.Length >> 1); i++)
                {
                    int tmpCount = moduleBitCount[i];
                    moduleBitCount[i] = moduleBitCount[moduleBitCount.Length - 1 - i];
                    moduleBitCount[moduleBitCount.Length - 1 - i] = tmpCount;
                }
                endColumn   = startColumn;
                startColumn = endColumn - codewordBitCount;
            }
            // TODO implement check for width and correction of black and white bars
            // use start (and maybe stop pattern) to determine if blackbars are wider than white bars. If so, adjust.
            // should probably done only for codewords with a lot more than 17 bits.
            // The following fixes 10-1.png, which has wide black bars and small white bars
            //    for (int i = 0; i < moduleBitCount.Length; i++) {
            //      if (i % 2 == 0) {
            //        moduleBitCount[i]--;
            //      } else {
            //        moduleBitCount[i]++;
            //      }
            //    }

            // We could also use the width of surrounding codewords for more accurate results, but this seems
            // sufficient for now
            if (!checkCodewordSkew(codewordBitCount, minCodewordWidth, maxCodewordWidth))
            {
                // We could try to use the startX and endX position of the codeword in the same column in the previous row,
                // create the bit count from it and normalize it to 8. This would help with single pixel errors.
                return(null);
            }

            int decodedValue = PDF417CodewordDecoder.getDecodedValue(moduleBitCount);
            int codeword     = PDF417Common.getCodeword(decodedValue);

            if (codeword == -1)
            {
                return(null);
            }
            return(new Codeword(startColumn, endColumn, getCodewordBucketNumber(decodedValue), codeword));
        }