tinytrie 0.1.0a0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinytrie
3
+ Version: 0.1.0a0
4
+ Summary: A minimal type-safe trie (prefix tree) implementation in Python.
5
+ Author-email: Jifeng Wu <jifengwu2k@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jifengwu2k/tinytrie
8
+ Project-URL: Bug Tracker, https://github.com/jifengwu2k/tinytrie/issues
9
+ Keywords: trie,prefix tree,data structure,python
10
+ Classifier: Programming Language :: Python :: 2
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=2
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # TinyTrie
19
+
20
+ A minimal and type-safe trie (prefix tree) implementation for Python 2+.
21
+
22
+ ## Features
23
+
24
+ - **Typed**: Works with arbitrary key and value types (`Generic[K, V]`)
25
+ - **Minimal**: Only essential functionalities
26
+ - **Efficient**: Memory-efficient with `__slots__`
27
+ - **Iterable**: Easily traverse and list all stored sequences
28
+ - **No external dependencies** (except `typing` on Python <3.5)
29
+
30
+ ## Basic Operations
31
+
32
+ ```python
33
+ from tinytrie import *
34
+
35
+ # Create a trie with character (`str`) keys and integer values
36
+ root = TrieNode[str, int]()
37
+
38
+ # Insert some words with values
39
+ update(root, "apple", 1)
40
+ update(root, "app", 2)
41
+ update(root, "banana", 3)
42
+ update(root, "band", 4)
43
+
44
+ # Search for existing words
45
+ assert search(root, "apple").value == 1
46
+ assert search(root, "app").value == 2
47
+ assert search(root, "banana").value == 3
48
+
49
+ # Search for non-existent words
50
+ assert search(root, "orange") is None
51
+ assert search(root, "appetizer") is None
52
+
53
+ update(root, "apple", 10)
54
+ assert search(root, "apple").value == 10 # Value updated
55
+
56
+ # Insert a new word
57
+ update(root, "orange", 5)
58
+ assert search(root, "orange").value == 5
59
+
60
+ # Delete "apple", "app" remains
61
+ assert delete(root, "apple") is True
62
+ assert search(root, "apple") is None
63
+ assert delete(root, "apple") is False
64
+ assert search(root, "app") is not None
65
+
66
+ # Add back "apple", delete "app", "apple" remains
67
+ update(root, "apple", 10)
68
+ assert delete(root, "app") is True
69
+ assert search(root, "app") is None
70
+ assert delete(root, "app") is False
71
+ assert search(root, "apple") is not None
72
+
73
+ # Try to delete non-existent words
74
+ assert delete(root, "ban") is False
75
+ assert delete(root, "appetizer") is False
76
+
77
+ # Get common prefix from root (no common prefix)
78
+ prefix, node = longest_common_prefix(root)
79
+ assert prefix == [] # No common prefix among all words
80
+
81
+ # Get common prefix from "b" subtree
82
+ prefix, node = longest_common_prefix(root.children["b"])
83
+ assert prefix == ["a", "n"] # Common between "banana" and "band" after "b"
84
+
85
+
86
+ # Get all words in the trie
87
+ words = ["".join(s) for s, _ in collect_sequences(root)]
88
+ assert set(words) == {"apple", "banana", "band", "orange"}
89
+ ```
90
+
91
+ ## Non-String Keys Example
92
+
93
+ ```python
94
+ from tinytrie import *
95
+
96
+ # Create a trie with tuple keys
97
+ trajectory_trie = TrieNode[Tuple[int, int], str]()
98
+ update(trajectory_trie, [(1,2), (3,4)], "traj1")
99
+ update(trajectory_trie, [(1,2), (5,6)], "traj2")
100
+
101
+ assert search(trajectory_trie, [(1,2), (3,4)]).value == "traj1"
102
+ assert search(trajectory_trie, [(1,2), (5,6)]).value == "traj2"
103
+ assert search(trajectory_trie, [(1,2)]) is None # Partial path
104
+
105
+ prefix, _ = longest_common_prefix(trajectory_trie)
106
+ assert prefix == [(1, 2)]
107
+ ```
108
+
109
+ ## Contributing
110
+
111
+ Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.
112
+
113
+ ## License
114
+
115
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,6 @@
1
+ tinytrie.py,sha256=hT5rc8cP9-CDVJ0-_Z-jT062gVDIBOlvpiqs6meAfpg,5063
2
+ tinytrie-0.1.0a0.dist-info/licenses/LICENSE,sha256=FZ9XWedK_wQ4wfqVanrQVQpArRHDkxwxic2rgii1pZg,1066
3
+ tinytrie-0.1.0a0.dist-info/METADATA,sha256=vmwIxccv50ZXy14D929swdPQcJEHXQYm9aGTRXkdPRU,3434
4
+ tinytrie-0.1.0a0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
5
+ tinytrie-0.1.0a0.dist-info/top_level.txt,sha256=LmhFpKkudLkEcL3grI6tGPlXow6ouTLGawd4KyatyVw,9
6
+ tinytrie-0.1.0a0.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
6
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jifeng Wu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ tinytrie
tinytrie.py ADDED
@@ -0,0 +1,162 @@
1
+ # tinytrie: A minimal and type-safe trie (prefix tree) implementation in Python.
2
+ # Copyright (c) 2025 Jifeng Wu
3
+ # Licensed under the MIT License. See LICENSE file in the project root for full license information.
4
+ from typing import TypeVar, Generic, Dict, Optional, Sequence, List, Tuple, Iterator
5
+
6
+ K = TypeVar("K")
7
+ V = TypeVar("V")
8
+
9
+ class TrieNode(Generic[K, V]):
10
+ """A node in the trie structure.
11
+
12
+ Attributes:
13
+ children: Dictionary mapping keys to child nodes
14
+ is_end: Boolean indicating if this node completes a sequence
15
+ value: Optional value associated with this node if is_end is True"""
16
+ __slots__ = ("children", "is_end", "value")
17
+
18
+ def __init__(self):
19
+ self.children = {} # type: Dict[K, TrieNode[K, V]]
20
+
21
+ self.is_end = False # type: bool
22
+ self.value = None # type: Optional[V]
23
+
24
+
25
+ def search(root, sequence, index=0):
26
+ # type: (TrieNode[K, V], Sequence[K], int) -> Optional[TrieNode[K, V]]
27
+ """Search for a sequence in the trie.
28
+
29
+ Args:
30
+ root: Root node of the trie
31
+ sequence: Sequence of keys to search for
32
+ index: Current index in sequence (used internally for recursion)
33
+
34
+ Returns:
35
+ The terminal node if found, None otherwise
36
+
37
+ Time complexity: O(n) where n is length of sequence"""
38
+ if index >= len(sequence):
39
+ if root.is_end:
40
+ return root
41
+ else:
42
+ return None
43
+ else:
44
+ key = sequence[index]
45
+ if key not in root.children:
46
+ return None
47
+ else:
48
+ return search(root.children[key], sequence, index + 1)
49
+
50
+
51
+ def update(root, sequence, value=None, index=0):
52
+ # type: (TrieNode[K, V], Sequence[K], Optional[V], int) -> TrieNode[K, V]
53
+ """Search for a sequence, creating nodes if not found, and set a value for the terminal node.
54
+
55
+ Args:
56
+ root: Root node of the trie
57
+ sequence: Sequence of keys to insert
58
+ value: Value to associate with the terminal node
59
+ index: Current index in sequence (used internally for recursion)
60
+
61
+ Returns:
62
+ The terminal node for the sequence
63
+
64
+ Time complexity: O(n) where n is length of sequence"""
65
+ if index >= len(sequence):
66
+ if not root.is_end:
67
+ root.is_end = True
68
+
69
+ root.value = value
70
+ return root
71
+ else:
72
+ key = sequence[index]
73
+ if key not in root.children:
74
+ root.children[key] = TrieNode()
75
+ return update(root.children[key], sequence, value, index + 1)
76
+
77
+
78
+ def delete(root, sequence, index=0):
79
+ # type: (TrieNode[K, V], Sequence[K], int) -> bool
80
+ """Delete a sequence from the trie.
81
+
82
+ Args:
83
+ root: Root node of the trie
84
+ sequence: Sequence to delete
85
+ index: Current index in sequence (used internally for recursion)
86
+
87
+ Returns:
88
+ True if sequence was found and deleted, False otherwise
89
+
90
+ Time complexity: O(n) where n is length of sequence"""
91
+ if index >= len(sequence):
92
+ if not root.is_end:
93
+ return False # Sequence not found
94
+ else:
95
+ root.is_end = False
96
+ root.value = None
97
+ return True # Sequence found and marked as deleted
98
+ else:
99
+ key = sequence[index]
100
+ if key not in root.children:
101
+ return False # Sequence not found
102
+ else:
103
+ child = root.children[key]
104
+ deleted = delete(child, sequence, index + 1)
105
+
106
+ if not deleted:
107
+ return False
108
+ else:
109
+ # Prune the child if it's now a leaf node and not a terminal
110
+ if not child.is_end and not child.children:
111
+ del root.children[key]
112
+
113
+ return True
114
+
115
+
116
+ def longest_common_prefix(root):
117
+ # type: (TrieNode[K, V]) -> Tuple[Sequence[K], TrieNode[K, V]]
118
+ """Find the longest sequence that is a prefix of all sequences in the trie.
119
+
120
+ Args:
121
+ root: Root node of the trie
122
+
123
+ Returns:
124
+ Tuple of (prefix sequence, terminal node)
125
+
126
+ Time complexity: O(m) where m is length of longest common prefix"""
127
+ prefix = []
128
+ node = root
129
+
130
+ while True:
131
+ # Stop if node is end of word or has multiple children
132
+ if node.is_end or len(node.children) != 1:
133
+ break
134
+ # Get the only child
135
+ key, next_node = next(iter(node.children.items()))
136
+ prefix.append(key)
137
+ node = next_node
138
+
139
+ return prefix, node
140
+
141
+
142
+ def collect_sequences(root, prefix=None):
143
+ # type: (TrieNode[K, V], Optional[List[K]]) -> Iterator[Tuple[List[K], TrieNode[K, V]]]
144
+ """Generate all sequences stored in the trie.
145
+ Args:
146
+ root: Root node of the trie
147
+ prefix: Current prefix (used internally for recursion)
148
+
149
+ Yields:
150
+ Tuples of (sequence, terminal node) for all stored sequences
151
+
152
+ Time complexity: O(n) per sequence where n is average sequence length"""
153
+ if prefix is None:
154
+ prefix = []
155
+
156
+ if root.is_end:
157
+ yield prefix.copy(), root
158
+
159
+ for key, child in root.children.items():
160
+ prefix.append(key)
161
+ yield from collect_sequences(child, prefix)
162
+ prefix.pop()