protected MSCompactErrorShortcutSet FindShortcutsInDirection(MSShortcutChecker checker, bool forward)
        {
            var input  = checker.Input;
            var output = checker.Output;

            checker.Forward = forward;

            var trajectory = input.Trajectory;
            var shortcuts  = new MSCompactErrorShortcutSet(input, ShortcutSetFactory);

            output.LogLine("Starting calculations of shortcuts, " + (forward ? "forward" : "backwards"));

            Func <int, int>  step;
            int              startI;
            Func <int, bool> conditionI;
            Func <int, bool> conditionJ;

            if (forward)
            {
                step       = i => i + 1;
                startI     = 0;
                conditionI = i => i < trajectory.Count - 2;
                conditionJ = j => j < trajectory.Count;
            }
            else
            {
                step       = i => i - 1;
                startI     = trajectory.Count - 1;
                conditionI = i => i >= 2;
                conditionJ = j => j >= 0;
            }

            TPoint2D intervalLimit = null;
            LinkedListNode <TPoint2D> intervalLimitNode = null;

            if (input.SearchIntervals != null)
            {
                intervalLimitNode = forward ? input.SearchIntervals.First.Next : input.SearchIntervals.Last.Previous;
                intervalLimit     = intervalLimitNode.Value;
            }

            var shortcutStartsHandled = 0;

            for (var i = startI; conditionI(i); i = step(i))
            {
                var pointI = trajectory[i];

                if (input.PrunedPoints.Contains(pointI))
                {
                    continue;
                }

                if (input.SearchIntervals != null)
                {
                    if (intervalLimit.Index == pointI.Index)
                    {
                        intervalLimitNode = forward ? intervalLimitNode.Next : intervalLimitNode.Previous;
                        intervalLimit     = intervalLimitNode.Value;
                    }
                }

                checker.OnNewShortcutStart(pointI);

                for (var j = step(i); conditionJ(j); j = step(j))
                {
                    var pointJ = trajectory[j];

                    if (input.SearchIntervals != null)
                    {
                        if (forward && pointJ.Index > intervalLimit.Index || !forward && pointJ.Index < intervalLimit.Index)
                        {
                            break;
                        }
                    }

                    checker.BeforeShortcut(pointI, pointJ);

                    //only continue when considering real shortcuts
                    if (Math.Abs(j - i) > 1 && !input.PrunedPoints.Contains(pointJ))
                    {
                        checker.BeforeShortcutValidation(pointI, pointJ);

                        var start    = forward ? pointI : pointJ;
                        var end      = forward ? pointJ : pointI;
                        var shortcut = new Shortcut(start, end);

                        var maxError = checker.GetMaxError(pointI, pointJ);

                        shortcuts.Add(shortcut, maxError);
                    }

                    if (!checker.AfterShortcut(pointI, pointJ))
                    {
                        break;
                    }
                }

                shortcutStartsHandled++;

                if (shortcutStartsHandled >= trajectory.Count / 100)
                {
                    var progress = forward ? i : trajectory.Count - i;
                    output.LogLine("Shortcuts handled: " + progress * 100 / trajectory.Count + "%");
                    shortcutStartsHandled = 0;
                }
            }

            return(shortcuts);
        }
        private MSSimpleShortcutSet FindShortcutsInDirection(MSShortcutChecker checker, bool forward)
        {
            var input      = checker.Input;
            var trajectory = input.Trajectory;

            checker.Forward = forward;

            var shortcutSet = new MSSimpleShortcutSet(input, ShortcutSetFactory);

            Func <int, int>  step;
            int              startI;
            Func <int, bool> conditionI;
            Func <int, bool> conditionJ;

            if (forward)
            {
                step       = i => i + 1;
                startI     = 0;
                conditionI = i => i < trajectory.Count - 2;
                conditionJ = j => j < trajectory.Count;
            }
            else
            {
                step       = i => i - 1;
                startI     = trajectory.Count - 1;
                conditionI = i => i >= 2;
                conditionJ = j => j >= 0;
            }

            TPoint2D intervalLimit = null;
            LinkedListNode <TPoint2D> intervalLimitNode = null;

            if (input.SearchIntervals != null)
            {
                intervalLimitNode = forward ? input.SearchIntervals.First.Next : input.SearchIntervals.Last.Previous;
                intervalLimit     = intervalLimitNode.Value;
            }

            var shortcutStartsHandled = 0;

            for (var i = startI; conditionI(i); i = step(i))
            {
                var pointI = trajectory[i];

                if (input.PrunedPoints.Contains(pointI))
                {
                    continue;
                }

                if (input.SearchIntervals != null)
                {
                    if (intervalLimit.Index == pointI.Index)
                    {
                        intervalLimitNode = forward ? intervalLimitNode.Next : intervalLimitNode.Previous;
                        intervalLimit     = intervalLimitNode.Value;
                    }
                }

                checker.OnNewShortcutStart(pointI);

                for (var j = step(i); conditionJ(j); j = step(j))
                {
                    var pointJ = trajectory[j];

                    if (input.SearchIntervals != null)
                    {
                        if (forward && pointJ.Index > intervalLimit.Index || !forward && pointJ.Index < intervalLimit.Index)
                        {
                            break;
                        }
                    }

                    checker.BeforeShortcut(pointI, pointJ);

                    if (Math.Abs(j - i) > 1 && !input.PrunedPoints.Contains(pointJ))
                    {
                        checker.BeforeShortcutValidation(pointI, pointJ);

                        var shortcutValid = false;
                        for (var level = 1; level <= input.NumLevels; level++)
                        {
                            if (!shortcutValid)
                            {
                                shortcutValid = checker.ShortcutValid(level, pointI, pointJ);
                            }

                            if (!shortcutValid)
                            {
                                continue;
                            }

                            var start = forward ? pointI : pointJ;
                            var end   = forward ? pointJ : pointI;

                            if (forward)
                            {
                                shortcutSet.Shortcuts[level].AppendShortcut(start, end);
                            }
                            else
                            {
                                shortcutSet.Shortcuts[level].PrependShortcut(start, end);
                            }
                        }
                    }

                    if (!checker.AfterShortcut(pointI, pointJ))
                    {
                        break;
                    }
                }

                shortcutStartsHandled++;

                if (shortcutStartsHandled >= trajectory.Count / 100)
                {
                    var progress = forward ? i : trajectory.Count - i;
                    //System.Diagnostics.Debug.WriteLine("Shortcuts handled: " + progress * 100 / trajectory.Count + "%");
                    checker.Output.LogLine("Shortcuts handled: " + progress * 100 / trajectory.Count + "%");
                    shortcutStartsHandled = 0;
                }
            }
            return(shortcutSet);
        }