/**
         * Match two feature stores.
         *
         * @return Number of matches
         */
        public virtual int match(FreakFeaturePointStack i_query, Keyframe i_key_frame, FeaturePairStack i_maches)
        {
            FreakMatchPointSetStack ref_ = i_key_frame.getFeaturePointSet();

            if (i_query.getLength() == 0 || ref_.getLength() == 0)
            {
                return(0);
            }
            // mMatches.reserve(features1.size());
            for (int i = 0; i < i_query.getLength(); i++)
            {
                int first_best  = int.MaxValue;
                int second_best = int.MaxValue;
                int best_index  = int.MaxValue;

                // Search for 1st and 2nd best match
                FreakFeaturePoint p1 = i_query.getItem(i);
                for (int j = 0; j < ref_.getLength(); j++)
                {
                    // Both points should be a MINIMA or MAXIMA
                    if (p1.maxima != ref_.getItem(j).maxima)
                    {
                        continue;
                    }

                    // ASSERT(FEATURE_SIZE == 96, "Only 96 bytes supported now");
                    int d = i_query.getItem(i).descripter.hammingDistance(ref_.getItem(j).descripter);
                    if (d < first_best)
                    {
                        second_best = first_best;
                        first_best  = d;
                        best_index  = (int)j;
                    }
                    else if (d < second_best)
                    {
                        second_best = d;
                    }
                }

                // Check if FIRST_BEST has been set
                if (first_best != int.MaxValue)
                {
                    // If there isn't a SECOND_BEST, then always choose the FIRST_BEST.
                    // Otherwise, do a ratio test.
                    if (second_best == int.MaxValue)
                    {
                        // mMatches.push_back(match_t((int)i, best_index));
                        FeaturePairStack.Item t = i_maches.prePush();
                        t.query = i_query.getItem(i);
                        t.ref_  = ref_.getItem(best_index);
                    }
                    else
                    {
                        // Ratio test
                        double r = (double)first_best / (double)second_best;
                        if (r < mThreshold)
                        {
                            FeaturePairStack.Item t = i_maches.prePush();
                            t.query = i_query.getItem(i);
                            t.ref_  = ref_.getItem(best_index);
                            // mMatches.push_back(match_t((int)i, best_index));
                        }
                    }
                }
            }
            // ASSERT(mMatches.size() <= features1->size(), "Number of matches should be lower");
            return(i_maches.getLength());
        }
        /**
         * Match two feature stores with an index on features2.
         *
         * @return Number of matches
         */
        override public int match(FreakFeaturePointStack i_query, Keyframe i_key_frame, FeaturePairStack i_maches)
        {
            //indexが無いときはベースクラスを使う。
            BinaryHierarchicalNode index2 = i_key_frame.getIndex();

            if (index2 == null)
            {
                return(base.match(i_query, i_key_frame, i_maches));
            }
            FreakMatchPointSetStack ref_ = i_key_frame.getFeaturePointSet();

            if (i_query.getLength() * ref_.getLength() == 0)
            {
                return(0);
            }
            FreakFeaturePoint[] query_buf = i_query.getArray();
            int q_len = i_query.getLength();

            for (int i = 0; i < q_len; i++)
            {
                int first_best  = int.MaxValue;                 // std::numeric_limits<unsigned int>::max();
                int second_best = int.MaxValue;                 // std::numeric_limits<unsigned int>::max();
                FreakMatchPointSetStack.Item best_index = null; // std::numeric_limits<int>::max();

                // Perform an indexed nearest neighbor lookup
                FreakFeaturePoint fptr1 = query_buf[i];

                int num_of_fp = this._selector.query(index2, fptr1.descripter);
                // Search for 1st and 2nd best match
                FreakMatchPointSetStack.Item[] v = this._selector._result;
                for (int j = 0; j < num_of_fp; j++)
                {
                    FreakFeaturePoint fptr2 = v[j];
                    // Both points should be a MINIMA or MAXIMA
                    if (fptr1.maxima != fptr2.maxima)
                    {
                        continue;
                    }

                    int d = fptr1.descripter.hammingDistance(fptr2.descripter);
                    if (d < first_best)
                    {
                        second_best = first_best;
                        first_best  = d;
                        best_index  = v[j];
                    }
                    else if (d < second_best)
                    {
                        second_best = d;
                    }
                }

                // Check if FIRST_BEST has been set
                if (first_best != int.MaxValue)
                {
                    // If there isn't a SECOND_BEST, then always choose the FIRST_BEST.
                    // Otherwise, do a ratio test.
                    if (second_best == int.MaxValue)
                    {
                        FeaturePairStack.Item t = i_maches.prePush();
                        t.query = fptr1;
                        t.ref_  = best_index;
                    }
                    else
                    {
                        // Ratio test
                        double r = (double)first_best / (double)second_best;
                        if (r < mThreshold)
                        {
                            // mMatches.push_back(match_t((int)i, best_index));
                            FeaturePairStack.Item t = i_maches.prePush();
                            t.query = fptr1;
                            t.ref_  = best_index;
                        }
                    }
                }
            }
            return(i_maches.getLength());
        }