protected double CalculateDifficulty()
        {
            double ActualStrainStep = STRAIN_STEP * TimeRate;

            // Find the highest strain value within each strain step
            List <double> HighestStrains  = new List <double>();
            double        IntervalEndTime = ActualStrainStep;
            double        MaximumStrain   = 0; // We need to keep track of the maximum strain in the current interval

            DifficultyHitObjectTaiko PreviousHitObject = null;

            foreach (DifficultyHitObjectTaiko hitObject in DifficultyHitObjects)
            {
                // While we are beyond the current interval push the currently available maximum to our strain list
                while (hitObject.BaseHitObject.StartTime > IntervalEndTime)
                {
                    HighestStrains.Add(MaximumStrain);

                    // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
                    // until the beginning of the next interval.
                    if (PreviousHitObject == null)
                    {
                        MaximumStrain = 0;
                    }
                    else
                    {
                        double Decay = Math.Pow(DifficultyHitObjectTaiko.DECAY_BASE, (double)(IntervalEndTime - PreviousHitObject.BaseHitObject.StartTime) / 1000);
                        MaximumStrain = PreviousHitObject.Strain * Decay;
                    }

                    // Go to the next time interval
                    IntervalEndTime += ActualStrainStep;
                }

                // Obtain maximum strain
                if (hitObject.Strain > MaximumStrain)
                {
                    MaximumStrain = hitObject.Strain;
                }

                PreviousHitObject = hitObject;
            }

            // Build the weighted sum over the highest strains for each interval
            double Difficulty = 0;
            double Weight     = 1;

            HighestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.

            foreach (double Strain in HighestStrains)
            {
                Difficulty += Weight * Strain;
                Weight     *= DECAY_WEIGHT;
            }

            return(Difficulty);
        }
        protected bool CalculateStrainValues()
        {
            // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
            List <DifficultyHitObjectTaiko> .Enumerator HitObjectsEnumerator = DifficultyHitObjects.GetEnumerator();
            if (HitObjectsEnumerator.MoveNext() == false)
            {
                return(false);
            }

            DifficultyHitObjectTaiko CurrentHitObject = HitObjectsEnumerator.Current;
            DifficultyHitObjectTaiko NextHitObject;

            // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.

            while (HitObjectsEnumerator.MoveNext())
            {
                NextHitObject = HitObjectsEnumerator.Current;
                NextHitObject.CalculateStrains(CurrentHitObject, TimeRate);
                CurrentHitObject = NextHitObject;
            }

            return(true);
        }