/**
         * Match two feature stores given a homography from the features in store 1 to store 2. The THRESHOLD is a spatial
         * threshold in pixels to restrict the number of feature comparisons.
         *
         * @return Number of matches
         */
        public int match(FreakFeaturePointStack i_query, FreakMatchPointSetStack i_ref, InverseHomographyMat i_hinv, double tr, FeaturePairStack i_maches)
        {
            if (i_query.getLength() * i_ref.getLength() == 0)
            {
                return(0);
            }

            double tr_sqr = tr * tr;

            FreakFeaturePoint[]            query_buf = i_query.getArray();
            FreakMatchPointSetStack.Item[] ref_buf   = i_ref.getArray();
            int q_len = i_query.getLength();
            int r_len = i_ref.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();
                int best_index  = int.MaxValue; // std::numeric_limits<int>::max();

                FreakFeaturePoint fptr1 = query_buf[i];

                // Map p1 to p2 space through H
                //i_hinv.multiplyPointHomographyInhomogenous(fptr1.x, fptr1.y, tmp);
                double qx, qy;
                {
                    double w = i_hinv.m20 * fptr1.x + i_hinv.m21 * fptr1.y + i_hinv.m22;
                    qx = (i_hinv.m00 * fptr1.x + i_hinv.m01 * fptr1.y + i_hinv.m02) / w; // XP
                    qy = (i_hinv.m10 * fptr1.x + i_hinv.m11 * fptr1.y + i_hinv.m12) / w; // YP
                }


                // Search for 1st and 2nd best match
                for (int j = 0; j < r_len; j++)
                {
                    FreakFeaturePoint fptr2 = ref_buf[j];

                    // Both points should be a MINIMA or MAXIMA
                    if (fptr1.maxima != fptr2.maxima)
                    {
                        continue;
                    }
                    double tx = (qx - fptr2.x);
                    double ty = (qy - fptr2.y);
                    // Check spatial constraint
                    if ((tx * tx) + (ty * ty) > tr_sqr)
                    {
                        continue;
                    }

                    int d = fptr1.descripter.hammingDistance(fptr2.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)
                    {
                        FeaturePairStack.Item t = i_maches.prePush();
                        t.query = fptr1;
                        t.ref_  = ref_buf[best_index];
                    }
                    else
                    {
                        // Ratio test
                        double r = (double)first_best / (double)second_best;
                        if (r < this.mThreshold)
                        {
                            FeaturePairStack.Item t = i_maches.prePush();
                            t.query = fptr1;
                            t.ref_  = ref_buf[best_index];
                        }
                    }
                }
            }
            return(i_maches.getLength());
        }
        /**
         * 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());
        }