hashrope 0.1.0__tar.gz
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.
- hashrope-0.1.0/.gitignore +18 -0
- hashrope-0.1.0/CHANGELOG.md +17 -0
- hashrope-0.1.0/LICENSE +21 -0
- hashrope-0.1.0/PKG-INFO +191 -0
- hashrope-0.1.0/README.md +162 -0
- hashrope-0.1.0/hashrope/__init__.py +64 -0
- hashrope-0.1.0/hashrope/polynomial_hash.py +241 -0
- hashrope-0.1.0/hashrope/rope.py +601 -0
- hashrope-0.1.0/hashrope/sliding.py +210 -0
- hashrope-0.1.0/pyproject.toml +54 -0
- hashrope-0.1.0/tests/__init__.py +0 -0
- hashrope-0.1.0/tests/conftest.py +16 -0
- hashrope-0.1.0/tests/test_polynomial_hash.py +167 -0
- hashrope-0.1.0/tests/test_rope.py +282 -0
- hashrope-0.1.0/tests/test_sliding.py +134 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-04-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release extracted from UHC (Unified Hash-Compression Engine).
|
|
7
|
+
- PolynomialHash: Mersenne-prime polynomial hashing with O(log n) power caching.
|
|
8
|
+
- phi: Geometric accumulator via inverse-free repeated doubling in O(log q).
|
|
9
|
+
- Leaf, Internal, RepeatNode: Immutable hash rope nodes (Definition 6).
|
|
10
|
+
- BB[2/7] weight-balanced tree with single/double rotations (Definition 7).
|
|
11
|
+
- rope_concat: O(k * |h_L - h_R|) balanced concatenation (Theorem 10).
|
|
12
|
+
- rope_split: O(k * log w) split at arbitrary byte position (Theorem 7).
|
|
13
|
+
- rope_repeat: O(k * log q) repetition via RepeatNode (Theorem 8).
|
|
14
|
+
- rope_substr_hash: O(k * log w) allocation-free substring hashing (Theorem 9).
|
|
15
|
+
- SlidingWindow: Bounded-memory streaming hash with eviction (Theorem 11).
|
|
16
|
+
- validate_rope: Invariant checker for all nine rope invariants.
|
|
17
|
+
- rope_from_bytes, rope_to_bytes: Construction and reconstruction helpers.
|
hashrope-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muntaser Syed
|
|
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.
|
hashrope-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hashrope
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hash rope: a BB[2/7] balanced binary tree with polynomial hash metadata for O(log n) concat, split, substring hashing, and O(log q) repetition encoding.
|
|
5
|
+
Project-URL: Homepage, https://github.com/jemsbhai/hashrope
|
|
6
|
+
Project-URL: Repository, https://github.com/jemsbhai/hashrope
|
|
7
|
+
Project-URL: Issues, https://github.com/jemsbhai/hashrope/issues
|
|
8
|
+
Author-email: Muntaser Syed <muntaser@ieee.org>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: balanced-tree,content-addressing,data-structure,hash,mersenne-prime,polynomial-hash,rope
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# hashrope
|
|
31
|
+
|
|
32
|
+
A BB[2/7] weight-balanced binary tree augmented with polynomial hash metadata at every node.
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/hashrope/)
|
|
35
|
+
[](https://pypi.org/project/hashrope/)
|
|
36
|
+
[](https://opensource.org/licenses/MIT)
|
|
37
|
+
[](https://github.com/jemsbhai/hashrope)
|
|
38
|
+
|
|
39
|
+
**Zero dependencies. Pure Python. Python 3.10+.**
|
|
40
|
+
|
|
41
|
+
## What it does
|
|
42
|
+
|
|
43
|
+
hashrope is a rope data structure where every node carries a polynomial hash. This enables:
|
|
44
|
+
|
|
45
|
+
- **O(k log w) concat and split** with strict BB[2/7] rebalancing via recursive join
|
|
46
|
+
- **O(k log q) repetition encoding** via RepeatNode -- represents "repeat this subtree q times" without materializing the data
|
|
47
|
+
- **O(k log w) substring hashing** without allocating new nodes
|
|
48
|
+
- **O(1) whole-sequence hash** at any point during streaming ingestion
|
|
49
|
+
- **Bounded-memory sliding window** that evicts old data while maintaining a running hash of everything seen
|
|
50
|
+
|
|
51
|
+
All arithmetic uses Mersenne prime modular reduction (bit-shift, no division).
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
pip install hashrope
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from hashrope import PolynomialHash, Leaf, rope_concat, rope_split, rope_substr_hash, rope_hash
|
|
63
|
+
|
|
64
|
+
h = PolynomialHash()
|
|
65
|
+
|
|
66
|
+
# Build a rope
|
|
67
|
+
a = Leaf(b"hello ", h)
|
|
68
|
+
b = Leaf(b"world", h)
|
|
69
|
+
ab = rope_concat(a, b, h)
|
|
70
|
+
|
|
71
|
+
# Whole-string hash matches direct computation
|
|
72
|
+
assert rope_hash(ab) == h.hash(b"hello world")
|
|
73
|
+
|
|
74
|
+
# Split at any byte position
|
|
75
|
+
left, right = rope_split(ab, 6, h)
|
|
76
|
+
|
|
77
|
+
# Substring hash without allocation
|
|
78
|
+
sub_h = rope_substr_hash(ab, 0, 5, h)
|
|
79
|
+
assert sub_h == h.hash(b"hello")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### RepeatNode
|
|
83
|
+
|
|
84
|
+
Encode repetitions in O(log q) time and O(1) space:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from hashrope import PolynomialHash, Leaf, rope_repeat, rope_hash
|
|
88
|
+
|
|
89
|
+
h = PolynomialHash()
|
|
90
|
+
pattern = Leaf(b"ab", h)
|
|
91
|
+
repeated = rope_repeat(pattern, 1000, h) # "ab" x 1000
|
|
92
|
+
|
|
93
|
+
# Hash computed in O(log 1000), not O(2000)
|
|
94
|
+
assert rope_hash(repeated) == h.hash(b"ab" * 1000)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### SlidingWindow
|
|
98
|
+
|
|
99
|
+
Bounded-memory streaming hash with copy-from-window support:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from hashrope import PolynomialHash, SlidingWindow
|
|
103
|
+
|
|
104
|
+
h = PolynomialHash()
|
|
105
|
+
sw = SlidingWindow(d_max=1024, m_max=256)
|
|
106
|
+
|
|
107
|
+
# Stream data in
|
|
108
|
+
sw.append_bytes(b"hello ")
|
|
109
|
+
sw.append_bytes(b"world")
|
|
110
|
+
|
|
111
|
+
# Hash of everything seen so far
|
|
112
|
+
assert sw.current_hash() == h.hash(b"hello world")
|
|
113
|
+
|
|
114
|
+
# Copy-from-window (like LZ77 back-references)
|
|
115
|
+
sw.append_copy(offset=5, length=5) # copies "world" again
|
|
116
|
+
assert sw.final_hash() == h.hash(b"hello worldworld")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API reference
|
|
120
|
+
|
|
121
|
+
### Polynomial hash
|
|
122
|
+
|
|
123
|
+
| Function / Class | Description |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `PolynomialHash(prime, base)` | Hash function over Z/pZ. Default: p = 2^61 - 1, base = 131 |
|
|
126
|
+
| `PolynomialHash.hash(data)` | H(data) with +1 byte offset to prevent zero collisions |
|
|
127
|
+
| `PolynomialHash.hash_concat(h_a, len_b, h_b)` | H(A \|\| B) from H(A), \|B\|, H(B) in O(1) |
|
|
128
|
+
| `PolynomialHash.hash_repeat(h_s, d, q)` | H(S^q) from H(S) and \|S\| in O(log q) |
|
|
129
|
+
| `PolynomialHash.hash_overlap(h_p, d, l, h_prefix)` | H(P^q \|\| P[0..r-1]) for overlapping references |
|
|
130
|
+
| `phi(q, alpha, p)` | Geometric accumulator: sum of alpha^i for i in [0, q) in O(log q) |
|
|
131
|
+
| `mersenne_mod(a, p)` | Bit-shift reduction mod Mersenne prime |
|
|
132
|
+
| `mersenne_mul(a, b, p)` | Modular multiply via mersenne_mod |
|
|
133
|
+
| `MERSENNE_61` | 2^61 - 1 (default prime) |
|
|
134
|
+
| `MERSENNE_127` | 2^127 - 1 (high-security prime) |
|
|
135
|
+
|
|
136
|
+
### Rope nodes
|
|
137
|
+
|
|
138
|
+
| Class | Description |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `Leaf(data, h)` | Immutable leaf storing a byte sequence. Weight = 1 |
|
|
141
|
+
| `Internal(left, right, h)` | Internal node. Hash = H(left) \* x^len(right) + H(right) |
|
|
142
|
+
| `RepeatNode(child, reps, h)` | Represents child^reps. Hash via geometric accumulator |
|
|
143
|
+
|
|
144
|
+
### Rope operations
|
|
145
|
+
|
|
146
|
+
| Function | Time | Description |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `rope_concat(left, right, h)` | O(k \|h_L - h_R\|) | Concatenate with recursive join and BB[2/7] rebalancing |
|
|
149
|
+
| `rope_split(node, pos, h)` | O(k log w) | Split at byte position |
|
|
150
|
+
| `rope_repeat(node, q, h)` | O(k log q) | Create RepeatNode |
|
|
151
|
+
| `rope_substr_hash(node, start, length, h)` | O(k log w) | Substring hash without allocation |
|
|
152
|
+
| `rope_len(node)` | O(1) | Total byte length |
|
|
153
|
+
| `rope_hash(node)` | O(1) | Hash of represented string |
|
|
154
|
+
| `rope_from_bytes(data, h)` | O(n) | Create Leaf from bytes |
|
|
155
|
+
| `rope_to_bytes(node)` | O(n) | Reconstruct bytes (for testing) |
|
|
156
|
+
| `validate_rope(node)` | O(w) | Assert all 9 invariants hold |
|
|
157
|
+
|
|
158
|
+
### SlidingWindow
|
|
159
|
+
|
|
160
|
+
| Method | Description |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `SlidingWindow(d_max, m_max, prime, base)` | Create window. W = d_max + m_max |
|
|
163
|
+
| `.append_bytes(data)` | Append literal bytes |
|
|
164
|
+
| `.append_copy(offset, length)` | Copy from window (handles overlapping) |
|
|
165
|
+
| `.current_hash()` | H(everything seen) in O(1) |
|
|
166
|
+
| `.final_hash()` | Same as current_hash (alias) |
|
|
167
|
+
| `.window_len` | Current window size in bytes |
|
|
168
|
+
| `.pos` | Total bytes ingested |
|
|
169
|
+
|
|
170
|
+
## Mathematical foundation
|
|
171
|
+
|
|
172
|
+
The data structure and its correctness proofs come from the compressed-domain hashing framework (24 theorems, 14 lemmas, 5 corollaries). Key results used by hashrope:
|
|
173
|
+
|
|
174
|
+
| Result | Statement |
|
|
175
|
+
|---|---|
|
|
176
|
+
| Theorem 1 (Concatenation) | H(A \|\| B) = H(A) * x^{\|B\|} + H(B) |
|
|
177
|
+
| Theorem 2 (Repetition) | H(S^q) = H(S) * Phi(q, x^d) |
|
|
178
|
+
| Theorem 3 (Phi computation) | Phi(q, alpha) computable in O(log q) without modular inverse |
|
|
179
|
+
| Theorem 6 (Join) | Recursive join with BB[2/7] rebalancing |
|
|
180
|
+
| Theorem 7 (Split) | Split in O(k log w) with RepeatNode splittability |
|
|
181
|
+
| Theorem 8 (Repeat) | RepeatNode in O(k log q), O(1) space |
|
|
182
|
+
| Theorem 9 (SubstrHash) | Allocation-free substring hashing in O(k log w) |
|
|
183
|
+
| Theorem 11 (Sliding window) | Bounded-memory streaming with eviction |
|
|
184
|
+
|
|
185
|
+
## Provenance
|
|
186
|
+
|
|
187
|
+
Extracted from [UHC](https://github.com/jemsbhai/uhc) (Unified Hash-Compression Engine). The rope and polynomial hash modules are verbatim copies with import paths updated. The sliding window is adapted from UHC's SlidingRopeState with the LZ77 dependency removed.
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
hashrope-0.1.0/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# hashrope
|
|
2
|
+
|
|
3
|
+
A BB[2/7] weight-balanced binary tree augmented with polynomial hash metadata at every node.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/hashrope/)
|
|
6
|
+
[](https://pypi.org/project/hashrope/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://github.com/jemsbhai/hashrope)
|
|
9
|
+
|
|
10
|
+
**Zero dependencies. Pure Python. Python 3.10+.**
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
hashrope is a rope data structure where every node carries a polynomial hash. This enables:
|
|
15
|
+
|
|
16
|
+
- **O(k log w) concat and split** with strict BB[2/7] rebalancing via recursive join
|
|
17
|
+
- **O(k log q) repetition encoding** via RepeatNode -- represents "repeat this subtree q times" without materializing the data
|
|
18
|
+
- **O(k log w) substring hashing** without allocating new nodes
|
|
19
|
+
- **O(1) whole-sequence hash** at any point during streaming ingestion
|
|
20
|
+
- **Bounded-memory sliding window** that evicts old data while maintaining a running hash of everything seen
|
|
21
|
+
|
|
22
|
+
All arithmetic uses Mersenne prime modular reduction (bit-shift, no division).
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
pip install hashrope
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from hashrope import PolynomialHash, Leaf, rope_concat, rope_split, rope_substr_hash, rope_hash
|
|
34
|
+
|
|
35
|
+
h = PolynomialHash()
|
|
36
|
+
|
|
37
|
+
# Build a rope
|
|
38
|
+
a = Leaf(b"hello ", h)
|
|
39
|
+
b = Leaf(b"world", h)
|
|
40
|
+
ab = rope_concat(a, b, h)
|
|
41
|
+
|
|
42
|
+
# Whole-string hash matches direct computation
|
|
43
|
+
assert rope_hash(ab) == h.hash(b"hello world")
|
|
44
|
+
|
|
45
|
+
# Split at any byte position
|
|
46
|
+
left, right = rope_split(ab, 6, h)
|
|
47
|
+
|
|
48
|
+
# Substring hash without allocation
|
|
49
|
+
sub_h = rope_substr_hash(ab, 0, 5, h)
|
|
50
|
+
assert sub_h == h.hash(b"hello")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### RepeatNode
|
|
54
|
+
|
|
55
|
+
Encode repetitions in O(log q) time and O(1) space:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from hashrope import PolynomialHash, Leaf, rope_repeat, rope_hash
|
|
59
|
+
|
|
60
|
+
h = PolynomialHash()
|
|
61
|
+
pattern = Leaf(b"ab", h)
|
|
62
|
+
repeated = rope_repeat(pattern, 1000, h) # "ab" x 1000
|
|
63
|
+
|
|
64
|
+
# Hash computed in O(log 1000), not O(2000)
|
|
65
|
+
assert rope_hash(repeated) == h.hash(b"ab" * 1000)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### SlidingWindow
|
|
69
|
+
|
|
70
|
+
Bounded-memory streaming hash with copy-from-window support:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from hashrope import PolynomialHash, SlidingWindow
|
|
74
|
+
|
|
75
|
+
h = PolynomialHash()
|
|
76
|
+
sw = SlidingWindow(d_max=1024, m_max=256)
|
|
77
|
+
|
|
78
|
+
# Stream data in
|
|
79
|
+
sw.append_bytes(b"hello ")
|
|
80
|
+
sw.append_bytes(b"world")
|
|
81
|
+
|
|
82
|
+
# Hash of everything seen so far
|
|
83
|
+
assert sw.current_hash() == h.hash(b"hello world")
|
|
84
|
+
|
|
85
|
+
# Copy-from-window (like LZ77 back-references)
|
|
86
|
+
sw.append_copy(offset=5, length=5) # copies "world" again
|
|
87
|
+
assert sw.final_hash() == h.hash(b"hello worldworld")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API reference
|
|
91
|
+
|
|
92
|
+
### Polynomial hash
|
|
93
|
+
|
|
94
|
+
| Function / Class | Description |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `PolynomialHash(prime, base)` | Hash function over Z/pZ. Default: p = 2^61 - 1, base = 131 |
|
|
97
|
+
| `PolynomialHash.hash(data)` | H(data) with +1 byte offset to prevent zero collisions |
|
|
98
|
+
| `PolynomialHash.hash_concat(h_a, len_b, h_b)` | H(A \|\| B) from H(A), \|B\|, H(B) in O(1) |
|
|
99
|
+
| `PolynomialHash.hash_repeat(h_s, d, q)` | H(S^q) from H(S) and \|S\| in O(log q) |
|
|
100
|
+
| `PolynomialHash.hash_overlap(h_p, d, l, h_prefix)` | H(P^q \|\| P[0..r-1]) for overlapping references |
|
|
101
|
+
| `phi(q, alpha, p)` | Geometric accumulator: sum of alpha^i for i in [0, q) in O(log q) |
|
|
102
|
+
| `mersenne_mod(a, p)` | Bit-shift reduction mod Mersenne prime |
|
|
103
|
+
| `mersenne_mul(a, b, p)` | Modular multiply via mersenne_mod |
|
|
104
|
+
| `MERSENNE_61` | 2^61 - 1 (default prime) |
|
|
105
|
+
| `MERSENNE_127` | 2^127 - 1 (high-security prime) |
|
|
106
|
+
|
|
107
|
+
### Rope nodes
|
|
108
|
+
|
|
109
|
+
| Class | Description |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `Leaf(data, h)` | Immutable leaf storing a byte sequence. Weight = 1 |
|
|
112
|
+
| `Internal(left, right, h)` | Internal node. Hash = H(left) \* x^len(right) + H(right) |
|
|
113
|
+
| `RepeatNode(child, reps, h)` | Represents child^reps. Hash via geometric accumulator |
|
|
114
|
+
|
|
115
|
+
### Rope operations
|
|
116
|
+
|
|
117
|
+
| Function | Time | Description |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `rope_concat(left, right, h)` | O(k \|h_L - h_R\|) | Concatenate with recursive join and BB[2/7] rebalancing |
|
|
120
|
+
| `rope_split(node, pos, h)` | O(k log w) | Split at byte position |
|
|
121
|
+
| `rope_repeat(node, q, h)` | O(k log q) | Create RepeatNode |
|
|
122
|
+
| `rope_substr_hash(node, start, length, h)` | O(k log w) | Substring hash without allocation |
|
|
123
|
+
| `rope_len(node)` | O(1) | Total byte length |
|
|
124
|
+
| `rope_hash(node)` | O(1) | Hash of represented string |
|
|
125
|
+
| `rope_from_bytes(data, h)` | O(n) | Create Leaf from bytes |
|
|
126
|
+
| `rope_to_bytes(node)` | O(n) | Reconstruct bytes (for testing) |
|
|
127
|
+
| `validate_rope(node)` | O(w) | Assert all 9 invariants hold |
|
|
128
|
+
|
|
129
|
+
### SlidingWindow
|
|
130
|
+
|
|
131
|
+
| Method | Description |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `SlidingWindow(d_max, m_max, prime, base)` | Create window. W = d_max + m_max |
|
|
134
|
+
| `.append_bytes(data)` | Append literal bytes |
|
|
135
|
+
| `.append_copy(offset, length)` | Copy from window (handles overlapping) |
|
|
136
|
+
| `.current_hash()` | H(everything seen) in O(1) |
|
|
137
|
+
| `.final_hash()` | Same as current_hash (alias) |
|
|
138
|
+
| `.window_len` | Current window size in bytes |
|
|
139
|
+
| `.pos` | Total bytes ingested |
|
|
140
|
+
|
|
141
|
+
## Mathematical foundation
|
|
142
|
+
|
|
143
|
+
The data structure and its correctness proofs come from the compressed-domain hashing framework (24 theorems, 14 lemmas, 5 corollaries). Key results used by hashrope:
|
|
144
|
+
|
|
145
|
+
| Result | Statement |
|
|
146
|
+
|---|---|
|
|
147
|
+
| Theorem 1 (Concatenation) | H(A \|\| B) = H(A) * x^{\|B\|} + H(B) |
|
|
148
|
+
| Theorem 2 (Repetition) | H(S^q) = H(S) * Phi(q, x^d) |
|
|
149
|
+
| Theorem 3 (Phi computation) | Phi(q, alpha) computable in O(log q) without modular inverse |
|
|
150
|
+
| Theorem 6 (Join) | Recursive join with BB[2/7] rebalancing |
|
|
151
|
+
| Theorem 7 (Split) | Split in O(k log w) with RepeatNode splittability |
|
|
152
|
+
| Theorem 8 (Repeat) | RepeatNode in O(k log q), O(1) space |
|
|
153
|
+
| Theorem 9 (SubstrHash) | Allocation-free substring hashing in O(k log w) |
|
|
154
|
+
| Theorem 11 (Sliding window) | Bounded-memory streaming with eviction |
|
|
155
|
+
|
|
156
|
+
## Provenance
|
|
157
|
+
|
|
158
|
+
Extracted from [UHC](https://github.com/jemsbhai/uhc) (Unified Hash-Compression Engine). The rope and polynomial hash modules are verbatim copies with import paths updated. The sliding window is adapted from UHC's SlidingRopeState with the LZ77 dependency removed.
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hashrope -- Hash rope data structure with algebraic content addressing.
|
|
3
|
+
|
|
4
|
+
A balanced binary tree (BB[2/7]) augmented with polynomial hash metadata
|
|
5
|
+
at every node. Supports O(log w) concat, split, and substring hashing,
|
|
6
|
+
plus O(log q) repetition encoding via RepeatNode and geometric accumulator.
|
|
7
|
+
|
|
8
|
+
All arithmetic is performed modulo a Mersenne prime using bit-shift
|
|
9
|
+
reduction (no expensive division).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
from hashrope.polynomial_hash import (
|
|
15
|
+
PolynomialHash,
|
|
16
|
+
phi,
|
|
17
|
+
mersenne_mod,
|
|
18
|
+
mersenne_mul,
|
|
19
|
+
MERSENNE_61,
|
|
20
|
+
MERSENNE_127,
|
|
21
|
+
)
|
|
22
|
+
from hashrope.rope import (
|
|
23
|
+
Leaf,
|
|
24
|
+
Internal,
|
|
25
|
+
RepeatNode,
|
|
26
|
+
Node,
|
|
27
|
+
rope_concat,
|
|
28
|
+
rope_split,
|
|
29
|
+
rope_repeat,
|
|
30
|
+
rope_substr_hash,
|
|
31
|
+
rope_len,
|
|
32
|
+
rope_hash,
|
|
33
|
+
rope_from_bytes,
|
|
34
|
+
rope_to_bytes,
|
|
35
|
+
validate_rope,
|
|
36
|
+
)
|
|
37
|
+
from hashrope.sliding import SlidingWindow
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Polynomial hash
|
|
41
|
+
"PolynomialHash",
|
|
42
|
+
"phi",
|
|
43
|
+
"mersenne_mod",
|
|
44
|
+
"mersenne_mul",
|
|
45
|
+
"MERSENNE_61",
|
|
46
|
+
"MERSENNE_127",
|
|
47
|
+
# Rope nodes
|
|
48
|
+
"Leaf",
|
|
49
|
+
"Internal",
|
|
50
|
+
"RepeatNode",
|
|
51
|
+
"Node",
|
|
52
|
+
# Rope operations
|
|
53
|
+
"rope_concat",
|
|
54
|
+
"rope_split",
|
|
55
|
+
"rope_repeat",
|
|
56
|
+
"rope_substr_hash",
|
|
57
|
+
"rope_len",
|
|
58
|
+
"rope_hash",
|
|
59
|
+
"rope_from_bytes",
|
|
60
|
+
"rope_to_bytes",
|
|
61
|
+
"validate_rope",
|
|
62
|
+
# Sliding window
|
|
63
|
+
"SlidingWindow",
|
|
64
|
+
]
|