diff --git a/csharp/Platform.Data.Doublets/ILinkTree.cs b/csharp/Platform.Data.Doublets/ILinkTree.cs
new file mode 100644
index 000000000..a6cbd546e
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/ILinkTree.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets
+{
+ ///
+ ///
+ /// Defines a tree-like interface for representing links in hierarchical structure
+ /// instead of flat IList<TLinkAddress> representation.
+ ///
+ ///
+ /// The type of the link address.
+ public interface ILinkTree where TLinkAddress : IUnsignedNumber
+ {
+ ///
+ /// Gets the index (identifier) of this link.
+ ///
+ TLinkAddress Index { get; }
+
+ ///
+ /// Gets the source of this link.
+ ///
+ TLinkAddress Source { get; }
+
+ ///
+ /// Gets the target of this link.
+ ///
+ TLinkAddress Target { get; }
+
+ ///
+ /// Gets the parent link in the tree structure, if any.
+ ///
+ ILinkTree? Parent { get; }
+
+ ///
+ /// Gets the children of this link in the tree structure.
+ ///
+ IEnumerable> Children { get; }
+
+ ///
+ /// Gets the number of children in this tree node.
+ ///
+ int ChildrenCount { get; }
+
+ ///
+ /// Gets the child at the specified index.
+ ///
+ /// The zero-based index of the child.
+ /// The child at the specified index, or null if index is out of range.
+ ILinkTree? GetChild(int index);
+
+ ///
+ /// Gets the source as a tree node, if it represents a link.
+ ///
+ /// The source as a tree node, or null if source is not a link.
+ ILinkTree? GetSourceAsTree();
+
+ ///
+ /// Gets the target as a tree node, if it represents a link.
+ ///
+ /// The target as a tree node, or null if target is not a link.
+ ILinkTree? GetTargetAsTree();
+
+ ///
+ /// Determines whether this tree represents a null/empty link.
+ ///
+ /// True if this is a null link, false otherwise.
+ bool IsNull();
+
+ ///
+ /// Converts this tree structure to a flat array representation for compatibility.
+ ///
+ /// An array containing [Index, Source, Target].
+ TLinkAddress[] ToArray();
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets/ILinksExtensions.cs b/csharp/Platform.Data.Doublets/ILinksExtensions.cs
index a94c8f579..081e2bc7e 100644
--- a/csharp/Platform.Data.Doublets/ILinksExtensions.cs
+++ b/csharp/Platform.Data.Doublets/ILinksExtensions.cs
@@ -1599,5 +1599,125 @@ public static void ClearGarbage(this ILinks links, T
}
#endregion
+
+ #region Tree-Compatible Extensions
+
+ ///
+ /// Gets a single link matching the tree query, or null if none or multiple links exist.
+ ///
+ /// The links storage.
+ /// The tree query to search for.
+ /// A single matching link as a tree, or null.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ILinkTree? SingleOrDefaultTree(this ILinks links, ILinkTree? query) where TLinkAddress : IUnsignedNumber
+ {
+ // Convert tree query to list format for compatibility with existing infrastructure
+ var listQuery = query?.ToArray();
+ var result = links.SingleOrDefault(listQuery);
+ return result != null ? new LinkTree(result) : null;
+ }
+
+ ///
+ /// Gets the index (identifier) of a tree-structured link.
+ ///
+ /// The links storage.
+ /// The tree-structured link.
+ /// The index of the link.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TLinkAddress GetTreeIndex(this ILinks links, ILinkTree? tree) where TLinkAddress : IUnsignedNumber
+ {
+ return tree != null ? tree.Index : links.Constants.Null;
+ }
+
+ ///
+ /// Gets the source of a tree-structured link.
+ ///
+ /// The links storage.
+ /// The tree-structured link.
+ /// The source of the link.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TLinkAddress GetTreeSource(this ILinks links, ILinkTree? tree) where TLinkAddress : IUnsignedNumber
+ {
+ return tree != null ? tree.Source : links.Constants.Null;
+ }
+
+ ///
+ /// Gets the target of a tree-structured link.
+ ///
+ /// The links storage.
+ /// The tree-structured link.
+ /// The target of the link.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TLinkAddress GetTreeTarget(this ILinks links, ILinkTree? tree) where TLinkAddress : IUnsignedNumber
+ {
+ return tree != null ? tree.Target : links.Constants.Null;
+ }
+
+ ///
+ /// Gets all links matching the tree query.
+ ///
+ /// The links storage.
+ /// The tree-based restriction to search for.
+ /// All matching links as trees.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IList?> AllTrees(this ILinks links, ILinkTree? restriction) where TLinkAddress : IUnsignedNumber
+ {
+ var listRestriction = restriction?.ToArray();
+ var results = links.All(listRestriction);
+ return results.Select(list => list != null ? (ILinkTree?)new LinkTree(list) : null).ToList();
+ }
+
+ ///
+ /// Ensures a link exists based on tree structure restriction.
+ ///
+ /// The links storage.
+ /// The tree-structured restriction.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void EnsureTreeLinkExists(this ILinks links, ILinkTree? restriction) where TLinkAddress : IUnsignedNumber
+ {
+ var listRestriction = restriction?.ToArray();
+ links.EnsureLinkExists(listRestriction);
+ }
+
+ ///
+ /// Formats a tree-structured link for display.
+ ///
+ /// The links storage.
+ /// The tree-structured link.
+ /// A formatted string representation of the link.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string FormatTree(this ILinks links, ILinkTree? tree) where TLinkAddress : IUnsignedNumber
+ {
+ if (tree == null) return "null";
+ var listRepresentation = tree.ToArray();
+ return links.Format(listRepresentation);
+ }
+
+ ///
+ /// Converts a list-based link to tree structure.
+ ///
+ /// The links storage.
+ /// The list-based link.
+ /// The tree-structured representation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ILinkTree? AsTree(this ILinks links, IList? list) where TLinkAddress : IUnsignedNumber
+ {
+ return list != null ? new LinkTree(list) : null;
+ }
+
+ ///
+ /// Converts a tree-structured link to list format for backward compatibility.
+ ///
+ /// The links storage.
+ /// The tree-structured link.
+ /// The list representation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IList? AsList(this ILinks links, ILinkTree? tree) where TLinkAddress : IUnsignedNumber
+ {
+ return tree?.ToArray();
+ }
+
+ #endregion
+
}
}
diff --git a/csharp/Platform.Data.Doublets/Link.cs b/csharp/Platform.Data.Doublets/Link.cs
index b09927904..b6e6b6274 100644
--- a/csharp/Platform.Data.Doublets/Link.cs
+++ b/csharp/Platform.Data.Doublets/Link.cs
@@ -15,7 +15,7 @@ namespace Platform.Data.Doublets
///
/// Структура описывающая уникальную связь.
///
- public struct Link : IEquatable>, IReadOnlyList, IList where TLinkAddress : IUnsignedNumber
+ public struct Link : IEquatable>, IReadOnlyList, IList, ILinkTree where TLinkAddress : IUnsignedNumber
{
///
///
@@ -96,6 +96,12 @@ public Link(object other)
{
SetValues(ref otherLink, out Index, out Source, out Target);
}
+ else if(other is ILinkTree otherTree)
+ {
+ Index = otherTree.Index;
+ Source = otherTree.Source;
+ Target = otherTree.Target;
+ }
else if(other is IList otherList)
{
SetValues(otherList, out Index, out Source, out Target);
@@ -144,6 +150,33 @@ public Link(TLinkAddress index, TLinkAddress source, TLinkAddress target)
Source = source;
Target = target;
}
+
+ ///
+ ///
+ /// Initializes a new instance from a tree structure.
+ ///
+ ///
+ ///
+ ///
+ /// A tree structure.
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Link(ILinkTree? tree)
+ {
+ if (tree == null)
+ {
+ Index = default;
+ Source = default;
+ Target = default;
+ }
+ else
+ {
+ Index = tree.Index;
+ Source = tree.Source;
+ Target = tree.Target;
+ }
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetValues(ref Link other, out TLinkAddress index, out TLinkAddress source, out TLinkAddress target)
{
@@ -559,5 +592,68 @@ public int IndexOf(TLinkAddress item)
public static bool operator !=(Link left, Link right) { return !(left == right);}
#endregion
+
+ #region ILinkTree
+
+ ///
+ /// Gets the index (identifier) of this link.
+ ///
+ TLinkAddress ILinkTree.Index => Index;
+
+ ///
+ /// Gets the source of this link.
+ ///
+ TLinkAddress ILinkTree.Source => Source;
+
+ ///
+ /// Gets the target of this link.
+ ///
+ TLinkAddress ILinkTree.Target => Target;
+
+ ///
+ /// Gets the parent link in the tree structure (always null for Link struct).
+ ///
+ public ILinkTree? Parent => null;
+
+ ///
+ /// Gets the children of this link in the tree structure (always empty for Link struct).
+ ///
+ public IEnumerable> Children => System.Linq.Enumerable.Empty>();
+
+ ///
+ /// Gets the number of children in this tree node (always 0 for Link struct).
+ ///
+ public int ChildrenCount => 0;
+
+ ///
+ /// Gets the child at the specified index (always returns null for Link struct).
+ ///
+ /// The zero-based index of the child.
+ /// Always null for Link struct.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetChild(int index) => null;
+
+ ///
+ /// Gets the source as a tree node, if it represents a link (returns null for primitive values).
+ ///
+ /// Null, indicating source is a primitive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetSourceAsTree() => null;
+
+ ///
+ /// Gets the target as a tree node, if it represents a link (returns null for primitive values).
+ ///
+ /// Null, indicating target is a primitive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetTargetAsTree() => null;
+
+ ///
+ /// Converts this tree structure to a flat array representation for compatibility.
+ ///
+ /// An array containing [Index, Source, Target].
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TLinkAddress[] ToArray() => new[] { Index, Source, Target };
+
+ #endregion
}
}
diff --git a/csharp/Platform.Data.Doublets/LinkTree.cs b/csharp/Platform.Data.Doublets/LinkTree.cs
new file mode 100644
index 000000000..ddd3ae5f0
--- /dev/null
+++ b/csharp/Platform.Data.Doublets/LinkTree.cs
@@ -0,0 +1,226 @@
+using Platform.Collections.Lists;
+using Platform.Exceptions;
+using Platform.Ranges;
+using Platform.Singletons;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Linq;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace Platform.Data.Doublets
+{
+ ///
+ ///
+ /// Represents a tree-like structure for links instead of flat IList representation.
+ ///
+ ///
+ /// The type of the link address.
+ public struct LinkTree : ILinkTree, IEquatable>
+ where TLinkAddress : IUnsignedNumber
+ {
+ public static readonly LinkTree Null = new LinkTree();
+ private static readonly LinksConstants _constants = Default>.Instance;
+
+ public readonly TLinkAddress Index;
+ public readonly TLinkAddress Source;
+ public readonly TLinkAddress Target;
+ private readonly ILinkTree? _parent;
+ private readonly ILinkTree[]? _children;
+
+ TLinkAddress ILinkTree.Index => Index;
+ TLinkAddress ILinkTree.Source => Source;
+ TLinkAddress ILinkTree.Target => Target;
+
+ public ILinkTree? Parent => _parent;
+
+ public IEnumerable> Children =>
+ _children ?? Enumerable.Empty>();
+
+ public int ChildrenCount => _children?.Length ?? 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkTree(TLinkAddress index, TLinkAddress source, TLinkAddress target)
+ {
+ Index = index;
+ Source = source;
+ Target = target;
+ _parent = null;
+ _children = null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkTree(TLinkAddress index, TLinkAddress source, TLinkAddress target,
+ ILinkTree? parent, ILinkTree[]? children = null)
+ {
+ Index = index;
+ Source = source;
+ Target = target;
+ _parent = parent;
+ _children = children;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkTree(params TLinkAddress[] values)
+ {
+ SetValues(values, out Index, out Source, out Target);
+ _parent = null;
+ _children = null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkTree(IList? values)
+ {
+ SetValues(values, out Index, out Source, out Target);
+ _parent = null;
+ _children = null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public LinkTree(object other)
+ {
+ if (other is LinkTree otherTree)
+ {
+ SetValues(ref otherTree, out Index, out Source, out Target);
+ _parent = otherTree._parent;
+ _children = otherTree._children;
+ }
+ else if (other is Link otherLink)
+ {
+ Index = otherLink.Index;
+ Source = otherLink.Source;
+ Target = otherLink.Target;
+ _parent = null;
+ _children = null;
+ }
+ else if (other is IList otherList)
+ {
+ SetValues(otherList, out Index, out Source, out Target);
+ _parent = null;
+ _children = null;
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void SetValues(ref LinkTree other, out TLinkAddress index, out TLinkAddress source, out TLinkAddress target)
+ {
+ index = other.Index;
+ source = other.Source;
+ target = other.Target;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void SetValues(IList? values, out TLinkAddress index, out TLinkAddress source, out TLinkAddress target)
+ {
+ if (values == null)
+ {
+ index = default;
+ source = default;
+ target = default;
+ return;
+ }
+ switch (values.Count)
+ {
+ case 3:
+ index = values[0];
+ source = values[1];
+ target = values[2];
+ break;
+ case 2:
+ index = values[0];
+ source = values[1];
+ target = default;
+ break;
+ case 1:
+ index = values[0];
+ source = default;
+ target = default;
+ break;
+ default:
+ index = default;
+ source = default;
+ target = default;
+ break;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetChild(int index)
+ {
+ if (_children == null || index < 0 || index >= _children.Length)
+ {
+ return null;
+ }
+ return _children[index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetSourceAsTree()
+ {
+ // If Source is a valid link address, we could potentially create a tree for it
+ // For now, returning null to indicate source is a primitive value
+ // This could be enhanced to look up the actual link if needed
+ return null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ILinkTree? GetTargetAsTree()
+ {
+ // If Target is a valid link address, we could potentially create a tree for it
+ // For now, returning null to indicate target is a primitive value
+ // This could be enhanced to look up the actual link if needed
+ return null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsNull() => Index == _constants.Null
+ && Source == _constants.Null
+ && Target == _constants.Null;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TLinkAddress[] ToArray() => new[] { Index, Source, Target };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override int GetHashCode() => (Index, Source, Target).GetHashCode();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object other) => other is LinkTree && Equals((LinkTree)other);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(LinkTree other) => Index == other.Index
+ && Source == other.Source
+ && Target == other.Target;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(LinkTree left, LinkTree right) => left.Equals(right);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(LinkTree left, LinkTree right) => !(left == right);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator TLinkAddress[](LinkTree tree) => tree.ToArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator LinkTree(TLinkAddress[] linkArray) => new LinkTree(linkArray);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator LinkTree(Link link) =>
+ new LinkTree(link.Index, link.Source, link.Target);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Link(LinkTree tree) =>
+ new Link(tree.Index, tree.Source, tree.Target);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override string ToString() => Index == _constants.Null ?
+ Link.ToString(Source, Target) :
+ Link.ToString(Index, Source, Target);
+ }
+}
\ No newline at end of file
diff --git a/experiments/TreeStructureDemo.cs b/experiments/TreeStructureDemo.cs
new file mode 100644
index 000000000..f609c4606
--- /dev/null
+++ b/experiments/TreeStructureDemo.cs
@@ -0,0 +1,63 @@
+using System;
+using Platform.Data.Doublets;
+using Platform.Data.Doublets.Memory.United.Generic;
+
+///
+/// Demonstrates the new tree-like structure for links instead of IList.
+/// This shows how the tree structure provides a more hierarchical way to work with links.
+///
+class TreeStructureDemo
+{
+ static void Main()
+ {
+ Console.WriteLine("=== Tree Structure Demo ===");
+
+ // Create a simple memory-based links storage
+ using var links = new UnitedMemoryLinks(new UnitedMemoryLinksOptions());
+
+ Console.WriteLine("1. Traditional IList approach:");
+
+ // Create some links using the traditional array/list approach
+ var link1 = links.Create();
+ var link2 = links.Create();
+ var link3 = links.GetOrCreate(link1, link2);
+
+ Console.WriteLine($" Created links: {link1}, {link2}, {link3}");
+
+ // Get link as traditional list format
+ var link3AsList = links.GetLink(link3);
+ Console.WriteLine($" Link 3 as list: [{string.Join(", ", link3AsList)}]");
+
+ Console.WriteLine("\n2. New Tree-like structure approach:");
+
+ // Convert to tree structure using new extension method
+ var link3AsTree = links.AsTree(link3AsList);
+ Console.WriteLine($" Link 3 as tree:");
+ Console.WriteLine($" Index: {links.GetTreeIndex(link3AsTree)}");
+ Console.WriteLine($" Source: {links.GetTreeSource(link3AsTree)}");
+ Console.WriteLine($" Target: {links.GetTreeTarget(link3AsTree)}");
+ Console.WriteLine($" Parent: {link3AsTree?.Parent}");
+ Console.WriteLine($" Children Count: {link3AsTree?.ChildrenCount}");
+
+ // Demonstrate tree-specific functionality
+ Console.WriteLine($" Is Null: {link3AsTree?.IsNull()}");
+ Console.WriteLine($" ToString: {link3AsTree?.ToString()}");
+
+ // Create a Link struct that implements both IList and ILinkTree
+ var linkStruct = new Link(link3, link1, link2);
+ Console.WriteLine($"\n3. Link struct with tree interface:");
+ Console.WriteLine($" As ILinkTree - Index: {((ILinkTree)linkStruct).Index}");
+ Console.WriteLine($" As ILinkTree - Source: {((ILinkTree)linkStruct).Source}");
+ Console.WriteLine($" As ILinkTree - Target: {((ILinkTree)linkStruct).Target}");
+
+ // Show backward compatibility
+ Console.WriteLine($"\n4. Backward compatibility:");
+ var linkAsArray = linkStruct.ToArray();
+ Console.WriteLine($" Link as array: [{string.Join(", ", linkAsArray)}]");
+
+ // Demonstrate format extension
+ Console.WriteLine($" Formatted tree: {links.FormatTree(link3AsTree)}");
+
+ Console.WriteLine("\n=== Demo Complete ===");
+ }
+}
\ No newline at end of file