diff --git a/Sequences/SequencesOptions.cs b/Sequences/SequencesOptions.cs
new file mode 100644
index 000000000..7e72c2501
--- /dev/null
+++ b/Sequences/SequencesOptions.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.CompilerServices;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Basic options for Sequences - minimal implementation for refactoring purposes.
+ ///
+ public class SequencesOptions
+ {
+ public bool UseSequenceMarker { get; set; } = false;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ValidateOptions()
+ {
+ // Basic validation - placeholder
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void InitOptions(object links)
+ {
+ // Basic initialization - placeholder
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/ISequenceMatcher.cs b/csharp/Platform.Data.Doublets/Sequences/ISequenceMatcher.cs
new file mode 100644
index 000000000..b2d072cc5
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/ISequenceMatcher.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Interface for sequence matching operations.
+ ///
+ public interface ISequenceMatcher
+ {
+ bool Match(LinkIndex sequenceToMatch);
+ void AddMatchedToResults(IList restrictions);
+ LinkIndex HandleMatched(IList restrictions);
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/Sequences.cs b/csharp/Platform.Data.Doublets/Sequences/Sequences.cs
new file mode 100644
index 000000000..8588daf64
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/Sequences.cs
@@ -0,0 +1,633 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Platform.Collections;
+using Platform.Collections.Lists;
+using Platform.Collections.Stacks;
+using Platform.Threading.Synchronization;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Представляет коллекцию последовательностей связей.
+ ///
+ ///
+ /// Обязательно реализовать атомарность каждого публичного метода.
+ ///
+ /// TODO:
+ ///
+ /// !!! Повышение вероятности повторного использования групп (подпоследовательностей),
+ /// через естественную группировку по unicode типам, все whitespace вместе, все символы вместе, все числа вместе и т.п.
+ /// + использовать ровно сбалансированный вариант, чтобы уменьшать вложенность (глубину графа)
+ ///
+ /// x*y - найти все связи между, в последовательностях любой формы, если не стоит ограничитель на то, что является последовательностью, а что нет,
+ /// то находятся любые структуры связей, которые содержат эти элементы именно в таком порядке.
+ ///
+ /// Рост последовательности слева и справа.
+ /// Поиск со звёздочкой.
+ /// URL, PURL - реестр используемых во вне ссылок на ресурсы,
+ /// так же проблема может быть решена при реализации дистанционных триггеров.
+ /// Нужны ли уникальные указатели вообще?
+ /// Что если обращение к информации будет происходить через содержимое всегда?
+ ///
+ /// Писать тесты.
+ ///
+ ///
+ /// Можно убрать зависимость от конкретной реализации Links,
+ /// на зависимость от абстрактного элемента, который может быть представлен несколькими способами.
+ ///
+ /// Можно ли как-то сделать один общий интерфейс
+ ///
+ ///
+ /// Блокчейн и/или гит для распределённой записи транзакций.
+ ///
+ ///
+ public partial class Sequences : ILinks // IList, IList (после завершения реализации Sequences)
+ {
+ /// Возвращает значение LinkIndex, обозначающее любое количество связей.
+ public const LinkIndex ZeroOrMany = LinkIndex.MaxValue;
+
+ public SequencesOptions Options { get; }
+ public SynchronizedLinks Links { get; }
+ private readonly ISynchronization _sync;
+
+ public LinksConstants Constants { get; }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Sequences(SynchronizedLinks links, SequencesOptions options)
+ {
+ Links = links;
+ _sync = links.SyncRoot;
+ Options = options;
+ Options.ValidateOptions();
+ Options.InitOptions(Links);
+ Constants = links.Constants;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Sequences(SynchronizedLinks links) : this(links, new SequencesOptions()) { }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsSequence(LinkIndex sequence)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Options.MarkedSequenceMatcher.IsMatched(sequence);
+ }
+ return !Links.Unsync.IsPartialPoint(sequence);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex GetSequenceByElements(LinkIndex sequence)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Links.SearchOrDefault(Options.SequenceMarkerLink, sequence);
+ }
+ return sequence;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex GetSequenceElements(LinkIndex sequence)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var linkContents = new Link(Links.GetLink(sequence));
+ if (linkContents.Source == Options.SequenceMarkerLink)
+ {
+ return linkContents.Target;
+ }
+ if (linkContents.Target == Options.SequenceMarkerLink)
+ {
+ return linkContents.Source;
+ }
+ }
+ return sequence;
+ }
+
+ #region Count
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Count(IList restrictions)
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Links.Count(Constants.Any, Options.SequenceMarkerLink, Constants.Any);
+ }
+ if (restrictions.Count == 1) // Первая связь это адрес
+ {
+ var sequenceIndex = restrictions[0];
+ if (sequenceIndex == Constants.Null)
+ {
+ return 0;
+ }
+ if (sequenceIndex == Constants.Any)
+ {
+ return Count(null);
+ }
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Count(Constants.Any, Options.SequenceMarkerLink, sequenceIndex);
+ }
+ return Links.Exists(sequenceIndex) ? 1UL : 0;
+ }
+ throw new NotImplementedException();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CountUsages(params LinkIndex[] restrictions)
+ {
+ if (restrictions.Length == 0)
+ {
+ return 0;
+ }
+ if (restrictions.Length == 1) // Первая связь это адрес
+ {
+ if (restrictions[0] == Constants.Null)
+ {
+ return 0;
+ }
+ var any = Constants.Any;
+ if (Options.UseSequenceMarker)
+ {
+ var elementsLink = GetSequenceElements(restrictions[0]);
+ var sequenceLink = GetSequenceByElements(elementsLink);
+ if (sequenceLink != Constants.Null)
+ {
+ return Links.Count(any, sequenceLink) + Links.Count(any, elementsLink) - 1;
+ }
+ return Links.Count(any, elementsLink);
+ }
+ return Links.Count(any, restrictions[0]);
+ }
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region Create
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Create(IList restrictions)
+ {
+ return _sync.ExecuteWriteOperation(() =>
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ Links.EnsureInnerReferenceExists(restrictions, nameof(restrictions));
+ return CreateCore(restrictions);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CreateCore(IList restrictions)
+ {
+ LinkIndex[] sequence = restrictions.SkipFirst();
+ if (Options.UseIndex)
+ {
+ Options.Index.Add(sequence);
+ }
+ var sequenceRoot = default(LinkIndex);
+ if (Options.EnforceSingleSequenceVersionOnWriteBasedOnExisting)
+ {
+ var matches = Each(restrictions);
+ if (matches.Count > 0)
+ {
+ sequenceRoot = matches[0];
+ }
+ }
+ else if (Options.EnforceSingleSequenceVersionOnWriteBasedOnNew)
+ {
+ return CompactCore(sequence);
+ }
+ if (sequenceRoot == default)
+ {
+ sequenceRoot = Options.LinksToSequenceConverter.Convert(sequence);
+ }
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Unsync.GetOrCreate(Options.SequenceMarkerLink, sequenceRoot);
+ }
+ return sequenceRoot; // Возвращаем корень последовательности (т.е. сами элементы)
+ }
+
+ #endregion
+
+ #region Each
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public List Each(IList sequence)
+ {
+ var results = new List();
+ var filler = new ListFiller(results, Constants.Continue);
+ Each(filler.AddFirstAndReturnConstant, sequence);
+ return results;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Each(Func, LinkIndex> handler, IList restrictions)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Constants.Continue;
+ }
+ Links.EnsureInnerReferenceExists(restrictions, nameof(restrictions));
+ if (restrictions.Count == 1)
+ {
+ var link = restrictions[0];
+ var any = Constants.Any;
+ if (link == any)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Unsync.Each(handler, new Link(any, Options.SequenceMarkerLink, any));
+ }
+ else
+ {
+ return Links.Unsync.Each(handler, new Link(any, any, any));
+ }
+ }
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceLinkValues = Links.Unsync.GetLink(link);
+ if (sequenceLinkValues[Constants.SourcePart] == Options.SequenceMarkerLink)
+ {
+ link = sequenceLinkValues[Constants.TargetPart];
+ }
+ }
+ var sequence = Options.Walker.Walk(link).ToArray().ShiftRight();
+ sequence[0] = link;
+ return handler(sequence);
+ }
+ else if (restrictions.Count == 2)
+ {
+ throw new NotImplementedException();
+ }
+ else if (restrictions.Count == 3)
+ {
+ return Links.Unsync.Each(handler, restrictions);
+ }
+ else
+ {
+ var sequence = restrictions.SkipFirst();
+ if (Options.UseIndex && !Options.Index.MightContain(sequence))
+ {
+ return Constants.Break;
+ }
+ return EachCore(handler, sequence);
+ }
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex EachCore(Func, LinkIndex> handler, IList values)
+ {
+ var matcher = new SimpleFullMatcher(this, values, new HashSet(), handler);
+ // TODO: Find out why matcher.HandleFullMatched executed twice for the same sequence Id.
+ Func, LinkIndex> innerHandler = Options.UseSequenceMarker ? (Func, LinkIndex>)matcher.HandleFullMatchedSequence : matcher.HandleFullMatched;
+ //if (sequence.Length >= 2)
+ if (StepRight(innerHandler, values[0], values[1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ var last = values.Count - 2;
+ for (var i = 1; i < last; i++)
+ {
+ if (PartialStepRight(innerHandler, values[i], values[i + 1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ }
+ if (values.Count >= 3)
+ {
+ if (StepLeft(innerHandler, values[values.Count - 2], values[values.Count - 1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ }
+ return Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex PartialStepRight(Func, LinkIndex> handler, LinkIndex left, LinkIndex right)
+ {
+ return Links.Unsync.Each(doublet =>
+ {
+ var doubletIndex = doublet[Constants.IndexPart];
+ if (StepRight(handler, doubletIndex, right) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ if (left != doubletIndex)
+ {
+ return PartialStepRight(handler, doubletIndex, right);
+ }
+ return Constants.Continue;
+ }, new Link(Constants.Any, Constants.Any, left));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex StepRight(Func, LinkIndex> handler, LinkIndex left, LinkIndex right) => Links.Unsync.Each(rightStep => TryStepRightUp(handler, right, rightStep[Constants.IndexPart]), new Link(Constants.Any, left, Constants.Any));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex TryStepRightUp(Func, LinkIndex> handler, LinkIndex right, LinkIndex stepFrom)
+ {
+ var upStep = stepFrom;
+ var firstSource = Links.Unsync.GetTarget(upStep);
+ while (firstSource != right && firstSource != upStep)
+ {
+ upStep = firstSource;
+ firstSource = Links.Unsync.GetSource(upStep);
+ }
+ if (firstSource == right)
+ {
+ return handler(new LinkAddress(stepFrom));
+ }
+ return Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex StepLeft(Func, LinkIndex> handler, LinkIndex left, LinkIndex right) => Links.Unsync.Each(leftStep => TryStepLeftUp(handler, left, leftStep[Constants.IndexPart]), new Link(Constants.Any, Constants.Any, right));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex TryStepLeftUp(Func, LinkIndex> handler, LinkIndex left, LinkIndex stepFrom)
+ {
+ var upStep = stepFrom;
+ var firstTarget = Links.Unsync.GetSource(upStep);
+ while (firstTarget != left && firstTarget != upStep)
+ {
+ upStep = firstTarget;
+ firstTarget = Links.Unsync.GetTarget(upStep);
+ }
+ if (firstTarget == left)
+ {
+ return handler(new LinkAddress(stepFrom));
+ }
+ return Constants.Continue;
+ }
+
+ #endregion
+
+ #region Update
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Update(IList restrictions, IList substitution)
+ {
+ var sequence = restrictions.SkipFirst();
+ var newSequence = substitution.SkipFirst();
+ if (sequence.IsNullOrEmpty() && newSequence.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ if (sequence.IsNullOrEmpty())
+ {
+ return Create(substitution);
+ }
+ if (newSequence.IsNullOrEmpty())
+ {
+ Delete(restrictions);
+ return Constants.Null;
+ }
+ return _sync.ExecuteWriteOperation((Func)(() =>
+ {
+ ILinksExtensions.EnsureLinkIsAnyOrExists(Links, (IList)sequence);
+ Links.EnsureLinkExists(newSequence);
+ return UpdateCore(sequence, newSequence);
+ }));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex UpdateCore(IList sequence, IList newSequence)
+ {
+ LinkIndex bestVariant;
+ if (Options.EnforceSingleSequenceVersionOnWriteBasedOnNew && !sequence.EqualTo(newSequence))
+ {
+ bestVariant = CompactCore(newSequence);
+ }
+ else
+ {
+ bestVariant = CreateCore(newSequence);
+ }
+ // TODO: Check all options only ones before loop execution
+ // Возможно нужно две версии Each, возвращающий фактические последовательности и с маркером,
+ // или возможно даже возвращать и тот и тот вариант. С другой стороны все варианты можно получить имея только фактические последовательности.
+ foreach (var variant in Each(sequence))
+ {
+ if (variant != bestVariant)
+ {
+ UpdateOneCore(variant, bestVariant);
+ }
+ }
+ return bestVariant;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void UpdateOneCore(LinkIndex sequence, LinkIndex newSequence)
+ {
+ if (Options.UseGarbageCollection)
+ {
+ var sequenceElements = GetSequenceElements(sequence);
+ var sequenceElementsContents = new Link(Links.GetLink(sequenceElements));
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ var newSequenceElements = GetSequenceElements(newSequence);
+ var newSequenceLink = GetSequenceByElements(newSequenceElements);
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.MergeAndDelete(sequenceLink, newSequenceLink);
+ }
+ Links.Unsync.MergeAndDelete(sequenceElements, newSequenceElements);
+ }
+ ClearGarbage(sequenceElementsContents.Source);
+ ClearGarbage(sequenceElementsContents.Target);
+ }
+ else
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceElements = GetSequenceElements(sequence);
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ var newSequenceElements = GetSequenceElements(newSequence);
+ var newSequenceLink = GetSequenceByElements(newSequenceElements);
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.MergeAndDelete(sequenceLink, newSequenceLink);
+ }
+ Links.Unsync.MergeAndDelete(sequenceElements, newSequenceElements);
+ }
+ }
+ else
+ {
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ Links.Unsync.MergeAndDelete(sequence, newSequence);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Delete
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Delete(IList restrictions)
+ {
+ _sync.ExecuteWriteOperation(() =>
+ {
+ var sequence = restrictions.SkipFirst();
+ // TODO: Check all options only ones before loop execution
+ foreach (var linkToDelete in Each(sequence))
+ {
+ DeleteOneCore(linkToDelete);
+ }
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void DeleteOneCore(LinkIndex link)
+ {
+ if (Options.UseGarbageCollection)
+ {
+ var sequenceElements = GetSequenceElements(link);
+ var sequenceElementsContents = new Link(Links.GetLink(sequenceElements));
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.Delete(sequenceLink);
+ }
+ Links.Unsync.Delete(link);
+ }
+ ClearGarbage(sequenceElementsContents.Source);
+ ClearGarbage(sequenceElementsContents.Target);
+ }
+ else
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceElements = GetSequenceElements(link);
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.Delete(sequenceLink);
+ }
+ Links.Unsync.Delete(link);
+ }
+ }
+ else
+ {
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ Links.Unsync.Delete(link);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Compactification
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CompactAll()
+ {
+ _sync.ExecuteWriteOperation(() =>
+ {
+ var sequences = Each((LinkAddress)Constants.Any);
+ for (int i = 0; i < sequences.Count; i++)
+ {
+ var sequence = this.ToList(sequences[i]);
+ Compact(sequence.ShiftRight());
+ }
+ });
+ }
+
+ ///
+ /// bestVariant можно выбирать по максимальному числу использований,
+ /// но балансированный позволяет гарантировать уникальность (если есть возможность,
+ /// гарантировать его использование в других местах).
+ ///
+ /// Получается этот метод должен игнорировать Options.EnforceSingleSequenceVersionOnWrite
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Compact(IList sequence)
+ {
+ return _sync.ExecuteWriteOperation(() =>
+ {
+ if (sequence.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ Links.EnsureInnerReferenceExists(sequence, nameof(sequence));
+ return CompactCore(sequence);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CompactCore(IList sequence) => UpdateCore(sequence, sequence);
+
+ #endregion
+
+ #region Garbage Collection
+
+ ///
+ /// TODO: Добавить дополнительный обработчик / событие CanBeDeleted которое можно определить извне или в унаследованном классе
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool IsGarbage(LinkIndex link) => link != Options.SequenceMarkerLink && !Links.Unsync.IsPartialPoint(link) && Links.Count(Constants.Any, link) == 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ClearGarbage(LinkIndex link)
+ {
+ if (IsGarbage(link))
+ {
+ var contents = new Link(Links.GetLink(link));
+ Links.Unsync.Delete(link);
+ ClearGarbage(contents.Source);
+ ClearGarbage(contents.Target);
+ }
+ }
+
+ #endregion
+
+ #region Walkers
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool EachPart(Func handler, LinkIndex sequence)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ var links = Links.Unsync;
+ foreach (var part in Options.Walker.Walk(sequence))
+ {
+ if (!handler(part))
+ {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/SequencesOptions.cs b/csharp/Platform.Data.Doublets/Sequences/SequencesOptions.cs
new file mode 100644
index 000000000..7e72c2501
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/SequencesOptions.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.CompilerServices;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Basic options for Sequences - minimal implementation for refactoring purposes.
+ ///
+ public class SequencesOptions
+ {
+ public bool UseSequenceMarker { get; set; } = false;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ValidateOptions()
+ {
+ // Basic validation - placeholder
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void InitOptions(object links)
+ {
+ // Basic initialization - placeholder
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/SimpleFullMatcher.cs b/csharp/Platform.Data.Doublets/Sequences/SimpleFullMatcher.cs
new file mode 100644
index 000000000..9dcd7d4c5
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/SimpleFullMatcher.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Matcher for full sequence matching operations - simplified version.
+ ///
+ public class SimpleFullMatcher : SimpleSequenceMatcherBase
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public SimpleFullMatcher(Sequences sequences, IList patternSequence, HashSet results, Func, LinkIndex> stopableHandler, HashSet? readAsElements = null)
+ : base(sequences, patternSequence, results, stopableHandler, readAsElements)
+ {
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Match(LinkIndex sequenceToMatch)
+ {
+ return FullMatch(sequenceToMatch);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool FullMatch(LinkIndex sequenceToMatch)
+ {
+ _filterPosition = 0;
+ foreach (var part in Walk(sequenceToMatch))
+ {
+ if (!FullMatchCore(part))
+ {
+ break;
+ }
+ }
+ return _filterPosition == _patternSequence.Count;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool FullMatchCore(LinkIndex element)
+ {
+ if (_filterPosition == _patternSequence.Count)
+ {
+ _filterPosition = -2; // Длиннее чем нужно
+ return false;
+ }
+ if (_patternSequence[_filterPosition] != _sequences.Links.Constants.Any
+ && element != _patternSequence[_filterPosition])
+ {
+ _filterPosition = -1;
+ return false; // Начинается/Продолжается иначе
+ }
+ _filterPosition++;
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void AddMatchedToResults(IList restrictions)
+ {
+ AddFullMatchedToResults(restrictions);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddFullMatchedToResults(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[_sequences.Links.Constants.IndexPart];
+ if (FullMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override LinkIndex HandleMatched(IList restrictions)
+ {
+ return HandleFullMatched(restrictions);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandleFullMatched(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[_sequences.Links.Constants.IndexPart];
+ if (FullMatch(sequenceToMatch) && _results.Add(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequenceToMatch));
+ }
+ return _sequences.Links.Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandleFullMatchedSequence(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[_sequences.Links.Constants.IndexPart];
+ var sequence = _sequences.GetSequenceByElements(sequenceToMatch);
+ if (sequence != _sequences.Links.Constants.Null && FullMatch(sequenceToMatch) && _results.Add(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequence));
+ }
+ return _sequences.Links.Constants.Continue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/SimplePartialMatcher.cs b/csharp/Platform.Data.Doublets/Sequences/SimplePartialMatcher.cs
new file mode 100644
index 000000000..58191a68a
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/SimplePartialMatcher.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Matcher for partial sequence matching operations - simplified version.
+ ///
+ public class SimplePartialMatcher : SimpleSequenceMatcherBase
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public SimplePartialMatcher(Sequences sequences, IList patternSequence, HashSet results, Func, LinkIndex> stopableHandler, HashSet? readAsElements = null)
+ : base(sequences, patternSequence, results, stopableHandler, readAsElements)
+ {
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Match(LinkIndex sequenceToMatch)
+ {
+ return PartialMatch(sequenceToMatch);
+ }
+
+ ///
+ /// TODO: Add support for LinksConstants.Any
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool PartialMatch(LinkIndex sequenceToMatch)
+ {
+ _filterPosition = -1;
+ foreach (var part in Walk(sequenceToMatch))
+ {
+ if (!PartialMatchCore(part))
+ {
+ break;
+ }
+ }
+ return _filterPosition == _patternSequence.Count - 1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool PartialMatchCore(LinkIndex element)
+ {
+ if (_filterPosition == (_patternSequence.Count - 1))
+ {
+ return false; // Нашлось
+ }
+ if (_filterPosition >= 0)
+ {
+ if (element == _patternSequence[_filterPosition + 1])
+ {
+ _filterPosition++;
+ }
+ else
+ {
+ _filterPosition = -1;
+ }
+ }
+ if (_filterPosition < 0)
+ {
+ if (element == _patternSequence[0])
+ {
+ _filterPosition = 0;
+ }
+ }
+ return true; // Ищем дальше
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void AddMatchedToResults(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[_sequences.Links.Constants.IndexPart];
+ AddPartialMatchedToResults(sequenceToMatch);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddPartialMatchedToResults(LinkIndex sequenceToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override LinkIndex HandleMatched(IList restrictions)
+ {
+ return HandlePartialMatched(restrictions);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandlePartialMatched(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[_sequences.Links.Constants.IndexPart];
+ if (PartialMatch(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequenceToMatch));
+ }
+ return _sequences.Links.Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddAllPartialMatchedToResults(IEnumerable sequencesToMatch)
+ {
+ foreach (var sequenceToMatch in sequencesToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddAllPartialMatchedToResultsAndReadAsElements(IEnumerable sequencesToMatch)
+ {
+ foreach (var sequenceToMatch in sequencesToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _readAsElements?.Add(sequenceToMatch);
+ _results.Add(sequenceToMatch);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/Sequences/SimpleSequenceMatcherBase.cs b/csharp/Platform.Data.Doublets/Sequences/SimpleSequenceMatcherBase.cs
new file mode 100644
index 000000000..45fa5b922
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/Sequences/SimpleSequenceMatcherBase.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Base class for sequence matchers providing common functionality without Walker dependency.
+ ///
+ public abstract class SimpleSequenceMatcherBase : ISequenceMatcher
+ {
+ protected readonly Sequences _sequences;
+ protected readonly IList _patternSequence;
+ protected readonly HashSet _linksInSequence;
+ protected readonly HashSet _results;
+ protected readonly Func, LinkIndex> _stopableHandler;
+ protected readonly HashSet? _readAsElements;
+ protected int _filterPosition;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected SimpleSequenceMatcherBase(Sequences sequences, IList patternSequence, HashSet results, Func, LinkIndex> stopableHandler, HashSet? readAsElements = null)
+ {
+ _sequences = sequences;
+ _patternSequence = patternSequence;
+ _linksInSequence = new HashSet(patternSequence.Where(x => x != sequences.Links.Constants.Any));
+ _results = results;
+ _stopableHandler = stopableHandler;
+ _readAsElements = readAsElements;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected virtual bool IsElement(LinkIndex link) => (_readAsElements != null && _readAsElements.Contains(link)) || _linksInSequence.Contains(link);
+
+ public abstract bool Match(LinkIndex sequenceToMatch);
+
+ public abstract void AddMatchedToResults(IList restrictions);
+
+ public abstract LinkIndex HandleMatched(IList restrictions);
+
+ ///
+ /// Simple walk method without complex Walker dependency.
+ ///
+ protected virtual IEnumerable Walk(LinkIndex sequence)
+ {
+ // Simple implementation for walking through a sequence
+ // This is a basic version for the refactoring goal
+
+ if (!_sequences.Links.Exists(sequence))
+ yield break;
+
+ var link = _sequences.Links.GetLink(sequence);
+ var source = _sequences.Links.GetSource(link);
+ var target = _sequences.Links.GetTarget(link);
+
+ if (IsElement(source))
+ yield return source;
+ else if (source != sequence) // Avoid infinite recursion
+ {
+ foreach (var part in Walk(source))
+ yield return part;
+ }
+
+ if (IsElement(target))
+ yield return target;
+ else if (target != sequence) // Avoid infinite recursion
+ {
+ foreach (var part in Walk(target))
+ yield return part;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sequences_original.cs b/sequences_original.cs
new file mode 100644
index 000000000..475d857e1
--- /dev/null
+++ b/sequences_original.cs
@@ -0,0 +1,814 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Platform.Collections;
+using Platform.Collections.Lists;
+using Platform.Collections.Stacks;
+using Platform.Threading.Synchronization;
+using Platform.Data.Doublets.Sequences.Walkers;
+using LinkIndex = System.UInt64;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets.Sequences
+{
+ ///
+ /// Представляет коллекцию последовательностей связей.
+ ///
+ ///
+ /// Обязательно реализовать атомарность каждого публичного метода.
+ ///
+ /// TODO:
+ ///
+ /// !!! Повышение вероятности повторного использования групп (подпоследовательностей),
+ /// через естественную группировку по unicode типам, все whitespace вместе, все символы вместе, все числа вместе и т.п.
+ /// + использовать ровно сбалансированный вариант, чтобы уменьшать вложенность (глубину графа)
+ ///
+ /// x*y - найти все связи между, в последовательностях любой формы, если не стоит ограничитель на то, что является последовательностью, а что нет,
+ /// то находятся любые структуры связей, которые содержат эти элементы именно в таком порядке.
+ ///
+ /// Рост последовательности слева и справа.
+ /// Поиск со звёздочкой.
+ /// URL, PURL - реестр используемых во вне ссылок на ресурсы,
+ /// так же проблема может быть решена при реализации дистанционных триггеров.
+ /// Нужны ли уникальные указатели вообще?
+ /// Что если обращение к информации будет происходить через содержимое всегда?
+ ///
+ /// Писать тесты.
+ ///
+ ///
+ /// Можно убрать зависимость от конкретной реализации Links,
+ /// на зависимость от абстрактного элемента, который может быть представлен несколькими способами.
+ ///
+ /// Можно ли как-то сделать один общий интерфейс
+ ///
+ ///
+ /// Блокчейн и/или гит для распределённой записи транзакций.
+ ///
+ ///
+ public partial class Sequences : ILinks // IList, IList (после завершения реализации Sequences)
+ {
+ /// Возвращает значение LinkIndex, обозначающее любое количество связей.
+ public const LinkIndex ZeroOrMany = LinkIndex.MaxValue;
+
+ public SequencesOptions Options { get; }
+ public SynchronizedLinks Links { get; }
+ private readonly ISynchronization _sync;
+
+ public LinksConstants Constants { get; }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Sequences(SynchronizedLinks links, SequencesOptions options)
+ {
+ Links = links;
+ _sync = links.SyncRoot;
+ Options = options;
+ Options.ValidateOptions();
+ Options.InitOptions(Links);
+ Constants = links.Constants;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Sequences(SynchronizedLinks links) : this(links, new SequencesOptions()) { }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsSequence(LinkIndex sequence)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Options.MarkedSequenceMatcher.IsMatched(sequence);
+ }
+ return !Links.Unsync.IsPartialPoint(sequence);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex GetSequenceByElements(LinkIndex sequence)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Links.SearchOrDefault(Options.SequenceMarkerLink, sequence);
+ }
+ return sequence;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex GetSequenceElements(LinkIndex sequence)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var linkContents = new Link(Links.GetLink(sequence));
+ if (linkContents.Source == Options.SequenceMarkerLink)
+ {
+ return linkContents.Target;
+ }
+ if (linkContents.Target == Options.SequenceMarkerLink)
+ {
+ return linkContents.Source;
+ }
+ }
+ return sequence;
+ }
+
+ #region Count
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Count(IList restrictions)
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Links.Count(Constants.Any, Options.SequenceMarkerLink, Constants.Any);
+ }
+ if (restrictions.Count == 1) // Первая связь это адрес
+ {
+ var sequenceIndex = restrictions[0];
+ if (sequenceIndex == Constants.Null)
+ {
+ return 0;
+ }
+ if (sequenceIndex == Constants.Any)
+ {
+ return Count(null);
+ }
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Count(Constants.Any, Options.SequenceMarkerLink, sequenceIndex);
+ }
+ return Links.Exists(sequenceIndex) ? 1UL : 0;
+ }
+ throw new NotImplementedException();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CountUsages(params LinkIndex[] restrictions)
+ {
+ if (restrictions.Length == 0)
+ {
+ return 0;
+ }
+ if (restrictions.Length == 1) // Первая связь это адрес
+ {
+ if (restrictions[0] == Constants.Null)
+ {
+ return 0;
+ }
+ var any = Constants.Any;
+ if (Options.UseSequenceMarker)
+ {
+ var elementsLink = GetSequenceElements(restrictions[0]);
+ var sequenceLink = GetSequenceByElements(elementsLink);
+ if (sequenceLink != Constants.Null)
+ {
+ return Links.Count(any, sequenceLink) + Links.Count(any, elementsLink) - 1;
+ }
+ return Links.Count(any, elementsLink);
+ }
+ return Links.Count(any, restrictions[0]);
+ }
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region Create
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Create(IList restrictions)
+ {
+ return _sync.ExecuteWriteOperation(() =>
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ Links.EnsureInnerReferenceExists(restrictions, nameof(restrictions));
+ return CreateCore(restrictions);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CreateCore(IList restrictions)
+ {
+ LinkIndex[] sequence = restrictions.SkipFirst();
+ if (Options.UseIndex)
+ {
+ Options.Index.Add(sequence);
+ }
+ var sequenceRoot = default(LinkIndex);
+ if (Options.EnforceSingleSequenceVersionOnWriteBasedOnExisting)
+ {
+ var matches = Each(restrictions);
+ if (matches.Count > 0)
+ {
+ sequenceRoot = matches[0];
+ }
+ }
+ else if (Options.EnforceSingleSequenceVersionOnWriteBasedOnNew)
+ {
+ return CompactCore(sequence);
+ }
+ if (sequenceRoot == default)
+ {
+ sequenceRoot = Options.LinksToSequenceConverter.Convert(sequence);
+ }
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Unsync.GetOrCreate(Options.SequenceMarkerLink, sequenceRoot);
+ }
+ return sequenceRoot; // Возвращаем корень последовательности (т.е. сами элементы)
+ }
+
+ #endregion
+
+ #region Each
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public List Each(IList sequence)
+ {
+ var results = new List();
+ var filler = new ListFiller(results, Constants.Continue);
+ Each(filler.AddFirstAndReturnConstant, sequence);
+ return results;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Each(Func, LinkIndex> handler, IList restrictions)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ if (restrictions.IsNullOrEmpty())
+ {
+ return Constants.Continue;
+ }
+ Links.EnsureInnerReferenceExists(restrictions, nameof(restrictions));
+ if (restrictions.Count == 1)
+ {
+ var link = restrictions[0];
+ var any = Constants.Any;
+ if (link == any)
+ {
+ if (Options.UseSequenceMarker)
+ {
+ return Links.Unsync.Each(handler, new Link(any, Options.SequenceMarkerLink, any));
+ }
+ else
+ {
+ return Links.Unsync.Each(handler, new Link(any, any, any));
+ }
+ }
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceLinkValues = Links.Unsync.GetLink(link);
+ if (sequenceLinkValues[Constants.SourcePart] == Options.SequenceMarkerLink)
+ {
+ link = sequenceLinkValues[Constants.TargetPart];
+ }
+ }
+ var sequence = Options.Walker.Walk(link).ToArray().ShiftRight();
+ sequence[0] = link;
+ return handler(sequence);
+ }
+ else if (restrictions.Count == 2)
+ {
+ throw new NotImplementedException();
+ }
+ else if (restrictions.Count == 3)
+ {
+ return Links.Unsync.Each(handler, restrictions);
+ }
+ else
+ {
+ var sequence = restrictions.SkipFirst();
+ if (Options.UseIndex && !Options.Index.MightContain(sequence))
+ {
+ return Constants.Break;
+ }
+ return EachCore(handler, sequence);
+ }
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex EachCore(Func, LinkIndex> handler, IList values)
+ {
+ var matcher = new Matcher(this, values, new HashSet(), handler);
+ // TODO: Find out why matcher.HandleFullMatched executed twice for the same sequence Id.
+ Func, LinkIndex> innerHandler = Options.UseSequenceMarker ? (Func, LinkIndex>)matcher.HandleFullMatchedSequence : matcher.HandleFullMatched;
+ //if (sequence.Length >= 2)
+ if (StepRight(innerHandler, values[0], values[1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ var last = values.Count - 2;
+ for (var i = 1; i < last; i++)
+ {
+ if (PartialStepRight(innerHandler, values[i], values[i + 1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ }
+ if (values.Count >= 3)
+ {
+ if (StepLeft(innerHandler, values[values.Count - 2], values[values.Count - 1]) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ }
+ return Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex PartialStepRight(Func, LinkIndex> handler, LinkIndex left, LinkIndex right)
+ {
+ return Links.Unsync.Each(doublet =>
+ {
+ var doubletIndex = doublet[Constants.IndexPart];
+ if (StepRight(handler, doubletIndex, right) != Constants.Continue)
+ {
+ return Constants.Break;
+ }
+ if (left != doubletIndex)
+ {
+ return PartialStepRight(handler, doubletIndex, right);
+ }
+ return Constants.Continue;
+ }, new Link(Constants.Any, Constants.Any, left));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex StepRight(Func, LinkIndex> handler, LinkIndex left, LinkIndex right) => Links.Unsync.Each(rightStep => TryStepRightUp(handler, right, rightStep[Constants.IndexPart]), new Link(Constants.Any, left, Constants.Any));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex TryStepRightUp(Func, LinkIndex> handler, LinkIndex right, LinkIndex stepFrom)
+ {
+ var upStep = stepFrom;
+ var firstSource = Links.Unsync.GetTarget(upStep);
+ while (firstSource != right && firstSource != upStep)
+ {
+ upStep = firstSource;
+ firstSource = Links.Unsync.GetSource(upStep);
+ }
+ if (firstSource == right)
+ {
+ return handler(new LinkAddress(stepFrom));
+ }
+ return Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex StepLeft(Func, LinkIndex> handler, LinkIndex left, LinkIndex right) => Links.Unsync.Each(leftStep => TryStepLeftUp(handler, left, leftStep[Constants.IndexPart]), new Link(Constants.Any, Constants.Any, right));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex TryStepLeftUp(Func, LinkIndex> handler, LinkIndex left, LinkIndex stepFrom)
+ {
+ var upStep = stepFrom;
+ var firstTarget = Links.Unsync.GetSource(upStep);
+ while (firstTarget != left && firstTarget != upStep)
+ {
+ upStep = firstTarget;
+ firstTarget = Links.Unsync.GetTarget(upStep);
+ }
+ if (firstTarget == left)
+ {
+ return handler(new LinkAddress(stepFrom));
+ }
+ return Constants.Continue;
+ }
+
+ #endregion
+
+ #region Update
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Update(IList restrictions, IList substitution)
+ {
+ var sequence = restrictions.SkipFirst();
+ var newSequence = substitution.SkipFirst();
+ if (sequence.IsNullOrEmpty() && newSequence.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ if (sequence.IsNullOrEmpty())
+ {
+ return Create(substitution);
+ }
+ if (newSequence.IsNullOrEmpty())
+ {
+ Delete(restrictions);
+ return Constants.Null;
+ }
+ return _sync.ExecuteWriteOperation((Func)(() =>
+ {
+ ILinksExtensions.EnsureLinkIsAnyOrExists(Links, (IList)sequence);
+ Links.EnsureLinkExists(newSequence);
+ return UpdateCore(sequence, newSequence);
+ }));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex UpdateCore(IList sequence, IList newSequence)
+ {
+ LinkIndex bestVariant;
+ if (Options.EnforceSingleSequenceVersionOnWriteBasedOnNew && !sequence.EqualTo(newSequence))
+ {
+ bestVariant = CompactCore(newSequence);
+ }
+ else
+ {
+ bestVariant = CreateCore(newSequence);
+ }
+ // TODO: Check all options only ones before loop execution
+ // Возможно нужно две версии Each, возвращающий фактические последовательности и с маркером,
+ // или возможно даже возвращать и тот и тот вариант. С другой стороны все варианты можно получить имея только фактические последовательности.
+ foreach (var variant in Each(sequence))
+ {
+ if (variant != bestVariant)
+ {
+ UpdateOneCore(variant, bestVariant);
+ }
+ }
+ return bestVariant;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void UpdateOneCore(LinkIndex sequence, LinkIndex newSequence)
+ {
+ if (Options.UseGarbageCollection)
+ {
+ var sequenceElements = GetSequenceElements(sequence);
+ var sequenceElementsContents = new Link(Links.GetLink(sequenceElements));
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ var newSequenceElements = GetSequenceElements(newSequence);
+ var newSequenceLink = GetSequenceByElements(newSequenceElements);
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.MergeAndDelete(sequenceLink, newSequenceLink);
+ }
+ Links.Unsync.MergeAndDelete(sequenceElements, newSequenceElements);
+ }
+ ClearGarbage(sequenceElementsContents.Source);
+ ClearGarbage(sequenceElementsContents.Target);
+ }
+ else
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceElements = GetSequenceElements(sequence);
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ var newSequenceElements = GetSequenceElements(newSequence);
+ var newSequenceLink = GetSequenceByElements(newSequenceElements);
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.MergeAndDelete(sequenceLink, newSequenceLink);
+ }
+ Links.Unsync.MergeAndDelete(sequenceElements, newSequenceElements);
+ }
+ }
+ else
+ {
+ if (Options.UseCascadeUpdate || CountUsages(sequence) == 0)
+ {
+ Links.Unsync.MergeAndDelete(sequence, newSequence);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Delete
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Delete(IList restrictions)
+ {
+ _sync.ExecuteWriteOperation(() =>
+ {
+ var sequence = restrictions.SkipFirst();
+ // TODO: Check all options only ones before loop execution
+ foreach (var linkToDelete in Each(sequence))
+ {
+ DeleteOneCore(linkToDelete);
+ }
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void DeleteOneCore(LinkIndex link)
+ {
+ if (Options.UseGarbageCollection)
+ {
+ var sequenceElements = GetSequenceElements(link);
+ var sequenceElementsContents = new Link(Links.GetLink(sequenceElements));
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.Delete(sequenceLink);
+ }
+ Links.Unsync.Delete(link);
+ }
+ ClearGarbage(sequenceElementsContents.Source);
+ ClearGarbage(sequenceElementsContents.Target);
+ }
+ else
+ {
+ if (Options.UseSequenceMarker)
+ {
+ var sequenceElements = GetSequenceElements(link);
+ var sequenceLink = GetSequenceByElements(sequenceElements);
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ if (sequenceLink != Constants.Null)
+ {
+ Links.Unsync.Delete(sequenceLink);
+ }
+ Links.Unsync.Delete(link);
+ }
+ }
+ else
+ {
+ if (Options.UseCascadeDelete || CountUsages(link) == 0)
+ {
+ Links.Unsync.Delete(link);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Compactification
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CompactAll()
+ {
+ _sync.ExecuteWriteOperation(() =>
+ {
+ var sequences = Each((LinkAddress)Constants.Any);
+ for (int i = 0; i < sequences.Count; i++)
+ {
+ var sequence = this.ToList(sequences[i]);
+ Compact(sequence.ShiftRight());
+ }
+ });
+ }
+
+ ///
+ /// bestVariant можно выбирать по максимальному числу использований,
+ /// но балансированный позволяет гарантировать уникальность (если есть возможность,
+ /// гарантировать его использование в других местах).
+ ///
+ /// Получается этот метод должен игнорировать Options.EnforceSingleSequenceVersionOnWrite
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex Compact(IList sequence)
+ {
+ return _sync.ExecuteWriteOperation(() =>
+ {
+ if (sequence.IsNullOrEmpty())
+ {
+ return Constants.Null;
+ }
+ Links.EnsureInnerReferenceExists(sequence, nameof(sequence));
+ return CompactCore(sequence);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private LinkIndex CompactCore(IList sequence) => UpdateCore(sequence, sequence);
+
+ #endregion
+
+ #region Garbage Collection
+
+ ///
+ /// TODO: Добавить дополнительный обработчик / событие CanBeDeleted которое можно определить извне или в унаследованном классе
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool IsGarbage(LinkIndex link) => link != Options.SequenceMarkerLink && !Links.Unsync.IsPartialPoint(link) && Links.Count(Constants.Any, link) == 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ClearGarbage(LinkIndex link)
+ {
+ if (IsGarbage(link))
+ {
+ var contents = new Link(Links.GetLink(link));
+ Links.Unsync.Delete(link);
+ ClearGarbage(contents.Source);
+ ClearGarbage(contents.Target);
+ }
+ }
+
+ #endregion
+
+ #region Walkers
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool EachPart(Func handler, LinkIndex sequence)
+ {
+ return _sync.ExecuteReadOperation(() =>
+ {
+ var links = Links.Unsync;
+ foreach (var part in Options.Walker.Walk(sequence))
+ {
+ if (!handler(part))
+ {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ public class Matcher : RightSequenceWalker
+ {
+ private readonly Sequences _sequences;
+ private readonly IList _patternSequence;
+ private readonly HashSet _linksInSequence;
+ private readonly HashSet _results;
+ private readonly Func, LinkIndex> _stopableHandler;
+ private readonly HashSet _readAsElements;
+ private int _filterPosition;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Matcher(Sequences sequences, IList patternSequence, HashSet results, Func, LinkIndex> stopableHandler, HashSet readAsElements = null)
+ : base(sequences.Links.Unsync, new DefaultStack())
+ {
+ _sequences = sequences;
+ _patternSequence = patternSequence;
+ _linksInSequence = new HashSet(patternSequence.Where(x => x != Links.Constants.Any && x != ZeroOrMany));
+ _results = results;
+ _stopableHandler = stopableHandler;
+ _readAsElements = readAsElements;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override bool IsElement(LinkIndex link) => base.IsElement(link) || (_readAsElements != null && _readAsElements.Contains(link)) || _linksInSequence.Contains(link);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool FullMatch(LinkIndex sequenceToMatch)
+ {
+ _filterPosition = 0;
+ foreach (var part in Walk(sequenceToMatch))
+ {
+ if (!FullMatchCore(part))
+ {
+ break;
+ }
+ }
+ return _filterPosition == _patternSequence.Count;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool FullMatchCore(LinkIndex element)
+ {
+ if (_filterPosition == _patternSequence.Count)
+ {
+ _filterPosition = -2; // Длиннее чем нужно
+ return false;
+ }
+ if (_patternSequence[_filterPosition] != Links.Constants.Any
+ && element != _patternSequence[_filterPosition])
+ {
+ _filterPosition = -1;
+ return false; // Начинается/Продолжается иначе
+ }
+ _filterPosition++;
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddFullMatchedToResults(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[Links.Constants.IndexPart];
+ if (FullMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandleFullMatched(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[Links.Constants.IndexPart];
+ if (FullMatch(sequenceToMatch) && _results.Add(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequenceToMatch));
+ }
+ return Links.Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandleFullMatchedSequence(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[Links.Constants.IndexPart];
+ var sequence = _sequences.GetSequenceByElements(sequenceToMatch);
+ if (sequence != Links.Constants.Null && FullMatch(sequenceToMatch) && _results.Add(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequence));
+ }
+ return Links.Constants.Continue;
+ }
+
+ ///
+ /// TODO: Add support for LinksConstants.Any
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool PartialMatch(LinkIndex sequenceToMatch)
+ {
+ _filterPosition = -1;
+ foreach (var part in Walk(sequenceToMatch))
+ {
+ if (!PartialMatchCore(part))
+ {
+ break;
+ }
+ }
+ return _filterPosition == _patternSequence.Count - 1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool PartialMatchCore(LinkIndex element)
+ {
+ if (_filterPosition == (_patternSequence.Count - 1))
+ {
+ return false; // Нашлось
+ }
+ if (_filterPosition >= 0)
+ {
+ if (element == _patternSequence[_filterPosition + 1])
+ {
+ _filterPosition++;
+ }
+ else
+ {
+ _filterPosition = -1;
+ }
+ }
+ if (_filterPosition < 0)
+ {
+ if (element == _patternSequence[0])
+ {
+ _filterPosition = 0;
+ }
+ }
+ return true; // Ищем дальше
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddPartialMatchedToResults(LinkIndex sequenceToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkIndex HandlePartialMatched(IList restrictions)
+ {
+ var sequenceToMatch = restrictions[Links.Constants.IndexPart];
+ if (PartialMatch(sequenceToMatch))
+ {
+ return _stopableHandler(new LinkAddress(sequenceToMatch));
+ }
+ return Links.Constants.Continue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddAllPartialMatchedToResults(IEnumerable sequencesToMatch)
+ {
+ foreach (var sequenceToMatch in sequencesToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _results.Add(sequenceToMatch);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AddAllPartialMatchedToResultsAndReadAsElements(IEnumerable sequencesToMatch)
+ {
+ foreach (var sequenceToMatch in sequencesToMatch)
+ {
+ if (PartialMatch(sequenceToMatch))
+ {
+ _readAsElements.Add(sequenceToMatch);
+ _results.Add(sequenceToMatch);
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file