FingerMap getTreeNodeFingerMap(TreeNode node)
        {
            FingerMap map = node.parent != null?getTreeNodeFingerMap(node.parent) : new FingerMap();

            if (node.Choice >= 0)
            {
                FingerChord fc = ChoiceSequence[node.Index][node.Choice].chord;
                NoteChord   nc = NoteSeq[node.Index];
                map[nc.tick] = fc;
            }

            return(map);
        }
        public Fingering run()
        {
            var begin = DateTime.Now;

            generateChoiceSequence();

            var choiceEnd = DateTime.Now;

            UnityEngine.Debug.LogFormat("Choice generation cost: {0:F4}", choiceEnd.Subtract(begin).TotalMilliseconds / 1000f);

            TreeNode.s_BenchmarkDuration = benchmarkDuration;
            TreeNode.s_HandConfig        = Config;
            TreeNode.s_ChoiceSequence    = ChoiceSequence;
            TreeNode.s_NoteSeq           = NoteSeq;

            TreeRoot   = new TreeNode();
            TreeLeaves = new LeafSequence();
            TreeLeaves.Add(TreeRoot);

            ResultNodes = new List <TreeNode>();

            currentNode   = TreeRoot;
            candidateNode = TreeRoot;

            for (int i = 0; i < MaxStepCount; ++i)
            {
                if (TreeLeaves.Count == 0)
                {
                    break;
                }

                if (i >= MinStepCount && ResultNodes.Count > 0)
                {
                    break;
                }

                currentStep = i;

                step();

#if UNITY_EDITOR
                {
                    const int lenMax = 70;

                    string info = string.Format("Analyzing: {2}/{3}, {0}, {1}", Math.Round(currentNode.CommittedCost, 4), currentNode.ChoicePathDescription, currentNode.Index, NoteSeq.Length);
                    if (info.Length > lenMax)
                    {
                        info = info.Substring(0, lenMax - 9) + "..." + info.Substring(info.Length - 6, 6);
                    }

                    if (UnityEditor.EditorUtility.DisplayCancelableProgressBar("FingeringNavigator running", info, (float)i / MaxStepCount))
                    {
                        break;
                    }
                }
#endif
            }

            var stepEnd = DateTime.Now;

            ResultNodes.Sort(delegate(TreeNode node1, TreeNode node2)
            {
                double cost1 = node1.CommittedCost;
                double cost2 = node2.CommittedCost;

                return(cost1.CompareTo(cost2));
            });

            TreeNode resultNode = ResultNodes.Count > 0 ? ResultNodes[0] : candidateNode;

            Fingering result = new Fingering();
            result.markers = new Fingering.Marker[SourceTrack.notes.Length];

            // generate markers
            FingerMap map = getTreeNodeFingerMap(resultNode);
            for (int i = 0; i < SourceTrack.notes.Length; ++i)
            {
                var note = SourceTrack.notes[i];

                Finger finger = map.ContainsKey(note.tick) && map[note.tick].ContainsKey(note.pitch) ? map[note.tick][note.pitch] : Finger.EMPTY;

                result.markers[i] = new Fingering.Marker {
                    tick = note.tick, time = note.start, pitch = note.pitch, finger = finger
                };
            }

            var end = DateTime.Now;
            UnityEngine.Debug.LogFormat("Total cost: {0:F2}s, per step: {1:F4}s", end.Subtract(begin).TotalMilliseconds / 1000f, (stepEnd.Subtract(choiceEnd).TotalMilliseconds / 1000f) / currentStep);

#if UNITY_EDITOR
            UnityEditor.EditorUtility.ClearProgressBar();
#endif

            return(result);
        }