/// <summary>
        /// one sample stroke contains many template strokes
        /// </summary>
        /// <param name="sample"></param>
        /// <param name="template"></param>
        /// <returns>stroke index -> list of template indices it contains</returns>
        public static List <List <int> > StrokeToStrokeCorrespondenceConcatenating(List <SketchStroke> sample, List <SketchStroke> template)
        {
            List <List <int> > result = new List <List <int> >();

            List <KeyValuePair <SketchStroke, int> > templateStrokeToIndex = new List <KeyValuePair <SketchStroke, int> >();

            for (int i = 0; i < template.Count; i++)
            {
                templateStrokeToIndex.Add(new KeyValuePair <SketchStroke, int>(template[i], i));
            }

            foreach (SketchStroke sampleStroke in sample)
            {
                templateStrokeToIndex.Sort((t1, t2) => SketchTools.DirectedHausdorffDistance(t1.Key.Points, sampleStroke.Points).CompareTo(SketchTools.DirectedHausdorffDistance(t2.Key.Points, sampleStroke.Points)));

                double       currHausdorffFromSampleToTemplateList = double.MaxValue;
                SketchStroke currConcatenated = new SketchStroke();
                List <int>   currListIndices  = new List <int>();
                foreach (KeyValuePair <SketchStroke, int> pair in templateStrokeToIndex)
                {
                    SketchStroke newConcatenated = SketchStroke.ConcatenateStrokes(pair.Key, currConcatenated);
                    double       newDistance     = SketchTools.HausdorffDistance(sampleStroke, newConcatenated);
                    if (newDistance < currHausdorffFromSampleToTemplateList)
                    {
                        currHausdorffFromSampleToTemplateList = newDistance;
                        currConcatenated = newConcatenated;
                        currListIndices.Add(pair.Value);
                    }
                }

                result.Add(currListIndices);

                foreach (int currIndex in currListIndices)
                {
                    templateStrokeToIndex.Remove(new KeyValuePair <SketchStroke, int>(template[currIndex], currIndex));
                }
            }

            return(result);
        }
        /// <summary>
        /// one to one, one to many, or many to one, result is from template to sample strokes
        /// </summary>
        /// <param name="sample"></param>
        /// <param name="template"></param>
        /// <returns></returns>
        public static List <List <int>[]> StrokeToStrokeCorrespondenceDifferentCountStartFromSample(List <SketchStroke> sample, List <SketchStroke> template)
        {
            List <List <int>[]> result              = new List <List <int>[]>();
            HashSet <int>       usedSampleIndices   = new HashSet <int>();
            HashSet <int>       usedTemplateIndices = new HashSet <int>();

            List <SketchStroke> sampleNormalized   = SketchPreprocessing.Normalize(sample, 128, 500, new SketchPoint(0.0, 0.0));
            List <SketchStroke> templateNormalized = SketchPreprocessing.Normalize(template, 128, 500, new SketchPoint(0.0, 0.0));

            int si = 0;

            while (si < sample.Count && usedTemplateIndices.Count < template.Count)
            {
                Debug.WriteLine("si = " + si);

                if (usedSampleIndices.Contains(si))
                {
                    si++;
                    continue;
                }

                double oneToOneDis = double.MaxValue;
                int    matchedIdx  = -1;
                for (int ti = 0; ti < template.Count; ti++)
                {
                    if (usedTemplateIndices.Contains(ti))
                    {
                        continue;
                    }

                    double dis = SketchTools.HausdorffDistance(sampleNormalized[si], templateNormalized[ti]);

                    if (dis < oneToOneDis)
                    {
                        oneToOneDis = dis;
                        matchedIdx  = ti;
                    }
                }

                usedSampleIndices.Add(si);
                usedTemplateIndices.Add(matchedIdx);

                List <int> oneToManyIndices = new List <int>();
                oneToManyIndices.Add(matchedIdx);
                List <int> manyToOneIndices = new List <int>();
                manyToOneIndices.Add(si);

                SketchStroke currSampleStroke = sampleNormalized[si];
                double       oneToManyDis     = oneToOneDis;

                int preSample  = si - 1;
                int postSample = si + 1;
                while (preSample >= 0)
                {
                    if (usedSampleIndices.Contains(preSample))
                    {
                        break;
                    }

                    SketchStroke concatenatedStroke = SketchStroke.ConcatenateStrokes(sampleNormalized[preSample], currSampleStroke);
                    double       dis = SketchTools.HausdorffDistance(concatenatedStroke, templateNormalized[matchedIdx]);

                    if (dis < oneToManyDis)
                    {
                        oneToManyDis = dis;
                        oneToManyIndices.Insert(0, preSample);
                        currSampleStroke = concatenatedStroke;
                    }
                    else
                    {
                        break;
                    }

                    preSample--;
                }
                while (postSample < sample.Count)
                {
                    if (usedSampleIndices.Contains(postSample))
                    {
                        break;
                    }

                    SketchStroke concatenatedStroke = SketchStroke.ConcatenateStrokes(currSampleStroke, sampleNormalized[postSample]);
                    double       dis = SketchTools.HausdorffDistance(concatenatedStroke, templateNormalized[matchedIdx]);

                    if (dis < oneToManyDis)
                    {
                        oneToManyDis = dis;
                        oneToManyIndices.Add(postSample);
                        currSampleStroke = concatenatedStroke;
                    }
                    else
                    {
                        break;
                    }

                    postSample++;
                }

                SketchStroke currTemplateStroke = templateNormalized[matchedIdx];
                double       manyToOneDis       = oneToOneDis;

                int preTemplate  = matchedIdx - 1;
                int postTemplate = matchedIdx + 1;
                while (preTemplate >= 0)
                {
                    if (usedTemplateIndices.Contains(preTemplate))
                    {
                        break;
                    }

                    SketchStroke concatenatedStroke = SketchStroke.ConcatenateStrokes(templateNormalized[preTemplate], currTemplateStroke);
                    double       dis = SketchTools.HausdorffDistance(concatenatedStroke, sampleNormalized[si]);

                    if (dis < manyToOneDis)
                    {
                        manyToOneDis = dis;
                        manyToOneIndices.Insert(0, preTemplate);
                        currTemplateStroke = concatenatedStroke;
                    }
                    else
                    {
                        break;
                    }

                    preTemplate--;
                }
                while (postTemplate < template.Count)
                {
                    if (usedTemplateIndices.Contains(postTemplate))
                    {
                        break;
                    }

                    SketchStroke concatenatedStroke = SketchStroke.ConcatenateStrokes(currTemplateStroke, templateNormalized[postTemplate]);
                    double       dis = SketchTools.HausdorffDistance(concatenatedStroke, sampleNormalized[si]);

                    if (dis < manyToOneDis)
                    {
                        manyToOneDis = dis;
                        manyToOneIndices.Add(postTemplate);
                        currTemplateStroke = concatenatedStroke;
                    }
                    else
                    {
                        break;
                    }

                    postTemplate++;
                }

                Debug.WriteLine("matched index = " + matchedIdx);

                int flag = -2; // -1 for oneToMany, 0 for oneToOne, 1 for manyToOne
                if (oneToManyDis < oneToOneDis && manyToOneDis < oneToOneDis)
                {
                    if (oneToManyDis < manyToOneDis)
                    {
                        flag = -1;
                    }
                    else
                    {
                        flag = 1;
                    }
                }
                else if (oneToManyDis < oneToOneDis)
                {
                    flag = -1;
                }
                else if (manyToOneDis < oneToOneDis)
                {
                    flag = 1;
                }
                else
                {
                    flag = 0;
                }

                if (flag == 0)
                {
                    List <int> oneTemplate = new List <int>();
                    List <int> oneSample   = new List <int>();
                    oneTemplate.Add(matchedIdx);
                    oneSample.Add(si);
                    result.Add(new List <int>[] { oneTemplate, oneSample });
                    usedTemplateIndices.Add(matchedIdx);
                    usedSampleIndices.Add(matchedIdx);
                    si++;
                }
                else if (flag == -1) // one to many
                {
                    List <int> one  = new List <int>();
                    List <int> many = new List <int>();
                    one.Add(matchedIdx);
                    usedTemplateIndices.Add(matchedIdx);
                    foreach (int index in oneToManyIndices)
                    {
                        usedSampleIndices.Add(index);
                        many.Add(index);
                    }
                    result.Add(new List <int>[] { one, many });
                    si = many[many.Count - 1] + 1;
                }
                else if (flag == 1) // many to one
                {
                    List <int> many = new List <int>();
                    List <int> one  = new List <int>();
                    foreach (int index in manyToOneIndices)
                    {
                        usedTemplateIndices.Add(index);
                        many.Add(index);
                    }
                    one.Add(si);
                    result.Add(new List <int>[] { many, one });
                    usedSampleIndices.Add(matchedIdx);
                    si++;
                }
            }

            return(result);
        }