You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Trees/ItemFieldToTreeConverter.cs

305 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using ZeroLevel.Services.Collections;
namespace ZeroLevel.Services.Trees
{
/// <summary>
/// Выполняет преобразование набора элементов в дерево (в набор ветвей)
/// </summary>
/// <typeparam name="T">Тип элемента</typeparam>
/// <typeparam name="TKey">Тип связующих компонентов элемента</typeparam>
public class ItemFieldToTreeConverter<T, TKey>
{
#region Inner classes
private struct BranchTestResult
{
public bool HasInteraction;
public NodeBranch NewBranch;
}
private struct Node
{
public T Value;
public TKey In;
public TKey Out;
internal Node Clone()
{
return new Node
{
In = this.In,
Out = this.Out,
Value = this.Value
};
}
public bool Eq(Node other, Func<T, T, bool> comparer, Func<TKey, TKey, bool> key_comparer)
{
if (ReferenceEquals(this, other))
return true;
return comparer(this.Value, other.Value) &&
key_comparer(this.In, other.In) &&
key_comparer(this.Out, other.Out);
}
public override int GetHashCode()
{
return this.Value?.GetHashCode() ?? 0 ^ this.In.GetHashCode() ^ this.Out.GetHashCode();
}
}
private class NodeBranch
{
private List<Node> _nodes = new List<Node>();
private readonly Func<TKey, TKey, bool> _key_comparer;
private readonly Func<T, T, bool> _comparer;
public IEnumerable<Node> Nodes
{
get
{
return _nodes;
}
}
public NodeBranch(Node first, Func<T, T, bool> comparer, Func<TKey, TKey, bool> key_comparer)
{
_nodes.Add(first);
_key_comparer = key_comparer;
_comparer = comparer;
}
public NodeBranch(IEnumerable<Node> nodes, Func<T, T, bool> comparer, Func<TKey, TKey, bool> key_comparer)
{
_nodes.AddRange(nodes);
_key_comparer = key_comparer;
_comparer = comparer;
}
public List<T> Extract()
{
return _nodes.Select(n => n.Value).ToList();
}
public BranchTestResult Test(Node node)
{
var result = new BranchTestResult { HasInteraction = false, NewBranch = null };
if (_key_comparer(_nodes.Last().Out, node.In))
{
_nodes.Add(node);
result.HasInteraction = true;
}
else if (_key_comparer(_nodes.First().In, node.Out))
{
_nodes.Insert(0, node);
result.HasInteraction = true;
}
else
{
for (int i = 0; i < _nodes.Count; i++)
{
var current = _nodes[i];
if (_key_comparer(current.Out, node.In))
{
var list = _nodes.Take(i + 1).ToList();
list.Add(node);
result.NewBranch = new NodeBranch(list, _comparer, _key_comparer);
result.HasInteraction = true;
break;
}
else if (_key_comparer(current.In, node.Out))
{
var list = _nodes.Skip(i).ToList();
list.Insert(0, node);
result.NewBranch = new NodeBranch(list, _comparer, _key_comparer);
result.HasInteraction = true;
break;
}
}
}
return result;
}
/// <summary>
/// Проверка возможности объединения с другой ветвью, если текущая является ее началом или продолжением
/// </summary>
public bool TryJoin(NodeBranch other)
{
if (other == null)
{
return false;
}
if (this._nodes.First().Eq(other._nodes.Last(), _comparer ,_key_comparer))
{
this._nodes = other._nodes.Union(this._nodes).ToList();
return true;
}
else if (this._nodes.Last().Eq(other._nodes.First(), _comparer, _key_comparer))
{
this._nodes = this._nodes.Union(other._nodes).ToList();
return true;
}
return false;
}
public override bool Equals(object obj)
{
return this.Equals(obj as NodeBranch);
}
public override int GetHashCode()
{
return this._nodes.GetEnumHashCode();
}
public bool Equals(NodeBranch other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
return this._nodes.OrderingEquals(other._nodes);
}
}
#endregion
private readonly Func<T, TKey> _inKeyExtractor;
private readonly Func<T, TKey> _outKeyExtractor;
private readonly Func<T, T, bool> _comparer;
private readonly Func<TKey, TKey, bool> _key_comparer;
/// <summary>
/// Конструктор
/// </summary>
/// <param name="inKeyExtractor">Экстрактор входной связи элемента</param>
/// <param name="outKeyExtractor">Экстрактор исходящей связи элемента</param>
public ItemFieldToTreeConverter(Func<T, TKey> inKeyExtractor,
Func<T, TKey> outKeyExtractor,
Func<T, T, bool> comparer = null,
Func<TKey, TKey, bool> key_comparer = null)
{
if (inKeyExtractor == null)
{
throw new ArgumentNullException(nameof(inKeyExtractor));
}
if (outKeyExtractor == null)
{
throw new ArgumentNullException(nameof(outKeyExtractor));
}
_inKeyExtractor = inKeyExtractor;
_outKeyExtractor = outKeyExtractor;
_comparer = comparer;
_key_comparer = key_comparer ?? new Func<TKey, TKey, bool>((k1, k2) =>
{
if (k1 == null && k2 == null) return true;
if (k1 == null) return false;
if (k2 == null) return false;
return k1.Equals(k2);
});
}
/// <summary>
/// Преобразование набора элементов к набору ветвей
/// </summary>
public IEnumerable<List<T>> Convert(IEnumerable<T> entries)
{
if (entries == null || entries.Any() == false)
{
return Enumerable.Empty<List<T>>();
}
var iterator = new SparseIterator<T>(entries);
var result = new List<NodeBranch>();
if (iterator.MoveNext() != -1)
{
result.Add(new NodeBranch(new Node
{
Value = iterator.Current,
In = _inKeyExtractor(iterator.Current),
Out = _outKeyExtractor(iterator.Current)
}, _comparer, _key_comparer));
iterator.Exclude();
}
else
{
return Enumerable.Empty<List<T>>();
}
int index;
var cachee = new Dictionary<int, Node>();
while ((index = iterator.MoveNext()) != -1)
{
if (cachee.ContainsKey(index) == false)
{
cachee.Add(index, new Node
{
Value = iterator.Current,
In = _inKeyExtractor(iterator.Current),
Out = _outKeyExtractor(iterator.Current)
});
}
var node = cachee[index];
bool included = false;
var include = new List<NodeBranch>();
foreach (var branch in result)
{
var tr = branch.Test(node);
if (tr.HasInteraction)
{
included = true;
if (tr.NewBranch != null)
{
include.Add(tr.NewBranch);
}
}
}
if (included == false)
{
result.Add(new NodeBranch(node, _comparer, _key_comparer));
}
iterator.Exclude();
if (include.Count > 0) result.AddRange(include);
}
// Проверить, если одна ветка является началом, или продолжением другой, выполнить склейки
for (int i = 0; i < result.Count - 1; i++)
{
var left = result[i];
for (int j = i + 1; j < result.Count; j++)
{
var right = result[j];
if (IsNodeBrunchEquals(left, right) || left.TryJoin(right))
{
result.RemoveAt(j); j--;
}
}
}
return result.Select(e => e.Extract());
}
private bool IsNodeBrunchEquals(NodeBranch first, NodeBranch second)
{
if (first == null && second == null)
return true;
if (first == null)
return false;
if (second == null)
return false;
if (_comparer == null)
return first.Equals(second);
if (ReferenceEquals(first, second))
return true;
var f_arr = first.Nodes.ToArray();
var s_arr = second.Nodes.ToArray();
if (f_arr.Length != s_arr.Length)
return false;
for (int i = 0; i < f_arr.Length; i++)
{
var fi = f_arr[i];
var si = s_arr[i];
if (_key_comparer(fi.In, si.In) == false) return false;
if (_key_comparer(fi.Out, si.Out) == false) return false;
if (_comparer(fi.Value, si.Value) == false) return false;
}
return true;
}
}
}

Powered by TurnKey Linux.