mirror of https://github.com/ogoun/Zero.git
				
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							285 lines
						
					
					
						
							8.6 KiB
						
					
					
				
			
		
		
	
	
							285 lines
						
					
					
						
							8.6 KiB
						
					
					
				using System.Collections.Generic;
 | 
						|
using System.Threading;
 | 
						|
using ZeroLevel.Services.Serialization;
 | 
						|
 | 
						|
namespace ZeroLevel.Services.Semantic
 | 
						|
{
 | 
						|
    public class Trie
 | 
						|
        : IBinarySerializable
 | 
						|
    {
 | 
						|
        internal class TrieNode
 | 
						|
            : IBinarySerializable
 | 
						|
        {
 | 
						|
            public char Key;
 | 
						|
            public uint? Value;
 | 
						|
            public TrieNode Parent;
 | 
						|
            public List<TrieNode> Children;
 | 
						|
 | 
						|
            public TrieNode() { }
 | 
						|
            public TrieNode(TrieNode parent) { Parent = parent; }
 | 
						|
            public void Deserialize(IBinaryReader reader)
 | 
						|
            {
 | 
						|
                this.Key = reader.ReadChar();
 | 
						|
                if (reader.ReadBoolean())
 | 
						|
                {
 | 
						|
                    this.Value = reader.ReadUInt32();
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    this.Value = null;
 | 
						|
                }
 | 
						|
                this.Children = reader.ReadCollection<TrieNode>();
 | 
						|
            }
 | 
						|
 | 
						|
            public void Serialize(IBinaryWriter writer)
 | 
						|
            {
 | 
						|
                writer.WriteChar(this.Key);
 | 
						|
                if (this.Value.HasValue)
 | 
						|
                {
 | 
						|
                    writer.WriteBoolean(true);
 | 
						|
                    writer.WriteUInt32(this.Value.Value);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    writer.WriteBoolean(false);
 | 
						|
                }
 | 
						|
                if (this.Children == null)
 | 
						|
                {
 | 
						|
                    writer.WriteInt32(0);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    writer.WriteCollection<TrieNode>(this.Children);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            internal TrieNode Append(string word, int index, bool reverse)
 | 
						|
            {
 | 
						|
                if (word.Length == index)
 | 
						|
                {
 | 
						|
                    if (!this.Value.HasValue)
 | 
						|
                    {
 | 
						|
                        return this;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (this.Children == null)
 | 
						|
                    {
 | 
						|
                        this.Children = new List<TrieNode>();
 | 
						|
                    }
 | 
						|
                    for (int i = 0; i < Children.Count; i++)
 | 
						|
                    {
 | 
						|
                        if (Children[i].Key == word[index])
 | 
						|
                        {
 | 
						|
                            return Children[i].Append(word, index + 1, reverse);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    var tn = new TrieNode(reverse ? this : null) { Key = word[index] };
 | 
						|
                    Children.Add(tn);
 | 
						|
                    return tn.Append(word, index + 1, reverse);
 | 
						|
                }
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
            internal uint? GetKey(string word, int index)
 | 
						|
            {
 | 
						|
                if (word.Length == index + 1)
 | 
						|
                {
 | 
						|
                    return this.Value;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (this.Children == null)
 | 
						|
                    {
 | 
						|
                        this.Children = new List<TrieNode>();
 | 
						|
                    }
 | 
						|
                    for (int i = 0; i < Children.Count; i++)
 | 
						|
                    {
 | 
						|
                        if (Children[i].Key == word[index])
 | 
						|
                        {
 | 
						|
                            return Children[i].GetKey(word, index + 1);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
 | 
						|
            internal void RebuildReverseIndex(TrieNode parent, Dictionary<uint, TrieNode> index)
 | 
						|
            {
 | 
						|
                this.Parent = parent;
 | 
						|
                if (this.Value.HasValue)
 | 
						|
                {
 | 
						|
                    index.Add(this.Value.Value, this);
 | 
						|
                }
 | 
						|
                if (this.Children != null)
 | 
						|
                {
 | 
						|
                    foreach (var child in this.Children)
 | 
						|
                    {
 | 
						|
                        child.RebuildReverseIndex(this, index);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            internal void DestroyReverseIndex()
 | 
						|
            {
 | 
						|
                this.Parent = null;
 | 
						|
                if (this.Children != null)
 | 
						|
                {
 | 
						|
                    foreach (var child in this.Children)
 | 
						|
                    {
 | 
						|
                        child.DestroyReverseIndex();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        internal List<TrieNode> _roots;
 | 
						|
        private int _word_index = 0;
 | 
						|
        private bool _use_reverse_index;
 | 
						|
 | 
						|
        private Dictionary<uint, TrieNode> _reverse_index;
 | 
						|
        public Trie(bool reverse_index = false)
 | 
						|
        {
 | 
						|
            _use_reverse_index = reverse_index;
 | 
						|
            if (_use_reverse_index)
 | 
						|
            {
 | 
						|
                _reverse_index = new Dictionary<uint, TrieNode>();
 | 
						|
            }
 | 
						|
            _roots = new List<TrieNode>();
 | 
						|
        }
 | 
						|
 | 
						|
        public void ToggleReverseIndex(bool enabled)
 | 
						|
        {
 | 
						|
            if (_use_reverse_index == enabled) return;
 | 
						|
            _use_reverse_index = enabled;
 | 
						|
            if (_use_reverse_index)
 | 
						|
            {
 | 
						|
                RebuildReverseIndex();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                DestroyReverseIndex();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public void Append(string word)
 | 
						|
        {
 | 
						|
            if (word.Length == 0) return;
 | 
						|
            bool found = false;
 | 
						|
            for (int i = 0; i < _roots.Count; i++)
 | 
						|
            {
 | 
						|
                if (_roots[i].Key == word[0])
 | 
						|
                {
 | 
						|
                    var node = _roots[i].Append(word, 1, _use_reverse_index);
 | 
						|
                    Thread.MemoryBarrier();
 | 
						|
                    if (node != null)
 | 
						|
                    {
 | 
						|
                        node.Value = (uint)Interlocked.Increment(ref _word_index);
 | 
						|
                        if (_use_reverse_index)
 | 
						|
                        {
 | 
						|
                            _reverse_index.Add(node.Value.Value, node);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    found = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (!found)
 | 
						|
            {
 | 
						|
                var tn = new TrieNode { Key = word[0] };
 | 
						|
                _roots.Add(tn);
 | 
						|
                var node = tn.Append(word, 1, _use_reverse_index);
 | 
						|
                Thread.MemoryBarrier();
 | 
						|
                node.Value = (uint)Interlocked.Increment(ref _word_index);
 | 
						|
                if (node != null)
 | 
						|
                {
 | 
						|
                    if (_use_reverse_index)
 | 
						|
                    {
 | 
						|
                        _reverse_index.Add(node.Value.Value, node);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public uint? Key(string word)
 | 
						|
        {
 | 
						|
            if (word?.Length == 0) return null;
 | 
						|
            for (int i = 0; i < _roots.Count; i++)
 | 
						|
            {
 | 
						|
                if (_roots[i].Key == word[0])
 | 
						|
                {
 | 
						|
                    if (word.Length == 1)
 | 
						|
                    {
 | 
						|
                        return _roots[i].Value;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        return _roots[i].GetKey(word, 1);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
 | 
						|
        public bool Contains(string word)
 | 
						|
        {
 | 
						|
            if (word?.Length == 0) return false;
 | 
						|
            for (int i = 0; i < _roots.Count; i++)
 | 
						|
            {
 | 
						|
                if (_roots[i].Key == word[0])
 | 
						|
                {
 | 
						|
                    if (word.Length == 1)
 | 
						|
                    {
 | 
						|
                        return _roots[i].Value.HasValue;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        return _roots[i].GetKey(word, 1).HasValue;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        public void Serialize(IBinaryWriter writer)
 | 
						|
        {
 | 
						|
            writer.WriteInt32(this._word_index);
 | 
						|
            writer.WriteBoolean(this._use_reverse_index);
 | 
						|
            writer.WriteCollection<TrieNode>(this._roots);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Deserialize(IBinaryReader reader)
 | 
						|
        {
 | 
						|
            this._word_index = reader.ReadInt32();
 | 
						|
            this._use_reverse_index = reader.ReadBoolean();
 | 
						|
            this._roots = reader.ReadCollection<TrieNode>();
 | 
						|
            RebuildReverseIndex();
 | 
						|
        }
 | 
						|
 | 
						|
        private void RebuildReverseIndex()
 | 
						|
        {
 | 
						|
            if (this._use_reverse_index)
 | 
						|
            {
 | 
						|
                if (_reverse_index == null)
 | 
						|
                {
 | 
						|
                    _reverse_index = new Dictionary<uint, TrieNode>();
 | 
						|
                }
 | 
						|
                foreach (var node in _roots)
 | 
						|
                {
 | 
						|
                    node.RebuildReverseIndex(null, _reverse_index);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private void DestroyReverseIndex()
 | 
						|
        {
 | 
						|
            if (_reverse_index != null)
 | 
						|
            {
 | 
						|
                _reverse_index.Clear();
 | 
						|
                _reverse_index = null;
 | 
						|
            }
 | 
						|
            foreach (var node in _roots)
 | 
						|
            {
 | 
						|
                node.DestroyReverseIndex();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |