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