pyrazobill 1.0.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.
Files changed (47) hide show
  1. pyrazobill-1.0.0/Guide.md +487 -0
  2. pyrazobill-1.0.0/LICENSE +201 -0
  3. pyrazobill-1.0.0/MANIFEST.in +12 -0
  4. pyrazobill-1.0.0/NOTICE +44 -0
  5. pyrazobill-1.0.0/PKG-INFO +315 -0
  6. pyrazobill-1.0.0/PyRazobill.cpp +513 -0
  7. pyrazobill-1.0.0/README.md +280 -0
  8. pyrazobill-1.0.0/includeCore/librazobill/Backend.hpp +87 -0
  9. pyrazobill-1.0.0/includeCore/librazobill/Channel.hpp +80 -0
  10. pyrazobill-1.0.0/includeCore/librazobill/Dialect.hpp +60 -0
  11. pyrazobill-1.0.0/includeCore/librazobill/FormCatalog.hpp +73 -0
  12. pyrazobill-1.0.0/includeCore/librazobill/Hash.hpp +105 -0
  13. pyrazobill-1.0.0/includeCore/librazobill/MemoryArena.hpp +162 -0
  14. pyrazobill-1.0.0/includeCore/librazobill/Razobill.hpp +143 -0
  15. pyrazobill-1.0.0/includeCore/librazobill/RazobillC.h +142 -0
  16. pyrazobill-1.0.0/includeCore/librazobill/Scanner.hpp +186 -0
  17. pyrazobill-1.0.0/includeCore/librazobill/SimdScanner.hpp +257 -0
  18. pyrazobill-1.0.0/includeCore/librazobill/Slots.hpp +96 -0
  19. pyrazobill-1.0.0/includeCore/librazobill/Stats.hpp +222 -0
  20. pyrazobill-1.0.0/includeCore/librazobill/Status.hpp +45 -0
  21. pyrazobill-1.0.0/includeCore/librazobill/StringPool.hpp +115 -0
  22. pyrazobill-1.0.0/includeCore/librazobill/ThreadPool.hpp +118 -0
  23. pyrazobill-1.0.0/includeCore/librazobill/Version.hpp +54 -0
  24. pyrazobill-1.0.0/pyproject.toml +61 -0
  25. pyrazobill-1.0.0/pyrazobill/Core.cp313-win_amd64.pyd +0 -0
  26. pyrazobill-1.0.0/pyrazobill/Core.pyi +186 -0
  27. pyrazobill-1.0.0/pyrazobill/__init__.py +133 -0
  28. pyrazobill-1.0.0/pyrazobill/py.typed +1 -0
  29. pyrazobill-1.0.0/pyrazobill.egg-info/PKG-INFO +315 -0
  30. pyrazobill-1.0.0/pyrazobill.egg-info/SOURCES.txt +45 -0
  31. pyrazobill-1.0.0/pyrazobill.egg-info/dependency_links.txt +1 -0
  32. pyrazobill-1.0.0/pyrazobill.egg-info/requires.txt +5 -0
  33. pyrazobill-1.0.0/pyrazobill.egg-info/top_level.txt +1 -0
  34. pyrazobill-1.0.0/setup.cfg +4 -0
  35. pyrazobill-1.0.0/setup.py +88 -0
  36. pyrazobill-1.0.0/srcCore/BackendAdbc.cpp +283 -0
  37. pyrazobill-1.0.0/srcCore/Channel.cpp +290 -0
  38. pyrazobill-1.0.0/srcCore/Dialect.cpp +98 -0
  39. pyrazobill-1.0.0/srcCore/Razobill.cpp +608 -0
  40. pyrazobill-1.0.0/srcCore/RazobillC.cpp +247 -0
  41. pyrazobill-1.0.0/srcCore/Slots.cpp +159 -0
  42. pyrazobill-1.0.0/srcCore/Stats.cpp +451 -0
  43. pyrazobill-1.0.0/tests/TestPyRazobill.py +317 -0
  44. pyrazobill-1.0.0/thirdpartyCore/adbc/adbc.h +2413 -0
  45. pyrazobill-1.0.0/thirdpartyCore/doctest.h +9119 -0
  46. pyrazobill-1.0.0/thirdpartyCore/duckdbcapi/duckdb.h +4545 -0
  47. pyrazobill-1.0.0/thirdpartyCore/duckdbcapi/duckdb_extension.h +1027 -0
@@ -0,0 +1,487 @@
1
+ # pyrazobill Developer & Operations Guide
2
+
3
+ This guide provides deep technical instructions for developing, embedding, and operating **pyrazobill 1.0.0** within production Python applications, streaming telemetry backends, analytical lakehouses, and information-theoretic research pipelines.
4
+
5
+ ---
6
+
7
+ ## 1. Architectural Philosophy & Safety Invariants
8
+
9
+ Python string objects are managed dynamically by the CPython heap. When passing strings into high-performance native C++ libraries, passing raw pointers or string views without lifetime guarantees risks dangling memory references once CPython garbage collects or reallocates the object.
10
+
11
+ To prevent dangling pointers with zero cognitive burden on the Python developer, `pyrazobill` encapsulates parsed results in a memory-safe `PySlotRecord`:
12
+
13
+ ```cpp
14
+ // PyRazobill.cpp
15
+ struct PySlotRecord {
16
+ std::shared_ptr<std::string> msgBuffer; // Retains ownership of the underlying buffer
17
+ core::SlotRecord slots; // String views point safely into msgBuffer
18
+ // ...
19
+ };
20
+ ```
21
+
22
+ ### Safety & Structural Invariants:
23
+ 1. **Lifetime Safety**: A `SlotRecord` can be passed across Python threads, stored in queues, or serialized without memory corruption or dangling pointer risks.
24
+ 2. **Deterministic Round-Trip (Lossless Invertibility)**: Every parsed record satisfies the invertibility law:
25
+ ```python
26
+ reconstructed = record.reconstruct()
27
+ assert reconstructed == originalMsg
28
+ ```
29
+ 3. **Zero-Underscore Convention**: All methods, functions, and properties adhere strictly to camelCase naming (`formKey`, `formKeyHash`, `parseBatch`, `auditParallel`, `computeLedger`).
30
+ 4. **Gil-Free Concurrency**: All heavy batch loops (`parseBatch`, `decomposeBatch`, `auditParallel`) release CPython's Global Interpreter Lock (GIL) via `py::gil_scoped_release`, allowing true multi-core parallel scaling across hardware threads.
31
+
32
+ ---
33
+
34
+ ## 2. Ingestion Strategies & Threading Models
35
+
36
+ CPython's Global Interpreter Lock ordinarily constrains CPU-bound Python workloads to a single core. `pyrazobill` bypasses this barrier by delegating batch processing directly to its C++20 engine with the GIL released.
37
+
38
+ ```text
39
+ ┌────────────────────────────────────────────────────────────────────────────────────────┐
40
+ │ GIL RELEASE & NATIVE THREAD POOL PIPELINE │
41
+ └────────────────────────────────────────────────────────────────────────────────────────┘
42
+ Python Main Process: `pyrazobill.parseBatch(msgs)` or `decomposeBatch(msgs)`
43
+ │
44
+ ▼
45
+ ┌────────────────────────────────────────────────────────────────────────────┐
46
+ │ py::gil_scoped_release (CPython GIL Dropped — Other Python Threads Free) │
47
+ └──────────────────────────────────────┬─────────────────────────────────────┘
48
+ │
49
+ ┌───────────────────────┼───────────────────────┐
50
+ ▼ ▼ ▼
51
+ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
52
+ │ Worker 0 (Chunk 0) │ │ Worker 1 (Chunk 1) │ │ Worker N (Chunk N) │
53
+ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤
54
+ │ SIMD AVX2 Parse │ │ SIMD AVX2 Parse │ │ SIMD AVX2 Parse │
55
+ │ Local Bump Arena │ │ Local Bump Arena │ │ Local Bump Arena │
56
+ └──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘
57
+ │ │ │
58
+ └───────────────────────┼───────────────────────┘
59
+ ▼
60
+ ┌────────────────────────────────────────────────────────────────────────────┐
61
+ │ Thread Pool Aggregate: Combined Slot Records / Hashes (Zero Mutex Jitter) │
62
+ └──────────────────────────────────────┬─────────────────────────────────────┘
63
+ │
64
+ ▼
65
+ ┌────────────────────────────────────────────────────────────────────────────┐
66
+ │ py::gil_scoped_acquire (GIL Reacquired — Memory Safe PySlotRecord Returned)│
67
+ └────────────────────────────────────────────────────────────────────────────┘
68
+ ```
69
+
70
+ ```mermaid
71
+ flowchart TD
72
+ PythonCaller["Python Process (Main Thread)"] -->|"Lines (List of Strings)"| GilRelease["Pybind11: py::gil_scoped_release"]
73
+
74
+ subgraph NativeCore["librazobill Thread Pool"]
75
+ Worker0["Worker Thread 0: Chunk 0"]
76
+ Worker1["Worker Thread 1: Chunk 1"]
77
+ WorkerN["Worker Thread N: Chunk N"]
78
+ end
79
+
80
+ GilRelease --> NativeCore
81
+ Worker0 -->|"Local SIMD Parse & Hash"| LocalSlots0["Local Scratch Arena"]
82
+ Worker1 -->|"Local SIMD Parse & Hash"| LocalSlots1["Local Scratch Arena"]
83
+ WorkerN -->|"Local SIMD Parse & Hash"| LocalSlotsN["Local Scratch Arena"]
84
+
85
+ LocalSlots0 --> Aggregate["Combine Global Metrics"]
86
+ LocalSlots1 --> Aggregate
87
+ LocalSlotsN --> Aggregate
88
+
89
+ Aggregate --> GilReacquire["Reacquire GIL"]
90
+ GilReacquire --> Return["Return Python AuditResult"]
91
+ ```
92
+
93
+ ### Ingestion API Catalog:
94
+
95
+ | Use Case | Recommended API | GIL Released? | Scaling Characteristic |
96
+ | :--- | :--- | :---: | :--- |
97
+ | Single event processing (e.g., HTTP webhook) | `parse(msg, mode)` | No | Sub-microsecond ($0.1\,\mu\text{s}$) latency, returns `SlotRecord` |
98
+ | Micro-batch streaming (e.g., Kafka consumer) | `parseBatch(msgs, mode)` | **Yes** | Multi-threaded record vector creation |
99
+ | Direct tuple extraction (single event) | `decompose(msg, mode)` | No | Returns `(formKey, ints, strs)` without wrapper allocation |
100
+ | High-throughput columnar batch ingestion | `decomposeBatch(msgs, mode)` | **Yes** | Returns `list[tuple]`, releases GIL, 515k–737k+ logs/sec |
101
+ | Symmetrical tuple reconstruction | `reconstruct(formKey, ints, strs)` | No | Exact bit-for-bit raw log string recovery |
102
+ | Large-scale file or corpus audit | `auditParallel(lines, mode, numThreads)` | **Yes** | Hardware thread pool, zero allocations, up to 69.4M msgs/sec |
103
+ | Lightweight template key lookup | `formKey(msg, mode)` | No | Direct string return |
104
+ | Information Ledger calculation | `computeLedger(cells, totalEvents, alpha)` | No | Theorem 1 & 4 balance identities with finite-sample bounds |
105
+ | Consequence decomposition | `consequenceDecomposition(xy, map)` | No | Theorem 6 diagnostic sufficiency decomposition |
106
+ | Finite-sample bias correction | `millerMadow(plugin, categories, n)` | No | First-order asymptotic bias compensation |
107
+ | Proposition 10 certified bounds | `certifiedInterval(k, n, alpha)` | No | Finite-sample confidence interval |
108
+ | Steganographic capacity audit | `channelAnalyzeCapacity(sequence)` | No | Theorem 4c safe payload bits calculation |
109
+ | Dynamic log watermarking | `channelEmbed(seq, msg, bits, key)` | No | Arithmetic cumulative interval embedding |
110
+ | Watermark verification & extraction | `channelDecode(seq, forms, bits, key)` | No | Arithmetic decoding from observed form indices |
111
+
112
+ ---
113
+
114
+ ## 3. Step-by-Step Code Walkthroughs
115
+
116
+ ### Recipe A: Streaming Micro-Batch Ingestion
117
+ Ingesting events from a Kafka topic, Redis Stream, or AWS SQS consumer:
118
+
119
+ ```python
120
+ import pyrazobill
121
+
122
+ def processStreamBatch(eventBatch: list[str]) -> list[dict]:
123
+ # parseBatch releases GIL while parsing hundreds or thousands of messages
124
+ records = pyrazobill.parseBatch(eventBatch, pyrazobill.ScanMode.Semantic)
125
+
126
+ structuredPayloads = []
127
+ for rec in records:
128
+ structuredPayloads.append({
129
+ "formKey": rec.formKey,
130
+ "formHash": rec.formKeyHash,
131
+ "ints": rec.ints,
132
+ "strs": rec.strs,
133
+ })
134
+ return structuredPayloads
135
+ ```
136
+
137
+ ---
138
+
139
+ ### Recipe B: Multi-Core Ingestion and Lossless Verification
140
+ Processing a 1,000,000-line server log file and auditing exact lossless reconstruction across all hardware threads:
141
+
142
+ ```python
143
+ import sys
144
+ import time
145
+ from pathlib import Path
146
+ import pyrazobill
147
+
148
+ def auditLogFile(filePath: Path):
149
+ print(f"Reading {filePath.name}...")
150
+ t0 = time.perf_counter()
151
+ with open(filePath, "r", encoding="utf-8", errors="replace") as f:
152
+ lines = [line.rstrip("\r\n") for line in f if line.strip()]
153
+ tRead = time.perf_counter() - t0
154
+ print(f"Loaded {len(lines):,} lines in {tRead:.2f}s")
155
+
156
+ # Audit across all hardware cores with zero GIL contention
157
+ res = pyrazobill.auditParallel(
158
+ lines,
159
+ mode=pyrazobill.ScanMode.Semantic,
160
+ numThreads=0 # auto-detect CPU count
161
+ )
162
+
163
+ print("\n--- Audit Results ---")
164
+ print(f"Lines Processed : {res.totalLines:,}")
165
+ print(f"Data Volume : {res.totalBytes / (1024**2):.2f} MB")
166
+ print(f"Processing Wall Time : {res.elapsedSec:.3f} s")
167
+ print(f"Throughput : {res.msgsPerSec:,.0f} msgs/s ({res.mbPerSec:.2f} MB/s)")
168
+ print(f"Unique Form Skeletons: {res.uniqueFormsCount:,}")
169
+ print(f"Bit Mismatches : {res.mismatches} (100% Exact)")
170
+
171
+ if res.mismatches == 0:
172
+ print("INVARIANT SATISFIED: Every message is 100% losslessly reconstructible.")
173
+ else:
174
+ raise RuntimeError(f"Reconstruction error: {res.mismatches} mismatches detected!")
175
+
176
+ if __name__ == "__main__":
177
+ auditLogFile(Path("server.log"))
178
+ ```
179
+
180
+ ---
181
+
182
+ ### Recipe C: Parallel Stream Decomposition with ThreadPoolExecutor
183
+ When feeding high-throughput streaming events into columnar memory formats (DuckDB Appender, Apache Arrow, Polars, or Parquet), decomposing directly into tuples avoids Python object allocation. Releasing the GIL in `decomposeBatch()` allows Python worker threads to scale linearly:
184
+
185
+ ```python
186
+ import concurrent.futures
187
+ import time
188
+ import pyrazobill
189
+
190
+ def streamWorkerParallel(batches: list[list[str]], numWorkers: int = 4) -> list[tuple]:
191
+ """
192
+ Decomposes log batches concurrently across multiple worker threads.
193
+ Because pyrazobill.decomposeBatch releases the GIL, workers achieve true multi-core scaling.
194
+ """
195
+ t0 = time.perf_counter()
196
+ with concurrent.futures.ThreadPoolExecutor(max_workers=numWorkers) as executor:
197
+ # Each worker processes a chunk of messages in C++
198
+ futures = list(executor.map(lambda b: pyrazobill.decomposeBatch(b, pyrazobill.ScanMode.Semantic), batches))
199
+
200
+ # Flatten results: list of (formKey, ints, strs) tuples
201
+ allDecomposed = [record for batch in futures for record in batch]
202
+ elapsed = time.perf_counter() - t0
203
+
204
+ totalCount = len(allDecomposed)
205
+ rate = totalCount / elapsed if elapsed > 0 else 0
206
+ print(f"Decomposed {totalCount:,} events in {elapsed:.3f}s ({rate:,.0f} logs/sec across {numWorkers} threads)")
207
+ return allDecomposed
208
+
209
+ # Example invocation
210
+ if __name__ == "__main__":
211
+ testEvents = [
212
+ f"Server node-{i%10} reported status {200 if i%5!=0 else 500} responseTime={i*3%150}ms client={10+i%5}.0.0.1"
213
+ for i in range(100_000)
214
+ ]
215
+ chunkSize = 25_000
216
+ workChunks = [testEvents[i:i + chunkSize] for i in range(0, len(testEvents), chunkSize)]
217
+
218
+ decomposedTuples = streamWorkerParallel(workChunks, numWorkers=4)
219
+ # Validate lossless invertibility on sample
220
+ form, ints, strs = decomposedTuples[0]
221
+ assert pyrazobill.reconstruct(form, ints, strs) == testEvents[0]
222
+ ```
223
+
224
+ ---
225
+
226
+ ### Recipe D: Lossless Storage Compaction with DuckDB Single-Pass Struct
227
+ Eliminating raw log strings to cut persistent storage requirements by ~45% while maintaining transparent querying.
228
+
229
+ > [!TIP]
230
+ > Use `rbDecomposeSemantic` or `rbDecompose` which returns a single composite `STRUCT(form VARCHAR, ints BIGINT[], strs VARCHAR[])`. This cuts parsing overhead by 66% compared to calling separate scalar functions.
231
+
232
+ ```python
233
+ import duckdb
234
+ import pyrazobill
235
+
236
+ # Initialize DuckDB connection
237
+ con = duckdb.connect("logs.duckdb", config={"allow_unsigned_extensions": "true"})
238
+
239
+ # Dynamically load the native librazobill extension
240
+ extPath = pyrazobill.extensionPath().replace("\\", "/")
241
+ con.execute(f"LOAD '{extPath}';")
242
+
243
+ # 1. Create compressed physical storage table
244
+ con.execute("""
245
+ CREATE TABLE IF NOT EXISTS logsPhysical (
246
+ timestamp TIMESTAMP,
247
+ level VARCHAR,
248
+ form VARCHAR,
249
+ ints BIGINT[],
250
+ strs VARCHAR[]
251
+ );
252
+ """)
253
+
254
+ # 2. Ingest raw log strings into decomposed columnar primitives via single-pass struct
255
+ rawEvents = [
256
+ ("2026-10-07 12:00:01", "INFO", "User 1042 authenticated from 192.168.1.10"),
257
+ ("2026-10-07 12:00:02", "WARN", "Connection timeout on port 5432 after 3 retries"),
258
+ ("2026-10-07 12:00:03", "ERROR", "Failed to bind socket 8080: address in use"),
259
+ ]
260
+
261
+ con.executemany("""
262
+ INSERT INTO logsPhysical
263
+ SELECT
264
+ CAST(? AS TIMESTAMP),
265
+ ?,
266
+ d.form,
267
+ d.ints,
268
+ d.strs
269
+ FROM (SELECT rbDecomposeSemantic(?) AS d);
270
+ """, [(ts, lvl, msg) for ts, lvl, msg in rawEvents])
271
+
272
+ # 3. Create transparent zero-copy reconstruction view
273
+ con.execute("""
274
+ CREATE OR REPLACE VIEW logs AS
275
+ SELECT
276
+ timestamp,
277
+ level,
278
+ form,
279
+ rbReconstruct(form, ints, strs) AS msg
280
+ FROM logsPhysical;
281
+ """)
282
+
283
+ # 4. Analysts query 'logs' naturally as if raw strings were stored on disk
284
+ results = con.execute("SELECT timestamp, level, msg FROM logs WHERE level = 'WARN';").fetchall()
285
+ for row in results:
286
+ print(row)
287
+ ```
288
+
289
+ ---
290
+
291
+ ### Recipe E: Information-Theoretic Entropy Monitoring
292
+ Detecting operational anomalies and schema drift using compensated Shannon entropy:
293
+
294
+ ```python
295
+ from collections import Counter
296
+ import pyrazobill
297
+
298
+ def computeClusterEntropy(records: list[pyrazobill.SlotRecord]) -> float:
299
+ # Tally frequency of each structural form key
300
+ counts = Counter(r.formKey for r in records)
301
+ frequencyVector = [float(c) for c in counts.values()]
302
+
303
+ # Calculate Shannon entropy with Neumaier floating-point compensation
304
+ bits = pyrazobill.entropy(frequencyVector)
305
+ return bits
306
+
307
+ # Baseline observation window
308
+ normalLogs = [
309
+ "GET /api/v1/health 200 OK",
310
+ "GET /api/v1/health 200 OK",
311
+ "GET /api/v1/health 200 OK",
312
+ "POST /api/v1/items 201 Created",
313
+ ]
314
+ hNormal = computeClusterEntropy([pyrazobill.parse(m) for m in normalLogs])
315
+ print(f"Normal Operational Entropy: {hNormal:.4f} bits")
316
+
317
+ # Anomaly window (sudden emergence of novel error templates)
318
+ anomalyLogs = [
319
+ "GET /api/v1/health 200 OK",
320
+ "Internal server error: DB connection pool exhausted",
321
+ "Kernel OOM killer invoked for pid 9481",
322
+ "Segfault at 0x7fff0032 in libnative.so",
323
+ ]
324
+ hAnomaly = computeClusterEntropy([pyrazobill.parse(m) for m in anomalyLogs])
325
+ print(f"Anomaly Operational Entropy: {hAnomaly:.4f} bits")
326
+ ```
327
+
328
+ ---
329
+
330
+ ### Recipe F: Information Ledger & Finite-Sample Correction
331
+ Auditing the information balance of a log cluster according to Theorems 1 and 4 from *Latitude and Doubt* (Atta, 2026):
332
+
333
+ $$\Lambda(Y \mid X) = H(Y \mid X) - \Delta(Y \mid X)$$
334
+
335
+ ```python
336
+ import pyrazobill
337
+
338
+ # Define joint observation counts between form templates and underlying semantic meanings
339
+ cells = [
340
+ pyrazobill.Cell(form=1, meaning=1, mass=500.0),
341
+ pyrazobill.Cell(form=2, meaning=1, mass=300.0), # Synonymous form
342
+ pyrazobill.Cell(form=3, meaning=2, mass=200.0),
343
+ ]
344
+ totalEvents = 1000.0
345
+
346
+ # Compute exact information ledger with 95% confidence bounds
347
+ ledger = pyrazobill.computeLedger(cells, totalEvents, confidenceAlpha=0.05)
348
+
349
+ print(f"Coverage Ratio : {ledger.coverage:.4f}")
350
+ print(f"Content H(Y) : {ledger.content:.4f} bits")
351
+ print(f"Expression H(X) : {ledger.expression:.4f} bits")
352
+ print(f"Latitude Lambda(Y|X) : {ledger.latitude:.4f} bits")
353
+ print(f"Doubt Delta(Y|X) : {ledger.doubt:.4f} bits")
354
+ print(f"Conveyance I(X;Y) : {ledger.conveyance:.4f} bits")
355
+ print(f"Variation Distance : {ledger.distance:.4f} bits")
356
+
357
+ # Verify fundamental mathematical invariants
358
+ assert ledger.satisfiesLedgerIdentities()
359
+ assert ledger.satisfiesVocabularyLaw(nDistinctForms=3)
360
+
361
+ # Evaluate Theorem 6 diagnostic sufficiency
362
+ xy = [
363
+ pyrazobill.XY(form=1, y=1, n=500.0),
364
+ pyrazobill.XY(form=2, y=1, n=300.0),
365
+ pyrazobill.XY(form=3, y=2, n=200.0),
366
+ ]
367
+ mapping = [
368
+ pyrazobill.FormMeaning(form=1, meaning=1, p=1.0),
369
+ pyrazobill.FormMeaning(form=2, meaning=1, p=1.0),
370
+ pyrazobill.FormMeaning(form=3, meaning=2, p=1.0),
371
+ ]
372
+ decomp = pyrazobill.consequenceDecomposition(xy, mapping)
373
+ print(f"Consequence Decomposition: E={decomp.expression:.4f}, I={decomp.consequence:.4f}, H_M={decomp.hedge:.4f}")
374
+ assert decomp.satisfiesIdentity()
375
+ ```
376
+
377
+ ---
378
+
379
+ ### Recipe G: Latitude Channel Capacity & Provenance Watermarking
380
+ Embedding tamper-evident provenance watermarks or tracing metadata into dynamic log streams using synonym selection without alerting downstream consumers (Theorem 4c):
381
+
382
+ ```python
383
+ import pyrazobill
384
+
385
+ # Define sequence of semantic events with available synonymous log forms
386
+ seq = [
387
+ pyrazobill.MeaningChoice(
388
+ meaning=101,
389
+ options=[
390
+ pyrazobill.SynonymousOption(formIndex=0, probability=0.5), # "User auth succeeded"
391
+ pyrazobill.SynonymousOption(formIndex=1, probability=0.5), # "Authentication OK for user"
392
+ ]
393
+ )
394
+ for _ in range(32)
395
+ ]
396
+
397
+ # 1. Analyze safe steganographic transmission capacity
398
+ capacity = pyrazobill.channelAnalyzeCapacity(seq)
399
+ print(f"Mean Capacity: {capacity.meanCapacityBits:.2f} bits")
400
+ print(f"Safe Payload : {capacity.safePayloadBits} bits")
401
+
402
+ # 2. Embed a 16-bit payload (e.g. cluster node ID = 42) into form selections
403
+ payload = 42
404
+ sharedKey = 0.81726354
405
+ res = pyrazobill.channelEmbed(seq, payload, payloadBits=16, sharedKey=sharedKey)
406
+ assert res.success
407
+ print(f"Chosen Form Indices: {res.chosenForms[:8]}...")
408
+
409
+ # 3. Decode payload from observed forms on downstream aggregator
410
+ dec = pyrazobill.channelDecode(seq, res.chosenForms, payloadBits=16, sharedKey=sharedKey)
411
+ assert dec.success
412
+ assert dec.message == payload
413
+ print(f"Successfully decoded watermark payload: {dec.message}")
414
+ ```
415
+
416
+ ---
417
+
418
+ ## 4. ClickHouse chDB Compatibility
419
+
420
+ `librazobill` uses xxHash64 (`hash64`) which is byte-for-byte identical to ClickHouse's native `xxHash64()` scalar function:
421
+
422
+ ```python
423
+ import chdb
424
+ import pyrazobill
425
+
426
+ msg = "Service started on port 8080"
427
+ pyHash = pyrazobill.hash64(msg)
428
+
429
+ chRes = chdb.query(f"SELECT xxHash64('{msg}') AS h;").to_dict()
430
+ chHash = chRes["h"][0]
431
+
432
+ assert pyHash == chHash
433
+ print(f"Parity Verified: pyrazobill ({pyHash}) == chDB ({chHash})")
434
+ ```
435
+
436
+ ---
437
+
438
+ ## 5. Summary Cheat Sheet: API & Properties
439
+
440
+ ```python
441
+ # Scan Modes
442
+ pyrazobill.ScanMode.Exact
443
+ pyrazobill.ScanMode.DigitOnly
444
+ pyrazobill.ScanMode.Semantic
445
+
446
+ # Core Parsing Functions
447
+ record = pyrazobill.parse(msg, mode) # -> SlotRecord
448
+ records = pyrazobill.parseBatch(msgs, mode) # -> list[SlotRecord] (releases GIL)
449
+ form, ints, strs = pyrazobill.decompose(msg, mode) # -> tuple[str, list[int], list[str]]
450
+ tuples = pyrazobill.decomposeBatch(msgs, mode) # -> list[tuple] (releases GIL)
451
+ orig = pyrazobill.reconstruct(record) # -> str
452
+ orig = pyrazobill.reconstruct(form, ints, strs) # -> str
453
+ key = pyrazobill.formKey(msg, mode) # -> str
454
+ tmpl = pyrazobill.templateOf(form) # -> str (converts to <*>)
455
+ h64 = pyrazobill.hash64(str) # -> int (xxHash64)
456
+ ent = pyrazobill.entropy(counts) # -> float (Neumaier-compensated bits)
457
+ audit = pyrazobill.auditParallel(lines, mode, threads) # -> AuditResult (multi-threaded, releases GIL)
458
+
459
+ # Statistical & Information Theory Functions
460
+ ledger = pyrazobill.computeLedger(cells, totalEvents, alpha)
461
+ decomp = pyrazobill.consequenceDecomposition(xy, mapping)
462
+ hCorrected = pyrazobill.millerMadow(pluginEntropy, k, n)
463
+ eps, halfW = pyrazobill.certifiedInterval(k, n, alpha)
464
+
465
+ # Latitude Steganographic Channel Functions
466
+ cap = pyrazobill.channelAnalyzeCapacity(sequence)
467
+ emb = pyrazobill.channelEmbed(sequence, msg, bits, key, dither)
468
+ dec = pyrazobill.channelDecode(sequence, observedForms, bits, key)
469
+
470
+ # SlotRecord Properties
471
+ record.formKey # -> str (e.g. 'User {} logged in')
472
+ record.formKeyHash # -> int (64-bit uint)
473
+ record.ints # -> list[int]
474
+ record.strs # -> list[str]
475
+ record.skeleton # -> str
476
+ record.types # -> str (e.g. 'IS')
477
+ record.reconstruct() # -> str (rebuilds original string)
478
+
479
+ # AuditResult Properties
480
+ audit.totalLines # -> int
481
+ audit.totalBytes # -> int
482
+ audit.elapsedSec # -> float
483
+ audit.uniqueFormsCount # -> int
484
+ audit.msgsPerSec # -> float
485
+ audit.mbPerSec # -> float
486
+ audit.mismatches # -> int (always 0 in valid runs)
487
+ ```