private static bool Equal(RelativeTone left, RelativeTone right)
 {
     return(ToneEqual(left, right) &&
            left.CycleTo == right.CycleTo &&
            left.CycleCount == right.CycleCount &&
            left.Next == right.Next);
 }
        private static RelativeTone[] Relative(Span <MediaToneMessage.Tone> tones)
        {
            var result = new RelativeTone[tones.Length];

            for (int i = 0; i < tones.Length; i++)
            {
                result[i] = new RelativeTone(tones[i],
                                             tones[i].CycleCount,
                                             tones[i].CycleCount == 0 ? 0 : tones[i].CycleTo - i,
                                             (tones[i].Next - i));
            }
            return(result);
        }
        private RelativeTone[] Limit(IndexedTone[] tones)
        {
            var indexed = tones.AsMemory(0, _limit);

            while (HasCycleIntoBoundary(indexed, indexed.Length + 1, indexed.Length + 1))
            {
                indexed = indexed.Slice(0, indexed.Length - 2);
            }
            var result = new RelativeTone[indexed.Length];

            for (int i = 0; i < indexed.Length; i++)
            {
                result[i] = indexed.Span[i].Tone;
            }
            return(result);
        }
 private static bool ToneEqual(RelativeTone left, RelativeTone right)
 {
     if (left == null)
     {
         return(false);
     }
     if (right == null)
     {
         return(false);
     }
     return(left.CB1 == right.CB1 &&
            left.CB2 == right.CB2 &&
            left.CB3 == right.CB3 &&
            left.CB4 == right.CB4 &&
            left.Frequency1 == right.Frequency1 &&
            left.Frequency2 == right.Frequency2 &&
            left.Frequency3 == right.Frequency3 &&
            left.Frequency4 == right.Frequency4 &&
            left.Duration == right.Duration);
 }
        private static RelativeTone[] Merge(RelativeTone[] tones)
        {
            RelativeTone last   = tones[0];
            var          result = new List <RelativeTone> {
                last
            };

            for (int i = 1; i < tones.Length; i++)
            {
                if (Equal(last, tones[i]))
                {
                    //merge
                    last.Duration += tones[i].Duration;
                }
                else
                {
                    last = tones[i];
                    result.Add(last);
                }
            }
            return(result.ToArray());
        }
        private static RelativeTone[] ReplaceMatch(List <SequenceMatch> matches, IndexedTone[] indexed)
        {
            foreach (var match in matches)
            {
                if (!CanBeReplaced(match, indexed, out var adjustBeforeCycles, out var adjustAfterCycles, out var cycleIntoBefore))
                {
                    continue;
                }
                var leftEnd = match.Left.Span[match.Length - 1];
                if (leftEnd.Tone.Next != 1)
                {
                    throw new NotImplementedException();
                }
                if (leftEnd.Tone.CycleCount != 0)
                {
                    if (!match.IsGapless)
                    {
                        throw new NotImplementedException();
                    }
                }

                var rightEnd = match.Right.Span[match.Length - 1];
                if (rightEnd.Tone.Next != 1 || rightEnd.Tone.CycleCount != 0)
                {
                    throw new NotImplementedException();
                }

                var leftStart = match.Left.Span[0];
                if (leftStart.Tone.Next != 1 || leftStart.Tone.CycleCount != 0)
                {
                    throw new NotImplementedException();
                }

                var rightStart = match.Right.Span[0];
                if (rightStart.Tone.Next != 1 || rightStart.Tone.CycleCount != 0)
                {
                    throw new NotImplementedException();
                }

                var before = indexed.AsMemory(0, match.Start);
                var after  = indexed.AsMemory(match.End + 1);
                Memory <IndexedTone> between;
                if (match.IsGapless)
                {
                    between = Memory <IndexedTone> .Empty;
                    leftEnd.Tone.CycleTo = 1 - match.Length;
                    leftEnd.Tone.CycleCount++;
                }
                else
                {
                    between = indexed.AsMemory(match.Start + match.Length, match.Between);
                    leftEnd.Tone.CycleTo    = 1;
                    leftEnd.Tone.CycleCount = 1;
                    leftEnd.Tone.Next       = between.Length + 1;
                    var lastBetween = between.Span[between.Length - 1];
                    lastBetween.Tone.CycleTo = 1; //marker
                    lastBetween.Tone.Next    = 1 - between.Length - match.Length;
                }
                var result = new List <RelativeTone>();
                foreach (var tone in before.Span)
                {
                    if (adjustBeforeCycles)
                    {
                        var next = tone.Tone.Next + tone.Index;
                        if (next > before.Length)
                        {
                            tone.Tone.Next -= match.Length;
                        }
                        var cycle = tone.Tone.CycleTo + tone.Index;
                        if (cycle > before.Length)
                        {
                            tone.Tone.CycleTo -= match.Length;
                        }
                    }
                    result.Add(tone.Tone);
                }
                foreach (var tone in match.Left.Span)
                {
                    result.Add(tone.Tone);
                }
                if (cycleIntoBefore)
                {
                    var injected = new RelativeTone(leftEnd.Tone.Tone(), 1, -match.Length, 1 + between.Length);
                    leftEnd.Tone.Next++;
                    var beforeLeftEnd = match.Left.Span[match.Length - 2];
                    beforeLeftEnd.Tone.CycleCount = 2;
                    beforeLeftEnd.Tone.CycleTo    = 2;
                    if (!match.IsGapless)
                    {
                        injected.CycleTo    = 1;                  //between
                        injected.CycleCount = 1;
                        injected.Next       = 1 + between.Length; //after between
                        var lastBetween = between.Span[between.Length - 1];
                        lastBetween.Tone.Next--;
                        leftEnd.Tone.CycleTo++;
                    }
                    result.Add(injected);
                }
                foreach (var tone in between.Span)
                {
                    result.Add(tone.Tone);
                }
                foreach (var tone in after.Span)
                {
                    if (adjustAfterCycles)
                    {
                        var next = tone.Tone.Next + tone.Index;
                        if (next < before.Length)
                        {
                            //pointed to before
                            if (cycleIntoBefore)
                            {
                                tone.Tone.Next += match.Length - 1;
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                        else if (next < before.Length + match.Length + between.Length)
                        {
                            //pointed to between
                            tone.Tone.Next += match.Length;
                        }
                        else if (next < before.Length + 2 * match.Length + between.Length)
                        {
                            //pointed to right
                            tone.Tone.Next -= between.Length;
                        }
                        var cycle = tone.Tone.CycleTo + tone.Index;
                        if (cycle < before.Length)
                        {
                            //pointed to before
                            if (cycleIntoBefore)
                            {
                                tone.Tone.CycleTo += match.Length - 1;
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                        else if (cycle < before.Length + match.Length + between.Length)
                        {
                            //pointed to between
                            tone.Tone.CycleTo += match.Length;
                        }
                        else if (cycle < before.Length + 2 * match.Length + between.Length)
                        {
                            //pointed to right
                            tone.Tone.CycleTo -= between.Length;
                        }
                    }
                    result.Add(tone.Tone);
                }
                return(result.ToArray());
            }
            return(null);
        }