From 78620863f7191711b710d46ba5cc9251d4f43f94 Mon Sep 17 00:00:00 2001
From: czgdp1807 <singh.23@iitj.ac.in>
Date: Sun, 30 Aug 2020 00:03:49 +0530
Subject: [PATCH 1/2] Coded and Tested

---
 pydatastructs/__init__.py                |  1 +
 pydatastructs/strings/__init__.py        |  8 +++
 pydatastructs/strings/tests/__init__.py  |  0
 pydatastructs/strings/tests/test_trie.py | 29 +++++++++
 pydatastructs/strings/trie.py            | 83 ++++++++++++++++++++++++
 pydatastructs/utils/__init__.py          |  3 +-
 pydatastructs/utils/misc_util.py         | 23 ++++++-
 7 files changed, 145 insertions(+), 2 deletions(-)
 create mode 100644 pydatastructs/strings/__init__.py
 create mode 100644 pydatastructs/strings/tests/__init__.py
 create mode 100644 pydatastructs/strings/tests/test_trie.py
 create mode 100644 pydatastructs/strings/trie.py

diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py
index ca6c0c75a..eecf5f905 100644
--- a/pydatastructs/__init__.py
+++ b/pydatastructs/__init__.py
@@ -3,3 +3,4 @@
 from .miscellaneous_data_structures import *
 from .utils import *
 from .graphs import *
+from .strings import *
diff --git a/pydatastructs/strings/__init__.py b/pydatastructs/strings/__init__.py
new file mode 100644
index 000000000..1ee05158f
--- /dev/null
+++ b/pydatastructs/strings/__init__.py
@@ -0,0 +1,8 @@
+__all__ = []
+
+from . import trie
+from .trie import (
+    Trie
+)
+
+__all__.extend(trie.__all__)
diff --git a/pydatastructs/strings/tests/__init__.py b/pydatastructs/strings/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/pydatastructs/strings/tests/test_trie.py b/pydatastructs/strings/tests/test_trie.py
new file mode 100644
index 000000000..a8291f57d
--- /dev/null
+++ b/pydatastructs/strings/tests/test_trie.py
@@ -0,0 +1,29 @@
+from pydatastructs import Trie
+
+def test_Trie():
+
+    strings = ["A", "to", "tea", "ted", "ten", "i", "in", "inn"]
+    trie = Trie()
+    for string in strings:
+        trie.insert(string)
+
+    for string in strings:
+        assert trie.is_present(string)
+
+    assert sorted(trie.strings_with_prefix("t")) == ['tea', 'ted', 'ten', 'to']
+    assert sorted(trie.strings_with_prefix("te")) == ["tea", "ted", "ten"]
+    assert trie.strings_with_prefix("i") == ["i", "in", "inn"]
+    assert trie.strings_with_prefix("a") == []
+
+    remove_order = ["to", "tea", "ted", "ten", "inn", "in", "A"]
+
+    assert trie.delete("z") is None
+
+    for string in remove_order:
+        trie.delete(string)
+        for present in strings:
+            if present == string:
+                assert not trie.is_present(present)
+            else:
+                assert trie.is_present(present)
+        strings.remove(string)
diff --git a/pydatastructs/strings/trie.py b/pydatastructs/strings/trie.py
new file mode 100644
index 000000000..88ae0471c
--- /dev/null
+++ b/pydatastructs/strings/trie.py
@@ -0,0 +1,83 @@
+from pydatastructs.utils.misc_util import TrieNode
+from collections import deque
+import copy
+
+__all__ = [
+    'Trie'
+]
+
+Stack = Queue = deque
+
+class Trie(object):
+
+    __slots__ = ['root']
+
+    def __new__(cls):
+        obj = object.__new__(cls)
+        obj.root = TrieNode()
+        return obj
+
+    def insert(self, string: str) -> None:
+        walk = self.root
+        for char in string:
+            if walk.get_child(char) is None:
+                newNode = TrieNode(char)
+                walk.add_child(newNode)
+                walk = newNode
+            else:
+                walk = walk.get_child(char)
+        walk.is_terminal = True
+
+    def is_present(self, string: str) -> bool:
+        walk = self.root
+        for char in string:
+            if walk.get_child(char) is None:
+                return False
+            walk = walk.get_child(char)
+        return True
+
+    def delete(self, string: str):
+        path = []
+        walk = self.root
+        size = len(string)
+        for i in range(size):
+            char = string[i]
+            path.append(walk)
+            if walk.get_child(char) is None:
+                return None
+            walk = walk.get_child(char)
+        path.append(walk)
+        i = len(path) - 1
+        path[i].is_terminal = False
+        while not path[i]._children and i >= 1:
+            path[i-1].remove_child(path[i].char)
+            i -= 1
+            if path[i].is_terminal:
+                return True
+        return True
+
+    def strings_with_prefix(self, string: str) -> list:
+
+        def _collect(prefix: str, node: TrieNode, strings: list) -> str:
+            TrieNode_stack = Stack()
+            TrieNode_stack.append((node, prefix))
+            while TrieNode_stack:
+                walk, curr_prefix = TrieNode_stack.pop()
+                if walk.is_terminal:
+                    strings.append(curr_prefix + walk.char)
+                for child in walk._children:
+                    TrieNode_stack.append((walk.get_child(child), curr_prefix + walk.char))
+
+        strings = []
+        prefix = ""
+        walk = self.root
+        for char in string:
+            walk = walk.get_child(char)
+            if walk is None:
+                return strings
+            prefix += char
+        if walk.is_terminal:
+            strings.append(walk.char)
+        for child in walk._children:
+            _collect(prefix, walk.get_child(child), strings)
+        return strings
diff --git a/pydatastructs/utils/__init__.py b/pydatastructs/utils/__init__.py
index f8f2d8c46..da7ec6e7f 100644
--- a/pydatastructs/utils/__init__.py
+++ b/pydatastructs/utils/__init__.py
@@ -11,6 +11,7 @@
     GraphEdge,
     Set,
     CartesianTreeNode,
-    RedBlackTreeNode
+    RedBlackTreeNode,
+    TrieNode
 )
 __all__.extend(misc_util.__all__)
diff --git a/pydatastructs/utils/misc_util.py b/pydatastructs/utils/misc_util.py
index 54b801d29..f7c4f2f3b 100644
--- a/pydatastructs/utils/misc_util.py
+++ b/pydatastructs/utils/misc_util.py
@@ -8,7 +8,8 @@
     'GraphEdge',
     'Set',
     'CartesianTreeNode',
-    'RedBlackTreeNode'
+    'RedBlackTreeNode',
+    'TrieNode'
 ]
 
 _check_type = lambda a, t: isinstance(a, t)
@@ -394,6 +395,26 @@ def __new__(cls, key, data=None):
         obj.parent, obj.size = [None]*2
         return obj
 
+class TrieNode(Node):
+
+    __slots__ = ['char', '_children', 'is_terminal']
+
+    def __new__(cls, char=None):
+        obj = Node.__new__(cls)
+        obj.char = char
+        obj._children = dict()
+        obj.is_terminal = False
+        return obj
+
+    def add_child(self, trie_node) -> None:
+        self._children[trie_node.char] = trie_node
+
+    def get_child(self, char: str):
+        return self._children.get(char, None)
+
+    def remove_child(self, char: str) -> None:
+        self._children.pop(char)
+
 def _comp(u, v, tcomp):
     """
     Overloaded comparator for comparing

From bc11a68aaed2667bc35160e62c4e242525112108 Mon Sep 17 00:00:00 2001
From: czgdp1807 <singh.23@iitj.ac.in>
Date: Sun, 30 Aug 2020 00:19:47 +0530
Subject: [PATCH 2/2] Docs done

---
 pydatastructs/strings/trie.py                 | 86 ++++++++++++++++++-
 pydatastructs/utils/misc_util.py              | 13 +++
 .../utils/tests/test_code_quality.py          |  3 +-
 3 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/pydatastructs/strings/trie.py b/pydatastructs/strings/trie.py
index 88ae0471c..5c9f39a4e 100644
--- a/pydatastructs/strings/trie.py
+++ b/pydatastructs/strings/trie.py
@@ -9,15 +9,57 @@
 Stack = Queue = deque
 
 class Trie(object):
+    """
+    Represents the trie data structure for storing strings.
+
+    Examples
+    ========
+
+    >>> from pydatastructs import Trie
+    >>> trie = Trie()
+    >>> trie.insert("a")
+    >>> trie.insert("aa")
+    >>> trie.strings_with_prefix("a")
+    ['a', 'aa']
+    >>> trie.is_present("aa")
+    True
+    >>> trie.delete("aa")
+    True
+    >>> trie.is_present("aa")
+    False
+
+    References
+    ==========
+
+    .. [1] https://en.wikipedia.org/wiki/Trie
+    """
 
     __slots__ = ['root']
 
+    @classmethod
+    def methods(cls):
+        return ['__new__', 'insert', 'is_present', 'delete',
+                'strings_with_prefix']
+
     def __new__(cls):
         obj = object.__new__(cls)
         obj.root = TrieNode()
         return obj
 
     def insert(self, string: str) -> None:
+        """
+        Inserts the given string into the trie.
+
+        Parameters
+        ==========
+
+        string: str
+
+        Returns
+        =======
+
+        None
+        """
         walk = self.root
         for char in string:
             if walk.get_child(char) is None:
@@ -29,6 +71,20 @@ def insert(self, string: str) -> None:
         walk.is_terminal = True
 
     def is_present(self, string: str) -> bool:
+        """
+        Checks if the given string is present as a prefix in the trie.
+
+        Parameters
+        ==========
+
+        string: str
+
+        Returns
+        =======
+
+        True if the given string is present as a prefix;
+        False in all other cases.
+        """
         walk = self.root
         for char in string:
             if walk.get_child(char) is None:
@@ -36,7 +92,21 @@ def is_present(self, string: str) -> bool:
             walk = walk.get_child(char)
         return True
 
-    def delete(self, string: str):
+    def delete(self, string: str) -> bool:
+        """
+        Deletes the given string from the trie.
+
+        Parameters
+        ==========
+
+        string: str
+
+        Returns
+        =======
+
+        True if successfully deleted;
+        None if the string is not present in the trie.
+        """
         path = []
         walk = self.root
         size = len(string)
@@ -57,6 +127,20 @@ def delete(self, string: str):
         return True
 
     def strings_with_prefix(self, string: str) -> list:
+        """
+        Generates a list of all strings with the given prefix.
+
+        Parameters
+        ==========
+
+        string: str
+
+        Returns
+        =======
+
+        strings: list
+            The list of strings with the given prefix.
+        """
 
         def _collect(prefix: str, node: TrieNode, strings: list) -> str:
             TrieNode_stack = Stack()
diff --git a/pydatastructs/utils/misc_util.py b/pydatastructs/utils/misc_util.py
index f7c4f2f3b..10a6b0cdc 100644
--- a/pydatastructs/utils/misc_util.py
+++ b/pydatastructs/utils/misc_util.py
@@ -396,9 +396,22 @@ def __new__(cls, key, data=None):
         return obj
 
 class TrieNode(Node):
+    """
+    Represents nodes in the trie data structure.
+
+    Parameters
+    ==========
+
+    char: The character stored in the current node.
+          Optional, by default None.
+    """
 
     __slots__ = ['char', '_children', 'is_terminal']
 
+    @classmethod
+    def methods(cls):
+        return ['__new__', 'add_child', 'get_child', 'remove_child']
+
     def __new__(cls, char=None):
         obj = Node.__new__(cls)
         obj.char = char
diff --git a/pydatastructs/utils/tests/test_code_quality.py b/pydatastructs/utils/tests/test_code_quality.py
index bd3e7f21d..ac3a8cf67 100644
--- a/pydatastructs/utils/tests/test_code_quality.py
+++ b/pydatastructs/utils/tests/test_code_quality.py
@@ -96,7 +96,8 @@ def _apis():
     pyds.DisjointSetForest, pyds.BinomialTree, pyds.TreeNode, pyds.MAryTreeNode,
     pyds.LinkedListNode, pyds.BinomialTreeNode, pyds.AdjacencyListGraphNode,
     pyds.AdjacencyMatrixGraphNode, pyds.GraphEdge, pyds.Set, pyds.BinaryIndexedTree,
-    pyds.CartesianTree, pyds.CartesianTreeNode, pyds.Treap, pyds.RedBlackTreeNode, pyds.RedBlackTree]
+    pyds.CartesianTree, pyds.CartesianTreeNode, pyds.Treap, pyds.RedBlackTreeNode, pyds.RedBlackTree,
+    pyds.Trie, pyds.TrieNode]
 
 def test_public_api():
     pyds = pydatastructs