/// <summary>
        /// Simulates the specified joints.
        /// </summary>
        /// <param name="joints">The joints.</param>
        /// <param name="links">The links.</param>
        /// <param name="Forward">The forward.</param>
        private void Simulate(List <Joint> joints, List <Link> links, Boolean Forward)
        {
            var timeStep          = (Forward == (InputSpeed > 0)) ? FixedTimeStep : -FixedTimeStep;
            var startingPosChange = (Forward == (InputSpeed > 0))
                ? Constants.DefaultStepSize
                : -Constants.DefaultStepSize;

            if (InputJoint.TypeOfJoint == JointType.P)
            {
                startingPosChange *= AverageLength;
            }
            var     maxLengthError = MaxSmoothingError * AverageLength;
            var     currentTime    = 0.0;
            Boolean validPosition;
            var     posFinder = new PositionFinder(joints, links, gearsData, simulationDriveIndex);
            var     velSolver = new VelocitySolver(joints, links, FirstInputJointIndex, simulationDriveIndex, InputLinkIndex,
                                                   InputSpeed, gearsData, AverageLength);
            var accelSolver = new AccelerationSolver(joints, links, FirstInputJointIndex, simulationDriveIndex,
                                                     InputLinkIndex, InputSpeed, gearsData, AverageLength);

            do
            {
                #region Find Next Positions

                if (useErrorMethod)
                {
                    var    k = 0;
                    double upperError;
                    do
                    {
                        timeStep = startingPosChange / InputSpeed;
                        NumericalPosition(timeStep, joints, links);
                        validPosition = posFinder.DefineNewPositions(startingPosChange, ref IsDyadic);
                        upperError    = posFinder.PositionError - maxLengthError;
                        if (validPosition && upperError < 0)
                        {
                            startingPosChange *= Constants.ErrorSizeIncrease;
                            // startingPosChange = startingPosChange * maxLengthError / (maxLengthError + upperError);
                        }
                        else
                        {
                            if (Math.Abs(startingPosChange * Constants.ConservativeErrorEstimation * 0.5) <
                                Constants.MinimumStepSize)
                            {
                                validPosition = false;
                            }
                            else
                            {
                                startingPosChange *= Constants.ConservativeErrorEstimation * 0.5;
                            }
                        }
                    } while ((!validPosition || upperError > 0) && k++ < Constants.MaxItersInPositionError
                             &&
                             (Math.Abs(startingPosChange * Constants.ConservativeErrorEstimation * 0.5) >=
                              Constants.MinimumStepSize));
                    //var tempStep = startingPosChange;
                    //startingPosChange = (Constants.ErrorEstimateInertia * prevStep + startingPosChange) / (1 + Constants.ErrorEstimateInertia);
                    //prevStep = tempStep;
                }
                else
                {
                    // this next function puts the xNumerical and yNumerical values in the joints
                    NumericalPosition(timeStep, joints, links);
                    var delta = InputSpeed * timeStep;
                    // this next function puts the x and y values in the joints
                    validPosition = posFinder.DefineNewPositions(delta, ref IsDyadic);
                }

                #endregion

                if (validPosition)
                {
                    if (Forward == (InputSpeed > 0))
                    {
                        lock (InputRange)
                        {
                            InputRange[1] = links[InputLinkIndex].Angle;
                        }
                    }
                    else
                    {
                        lock (InputRange)
                        {
                            InputRange[0] = links[InputLinkIndex].Angle;
                        }
                    }

                    #region Find Velocities for Current Position

                    // this next functions puts the vx and vy values as well as the vx_unit and vy_unit in the joints
                    if (!velSolver.Solve())
                    {
                        Status += "Instant Centers could not be found at" + currentTime + ".";
                        NumericalVelocity(timeStep, joints, links);
                    }

                    #endregion

                    #region Find Accelerations for Current Position

                    // this next functions puts the ax and ay values in the joints
                    if (!accelSolver.Solve())
                    {
                        Status += "Analytical acceleration could not be found at" + currentTime + ".";
                        NumericalAcceleration(timeStep, joints, links);
                    }

                    #endregion

                    currentTime += timeStep;
                    var jointParams = WriteJointStatesVariablesToMatrixAndToLast(joints);
                    var linkParams  = WriteLinkStatesVariablesToMatrixAndToLast(links);
                    if (Forward == (InputSpeed > 0))
                    {
                        lock (JointParameters)
                            JointParameters.AddNearEnd(currentTime, jointParams);
                        lock (LinkParameters)
                            LinkParameters.AddNearEnd(currentTime, linkParams);
                    }
                    else
                    {
                        lock (JointParameters)
                            JointParameters.AddNearBegin(currentTime, jointParams);
                        lock (LinkParameters)
                            LinkParameters.AddNearBegin(currentTime, linkParams);
                    }
                }
            } while (validPosition && LessThanFullRotation());
        }
        /// <summary>
        /// Defines the movement characteristics.
        /// </summary>
        private void DefineMovementCharacteristics()
        {
            StartTime = JointParameters.Times[0];
            EndTime   = JointParameters.Times.Last();
            InitializeQueryVars();
            for (var i = 0; i < NumAllJoints; i++)
            {
                var j = SimulationJoints[i];
                if (j.TypeOfJoint == JointType.R)
                {
                    continue;
                }
                j.OrigSlidePosition = JointParameters[0.0][oIOSJ[i], 6];
                j.MinSlidePosition  = JointParameters.Parameters.Min(jp => jp[oIOSJ[i], 6]);
                j.MaxSlidePosition  = JointParameters.Parameters.Max(jp => jp[oIOSJ[i], 6]);
            }
            if (LessThanFullRotation())
            {
                //if the crank input couldn't rotate all the way around,then this is easy...
                CycleType = CycleTypes.LessThanFullCycle;
                return;
            }
            // if the simulation did go all the way around then, we should be careful to ensure is connects correctly.
            var cyclePeriodTime = Constants.FullCircle / Math.Abs(InputSpeed);

            if (DoesMechanismRepeatOnCrankCycle(cyclePeriodTime))
            {
                StartTime = 0.0;
                EndTime   = cyclePeriodTime;
                CycleType = CycleTypes.OneCycle;
                while (JointParameters.Times.Last() >= EndTime)
                {
                    var time       = JointParameters.Times.Last();
                    var parameters = JointParameters.Parameters.Last();
                    JointParameters.RemoveAt(JointParameters.LastIndex);
                    time -= cyclePeriodTime;
                    JointParameters.AddNearBegin(time, parameters);

                    parameters = LinkParameters.Parameters.Last();
                    LinkParameters.RemoveAt(LinkParameters.LastIndex);
                    LinkParameters.AddNearEnd(time, parameters);
                }
                while (JointParameters.Times[0] < 0.0)
                {
                    var time       = JointParameters.Times[0];
                    var parameters = JointParameters.Parameters[0];
                    JointParameters.RemoveAt(0);
                    time += cyclePeriodTime;
                    JointParameters.AddNearEnd(time, parameters);

                    parameters = LinkParameters.Parameters[0];
                    LinkParameters.RemoveAt(0);
                    LinkParameters.AddNearEnd(time, parameters);
                }
            }
            else
            {
                CycleType = CycleTypes.MoreThanOneCycle;
            }
            InitializeQueryVars();
        }