tritlib 1.2.3__tar.gz → 2.1.2__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 (40) hide show
  1. tritlib-2.1.2/CHANGELOG.md +348 -0
  2. {tritlib-1.2.3 → tritlib-2.1.2}/PKG-INFO +46 -35
  3. {tritlib-1.2.3 → tritlib-2.1.2}/README.md +44 -33
  4. {tritlib-1.2.3 → tritlib-2.1.2}/pyproject.toml +11 -1
  5. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/__init__.py +25 -1
  6. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/base.py +10 -9
  7. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/bochvar.py +3 -2
  8. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/plural.py +5 -6
  9. tritlib-2.1.2/src/tritlib/tfloat.py +946 -0
  10. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/trit.py +26 -10
  11. tritlib-1.2.3/src/tritlib/tryte.py → tritlib-2.1.2/src/tritlib/tritarray.py +159 -119
  12. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/trits.py +75 -35
  13. tritlib-2.1.2/src/tritlib/trybble.py +13 -0
  14. tritlib-2.1.2/src/tritlib/tryte.py +13 -0
  15. tritlib-2.1.2/src/tritlib/word.py +60 -0
  16. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat.py +286 -134
  17. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat_add.py +24 -6
  18. tritlib-2.1.2/tests/test_tfloat_div.py +35 -0
  19. tritlib-2.1.2/tests/test_tritarray.py +277 -0
  20. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_trits.py +56 -31
  21. tritlib-2.1.2/tests/test_tryte.py +26 -0
  22. tritlib-2.1.2/tests/test_word.py +74 -0
  23. tritlib-1.2.3/CHANGELOG.md +0 -112
  24. tritlib-1.2.3/src/tritlib/tfloat.py +0 -675
  25. tritlib-1.2.3/tests/test_tryte.py +0 -247
  26. {tritlib-1.2.3 → tritlib-2.1.2}/.gitignore +0 -0
  27. {tritlib-1.2.3 → tritlib-2.1.2}/LICENSE +0 -0
  28. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/context.py +0 -0
  29. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/__init__.py +0 -0
  30. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/heyting.py +0 -0
  31. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/kleene.py +0 -0
  32. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/lukasiewicz.py +0 -0
  33. {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/rm3.py +0 -0
  34. {tritlib-1.2.3 → tritlib-2.1.2/src/tritlib}/py.typed +0 -0
  35. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_context.py +0 -0
  36. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_logic.py +0 -0
  37. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_logic_kleene.py +0 -0
  38. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_plural.py +0 -0
  39. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat_mul.py +0 -0
  40. {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_trit.py +0 -0
@@ -0,0 +1,348 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [2.1.2] — 2026-11-04
8
+
9
+ ### Fixed
10
+
11
+ - `py.typed` is now shipped in the wheel.
12
+ - The sdist no longer includes local tool configuration
13
+
14
+ ## [2.1.1] — 2026-07-21
15
+
16
+ ### Fixed
17
+
18
+ - `Trits.__divmod__` still drifted for **every even divisor**, whatever
19
+ the dividend. 2.1.0 fixed the quotient decision but kept correcting
20
+ the remainder only after the loop, one unit at a time, against a
21
+ drift that triples per trit: `divmod(TritArray(-2586565449575, 27),
22
+ TritArray(-4, 27))` needed ~10¹² iterations, i.e. never returned.
23
+ The result was correct whenever it did return, so the identity
24
+ `a == b·q + r` kept the bug hidden.
25
+
26
+ A single quotient trit cannot always reduce the scaled remainder:
27
+ with `|pr| ≤ |d| / 2`, `V = 3·pr + t` reaches `1.5·|d| + 1`, while
28
+ the three possible trits only span `[−1.5·|d|, +1.5·|d|]`. The
29
+ overshoot is by exactly one and is reachable for every even divisor;
30
+ odd divisors are safe because `|pr| ≤ (|d| − 1) / 2` keeps `|V|` in
31
+ range. The correction now happens inside the loop, at most once per
32
+ trit, which amounts to writing a quotient digit of ±2 at that rank —
33
+ balanced ternary addition carries it into the ranks already produced.
34
+
35
+ - `Trits.__divmod__` accumulates the quotient (`q = 3·q ± 1`) instead
36
+ of collecting trits into a list. A trit appended to a list cannot be
37
+ revised, which is what the in-loop correction requires.
38
+
39
+ ### Documentation
40
+
41
+ - `Trits.__divmod__`, `TritArray.__divmod__` and the _Division
42
+ pitfalls_ section of `CLAUDE.md` described the 2.1.0 state, where the
43
+ magnitude comparison alone was believed to maintain
44
+ `|pr| ≤ |d| / 2`. They now document why it does not, and record the
45
+ even-divisor case as the reason to test small even divisors
46
+ explicitly: random fuzzing essentially never draws 2, 4 or 6, which
47
+ is how this survived a full pass.
48
+ - Documented that an even divisor admits two valid `(quotient,
49
+ remainder)` pairs when the remainder reaches exactly `|d| / 2`, and
50
+ which one this implementation returns —
51
+ `divmod(Trits(10), Trits(4))` is `(3, -2)`, not `(2, 2)`.
52
+
53
+ ### Added
54
+
55
+ - `tests/test_trits.py::test_divmod_even_divisors` — small even
56
+ divisors at full width, the case none of the existing tests or
57
+ fuzzing runs covered.
58
+
59
+ ## [2.1.0] — 2026-07-21
60
+
61
+ ### Fixed
62
+
63
+ - `TritArray.__divmod__` returned an unreduced remainder whenever the
64
+ divisor exceeded roughly a third of the width's range (about 29 % of
65
+ random operand pairs at width 9). The quotient decision and the final
66
+ correction loop compared `V + V` at the operand width, which wrapped
67
+ silently. `divmod(TritArray(7872, 9), TritArray(-7887, 9))` gave
68
+ `(0, 7872)` instead of `(-1, -15)`. The identity `a == b·q + r` held
69
+ throughout, which is why the tests never caught it.
70
+ - `TritArray.__divmod__` now delegates to `Trits.__divmod__` instead of
71
+ duplicating the algorithm at fixed width. Division must run at
72
+ arbitrary precision: the intermediate partial remainder reaches
73
+ `1.5 × |divisor|`.
74
+ - `Trits.__divmod__` chose each quotient trit from the sign of the
75
+ leading trit alone, so the partial remainder doubled at every step
76
+ and the one-unit-at-a-time correction loop had to run an
77
+ astronomical number of iterations (a 52-trit division drifted to
78
+ `4·10²² × |d|`). The decision is now made on magnitude, restoring the
79
+ `|pr| ≤ |d| / 2` invariant.
80
+ - `Trits.__divmod__` dropped the `len(self) < len(other)` shortcut,
81
+ which returned an unreduced remainder and amounted to a second
82
+ remainder convention inside the same function: `divmod(34, 58)` gave
83
+ `(0, 34)` instead of `(1, -24)`.
84
+ - `TritArray.__eq__` and `__lt__` raised `TypeError` on any non-
85
+ `TritArray` operand instead of returning `NotImplemented`, so
86
+ `array == 5`, `array == None` and `array in [1, 2]` all blew up.
87
+ - `TritArray.__eq__` also raised `ValueError` when the widths differed,
88
+ which propagated through every implicit comparison Python performs:
89
+ `Tryte(1) in [Word(1)]`, `[Tryte(1)] == [Word(1)]`, `list.index`,
90
+ `!=`, and — via `@total_ordering` — `<=`, `>` and `>=`. Mismatched
91
+ widths now compare unequal (`False`), matching `TFloat`. `__lt__`
92
+ returns `NotImplemented`, so ordering across widths raises the
93
+ standard `TypeError` instead.
94
+ - `Trybble`, `Tryte` and `Word` declared no `__slots__`, so every
95
+ instance carried a `__dict__` despite `TritArray` defining slots.
96
+ They now declare `__slots__ = ()`; declaring the inherited names
97
+ again would allocate a second, shadowing slot per attribute (a
98
+ `Tryte` measured 64 bytes against 48 for a `TritArray`).
99
+ - `TritArray.set` accepted any object as the replacement trit. The
100
+ invalid instance was built silently and only failed later, in
101
+ `__str__`, as a confusing `KeyError`. It now rejects non-`Trit`
102
+ values on the spot.
103
+ - `cons` and `acons` accepted operands of a different width and
104
+ silently truncated through `zip`, unlike every other binary
105
+ operation. They now raise `TypeError`.
106
+ - `rotate_left` and `rotate_right` were wrong for `n > width`:
107
+ `Tryte(42).rotate_left(10)` did not equal `rotate_left(1)`. The
108
+ count is now taken modulo the width.
109
+ - `__lshift__` and `__rshift__` accepted negative counts and did
110
+ something arbitrary rather than failing: `n = -1` made the slice
111
+ count from the end, moving the most significant trit into the least
112
+ significant position and zeroing the rest, while `[Z] * -1` silently
113
+ produced an empty list. Below `-width` the value came back
114
+ unchanged. Both now raise `ValueError`, as Python's own integers do.
115
+ - `Trit(True)` was accepted and stored `True` as the trit value, since
116
+ `bool` is a subclass of `int`.
117
+ - `TFloat.__neg__` mapped the all-P pattern to the all-N one, turning
118
+ ∞ into NaR and NaR into ∞. Since `__sub__` is `self + (-other)`,
119
+ `x - ∞` returned NaR and `x - NaR` returned ∞. Both special values
120
+ are now fixed points under negation, which also matches the
121
+ constructor, where `TFloat(float("-inf"), w)` already encodes ∞.
122
+ - All errors now have a meaningful message.
123
+
124
+ ### Changed
125
+
126
+ - `tfloat.py`, `logic/base.py`, `logic/bochvar.py` and `logic/plural.py`
127
+ imported names from the `tritlib` package itself rather than from the
128
+ defining modules. That only worked because `__init__.py` happened to
129
+ import `trit`, `trits` and `tritarray` before `word` (which pulls in
130
+ `tfloat`): reordering two lines of `__init__.py` broke the package
131
+ with `ImportError: cannot import name 'TritArray'`. All four now
132
+ import the concrete modules, and the package no longer depends on the
133
+ order of its own imports.
134
+ - `TFloat.__add__`: `∞ + ∞` is now NaR rather than ∞. Tekum has a
135
+ single unsigned ∞, so `-∞ = ∞` and `∞ + ∞` is the same expression as
136
+ `∞ - ∞` — it cannot resolve to ∞. The IEEE rule `+∞ + +∞ = +∞`
137
+ depends on the sign being carried by the value. `∞ × ∞` is
138
+ unaffected and stays ∞.
139
+
140
+ ### Documentation
141
+
142
+ - The division docstrings described the opposite of what the code did
143
+ (« no magnitude comparison is needed ») and claimed the remainder
144
+ carried the sign of the dividend; it is balanced, so it takes
145
+ whichever sign minimises `|r|` — `divmod(Trits(-200), Trits(-57))`
146
+ is `(4, 28)`.
147
+ - Corrected the worked examples: `Trits(42)` is `"+---0"`, and
148
+ `"+-0+"` is 19, not 8 (both in `Trits` and `TritArray`).
149
+ - README: B3 is the internal (weak) Bochvar system, where Z is
150
+ absorbing — `Z ∧ Z = Z`, not `N`; the requirement is Python ≥ 3.11,
151
+ matching `pyproject.toml`; the `divmod` example showed a result that
152
+ no version of the code ever produced.
153
+ - `TFloat.__neg__` and `__add__` document the single unsigned ∞ and
154
+ why `∞ + ∞` cannot be ∞.
155
+ - `TFloat.__lt__` states the Tekum ordering explicitly: NaR sorts below
156
+ every finite value and ∞ above them, so the special values take part
157
+ in comparisons rather than poisoning them the way IEEE NaN does.
158
+ - The docstrings of `set`, `cons`, `acons`, the shifts and the
159
+ rotations describe the guards added above.
160
+
161
+ ## [2.0.0] — 2026-05-18
162
+
163
+ ### Breaking
164
+
165
+ - `Tryte` is now a fixed-width 9-trit type (T9), aligned with Setnex ISA v0.8.
166
+ Its constructor no longer accepts a `width` argument. Code that did
167
+ `Tryte(value, w)` for `w ≠ 9` must migrate to `TritArray(value, w)`.
168
+ - The arbitrary-width balanced ternary word type historically named `Tryte`
169
+ is renamed `TritArray`. All public methods (`add_with_carry`,
170
+ `add_saturating`, `to_width`, `set`, `__divmod__`, shifts, rotations,
171
+ consensus, etc.) live on `TritArray` and are inherited by the three
172
+ width-locked subclasses.
173
+
174
+ ### Added
175
+
176
+ - `TritArray(value, width)` — arbitrary-width balanced ternary word (the
177
+ generic base type).
178
+ - `Trybble(value)` — 3-trit field (T3), the smallest addressable grouping
179
+ per the ISA.
180
+ - `Word(value)` — 27-trit word (T27), the basic memory and register unit
181
+ per the ISA.
182
+ - Subclasses share `TritArray`'s implementation and preserve their concrete
183
+ type through arithmetic via `type(self)._from_trits(...)` dispatch.
184
+ - `tests/test_tritarray.py` — generic-width regression suite migrated from
185
+ the former `test_tryte.py`; `test_tryte.py` now exercises the 9-trit
186
+ semantics (overflow at ±9841, fixed `len() == 9`, etc.).
187
+
188
+ ### Changed
189
+
190
+ - All internal `TFloat` arithmetic (mantissa, regime, exponent, work-scale
191
+ bounds, double-width products, division work widths) routes through
192
+ `TritArray`. `TFloat._raw` is a `TritArray` of even width ≥ 8; the
193
+ constructor accepts `float | int | TritArray` (any subclass instance
194
+ whose width meets Tekum's even-and-≥-8 rule).
195
+ - `to_width(w)` on any subclass returns a `TritArray` (changing width
196
+ exits the width-locked contract).
197
+ - `__init__.py` exports `TritArray`, `Trybble`, `Word` alongside `Tryte`.
198
+
199
+ ## [1.3.0] — 2026-04-29
200
+
201
+ ### Added
202
+
203
+ - Native trit-level division on `TFloat` (Tekum arithmetic)
204
+ - Mantissa division at a working scale `p_work = max(p_self, p_other) + 2`
205
+ - Numerator pre-shifted by `3^p_work` to recover the fractional quotient as an integer
206
+ - Wheel-algebra special-value handling: `x/0 = ∞`, `0/0 = NaR`, `x/∞ = 0`, `∞/x = ∞`, `∞/∞ = NaR`
207
+ - Result re-encoded by `_finalize`; no longer routes through Python `float`
208
+
209
+ ### Fixed
210
+
211
+ - `Trits.__divmod__` and `Tryte.__divmod__` exponential drift on certain operand magnitudes.
212
+ Initial `partial_rest = s[:len(d)-1]` could already exceed `|d|/2` when `|d|` sat at the
213
+ lower end of its trit-length range, breaking the single-trit quotient invariant; the
214
+ resulting remainder grew geometrically and the correction loop ran for billions of
215
+ iterations. Both implementations now start with `partial_rest = []` and `pr = 0`,
216
+ consuming every trit of the dividend and maintaining `|pr| ≤ |d|/2` at every step.
217
+ - `Tryte.__divmod__` silent overflow at width boundaries: the quotient-digit decision
218
+ `V + V > abs_other` truncated when `2|V|` exceeded the Tryte range (e.g. width 26 with
219
+ values near `3²⁶/2`). The decision now uses an `int` comparison, immune to truncation.
220
+
221
+ ### Changed
222
+
223
+ - `TFloat._from_float` (renamed from `from_float`): privatized; bounded fraction
224
+ quantification with `_find_regime` recomputed in-loop, plus a final clamp
225
+ - `_finalize` factored out of `__add__`, `__mul__`, and `__truediv__` for shared
226
+ normalization and re-encoding
227
+ - `Trits.__divmod__` and `Tryte.__divmod__` now raise `TypeError` (not return
228
+ `NotImplemented`) when the divisor is of the wrong type, matching `divmod` protocol
229
+ - `TFloat.__eq__` returns `NotImplemented` for cross-width comparisons rather than
230
+ silently coercing
231
+ - `Tryte.to_tryte` (TFloat method): correct slicing for the `e == 0` and `e < 0`
232
+ edge cases, plus explicit overflow guard when `e ≥ width`
233
+
234
+ ## [1.2.3] — 2026-04-22
235
+
236
+ - added TFloat conversion from and into int/Tryte/Trits
237
+ - renamed to_tryte into to_raw
238
+
239
+ ## [1.2.2] — 2026-04-05
240
+
241
+ - Updated README.md and pyproject.toml
242
+
243
+ ## [1.2.1] — 2026-04-05
244
+
245
+ - Symmetric Euclidean `__divmod__` implementation
246
+
247
+ ## [1.2.0] — 2026-04-05
248
+
249
+ ### Added
250
+
251
+ - Native trit-level multiplication on `TFloat` (Tekum arithmetic)
252
+ - Double-width Tryte arithmetic for the product
253
+ - Exponent addition, scale reduction via ternary shift
254
+ - Normalization into Tekum's [0.5, 1.5) range
255
+
256
+ ## [1.1.0] — 2026-04-05
257
+
258
+ ### Added
259
+
260
+ - Native trit-level addition on `TFloat` (Tekum arithmetic)
261
+ - Direct manipulation of extended mantissas via `Tryte` operations
262
+ - Exponent alignment via ternary shift
263
+ - Normalization with Tekum's truncation-equals-rounding property
264
+ - No longer relies on Python float for addition
265
+ - `_find_regime` and `_assemble_raw` internal helpers factored out of `from_float`
266
+
267
+ ## [1.0.0] — 2026-04-04
268
+
269
+ ### Changed
270
+
271
+ - API declared stable
272
+ - Version bumped to 1.0.0 — no breaking changes from 0.7.0
273
+
274
+ ### Added
275
+
276
+ - `py.typed` marker for mypy support
277
+ - CHANGELOG.md
278
+ - 99% test coverage
279
+
280
+ ## [0.7.0] — 2026-04-04
281
+
282
+ ### Added
283
+
284
+ - `TFloat` — Tekum tapered precision floating-point type (arXiv:2512.10964)
285
+ - Decode from `Tryte` and encode from Python `float`
286
+ - Arithmetic via float conversion (`+`, `-`, `*`, `/`)
287
+ - Negation, comparison, hashing
288
+ - Special values: zero, infinity, NaR
289
+ - Configurable width (even, ≥ 8)
290
+ - `pluralize` function and `PluralResult` dataclass for logical pluralism
291
+ - `logic_mode` context manager for runtime logic system selection
292
+ - Logic aliases: `K3`, `L3`, `HT`, `BI3`, `RM3`
293
+ - `Trit.imply` method dispatched via context manager
294
+ - `context.py` module with `ContextVar`-based logic dispatch
295
+ - Docstrings on all public classes and methods
296
+ - This CHANGELOG
297
+
298
+ ### Changed
299
+
300
+ - `Trit.__and__`, `__or__`, `__invert__` now dispatch via active logic system (Kleene by default, fast path when no context manager is active)
301
+
302
+ ## [0.2.0] — 2026-04-03
303
+
304
+ ### Added
305
+
306
+ - `Tryte` — fixed-width ternary word type
307
+ - Three addition modes: truncating, with carry, saturating
308
+ - Shifts (`<<`, `>>`) and rotations (`rotate_left`, `rotate_right`)
309
+ - Per-trit access (`__getitem__`) and immutable modification (`set`)
310
+ - Sign extraction, absolute value
311
+ - Consensus and anti-consensus (`cons`, `acons`)
312
+ - Resize across widths (`to_width`)
313
+ - Division (`__floordiv__`, `__mod__`, `__divmod__`)
314
+ - Five ternary logic systems with `TernaryLogic` abstract base class
315
+ - Kleene (K3), Łukasiewicz (L3), Heyting (HT), Bochvar (BI3), R-mingle (RM3)
316
+ - Each implements NOT, AND, OR, IMPLY, XOR, EQUIV
317
+ - `Trits` division via double trial quotient method
318
+ - Kleene logic operators on `Trit` (`&`, `|`, `~`)
319
+ - `Trit.cons` and `Trit.acons` operators
320
+
321
+ ### Changed
322
+
323
+ - `Trits` internal storage changed to tuple (immutable)
324
+ - `Trits._from_trits` centralizes normalization and construction
325
+
326
+ ## [0.1.0] — 2026-04-02
327
+
328
+ ### Added
329
+
330
+ - `Trit` — immutable single balanced ternary digit
331
+ - Construction from int with validation
332
+ - Negation, multiplication
333
+ - Half-adder and full-adder with ternary carry
334
+ - Equality, hashing, comparison
335
+ - Immutability via `__setattr__` guard
336
+ - `Trits` — arbitrary-precision balanced ternary integer
337
+ - Construction from `int` or balanced ternary string
338
+ - Addition, subtraction, multiplication
339
+ - Conversion to/from Python `int`
340
+ - Comparison operators
341
+ - Constants `P`, `Z`, `N`
342
+ - `pyproject.toml` with hatchling build system
343
+ - MIT license
344
+ - Initial README
345
+
346
+ [0.7.0]: https://codeberg.org/newick_2/tritlib/compare/v0.2.0...v0.7.0
347
+ [0.2.0]: https://codeberg.org/newick_2/tritlib/compare/v0.1.0...v0.2.0
348
+ [0.1.0]: https://codeberg.org/newick_2/tritlib/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: tritlib
3
- Version: 1.2.3
3
+ Version: 2.1.2
4
4
  Summary: Balanced ternary arithmetic and multi-valued logic library
5
5
  Project-URL: Homepage, https://codeberg.org/setnex-org/tritlib
6
6
  Project-URL: Repository, https://codeberg.org/setnex-org/tritlib
@@ -20,7 +20,7 @@ Requires-Dist: pytest; extra == 'dev'
20
20
  Requires-Dist: pytest-cov; extra == 'dev'
21
21
  Description-Content-Type: text/markdown
22
22
 
23
- # tritlib — v1.2
23
+ # tritlib — v2.1
24
24
 
25
25
  A pure Python library for balanced ternary arithmetic, multi-valued logic, and floating-point representation.
26
26
 
@@ -34,20 +34,21 @@ Balanced ternary is a base-3 numeral system using digits from {−1, 0, +1}. It
34
34
 
35
35
  - **`Trit`** — Immutable single trit. Arithmetic (negation, multiplication, half/full addition with ternary carry), Kleene K3 logic (`&`, `|`, `~`), consensus and anti-consensus operators. Hashable and comparable.
36
36
  - **`Trits`** — Arbitrary-precision balanced ternary integer. Full arithmetic: addition, subtraction, multiplication, floor division and modulo via the double trial quotient method. Constructed from `int` or balanced ternary string (`"+-0+"`).
37
- - **`Tryte`** — Fixed-width ternary word, analogous to a hardware register. Three addition modes: truncating (wrap around), with carry, and saturating. Shifts, rotations, trit-level access and modification, sign extraction, absolute value, consensus/anti-consensus. Resizable across widths.
38
- - **`TFloat`** — Balanced ternary floating-point number using the Tekum tapered precision format (Hunhold, arXiv:2512.10964). No sign trit, truncation equals rounding, three special values (zero, infinity, NaR). Configurable width (even, ≥ 8). Addition and multiplication use native trit-level arithmetic; division converts via Python `float`.
37
+ - **`TritArray`** — Fixed-width ternary word at arbitrary width, analogous to a hardware register. Three addition modes: truncating (wrap around), with carry, and saturating. Shifts, rotations, trit-level access and modification, sign extraction, absolute value, consensus/anti-consensus. Resizable across widths via `to_width`.
38
+ - **`Trybble`** / **`Tryte`** / **`Word`** — Width-locked subclasses of `TritArray` matching the Setnex ISA unit hierarchy: 3 trits (T3, register address / sub-opcode), 9 trits (T9, byte-analogue), and 27 trits (T27, memory and register word). All inherit `TritArray`'s arithmetic and preserve their concrete type through operations.
39
+ - **`TFloat`** — Balanced ternary floating-point number using the Tekum tapered precision format (Hunhold, arXiv:2512.10964). No sign trit, truncation equals rounding, three special values (zero, infinity, NaR). Configurable width (even, ≥ 8). Addition, multiplication and division all use native trit-level arithmetic with wheel-algebra semantics for the special values.
39
40
 
40
41
  ### Multi-valued logic
41
42
 
42
43
  Five ternary logic systems, selectable at runtime via a common abstract interface:
43
44
 
44
- | System | Third value semantics | Key difference |
45
- |---|---|---|
46
- | **Kleene (K3)** | Unknown (epistemic) | Default. AND = min, OR = max |
47
- | **Łukasiewicz (L3)** | Indeterminate (ontological) | Z → Z yields P (tautological self-implication) |
48
- | **Heyting (HT)** | Non-proved (constructivist) | ~Z = N (unproved is false) |
49
- | **B3 (Bochvar)** | Both true and false (paraconsistent) | Z ∧ Z = N (contradiction resolves downward) |
50
- | **RM3 (R-mingle)** | Relevant entailment | Restricted implication |
45
+ | System | Third value semantics | Key difference |
46
+ | -------------------- | ------------------------------------ | ---------------------------------------------- |
47
+ | **Kleene (K3)** | Unknown (epistemic) | Default. AND = min, OR = max |
48
+ | **Łukasiewicz (L3)** | Indeterminate (ontological) | Z → Z yields P (tautological self-implication) |
49
+ | **Heyting (HT)** | Non-proved (constructivist) | ~Z = N (unproved is false) |
50
+ | **B3 (Bochvar)** | Meaningless (infectious) | Z is absorbing: Z ∧ Z = Z ∨ Z = Z → Z = Z |
51
+ | **RM3 (R-mingle)** | Relevant entailment | Restricted implication |
51
52
 
52
53
  Each system implements NOT, AND, OR, IMPLY, XOR, and EQUIV. Custom systems can be added by subclassing `TernaryLogic`. The `logic_mode` context manager switches the active logic system for operators on `Trit`. The `pluralize` function evaluates an expression across all five systems and reports convergence or divergence.
53
54
 
@@ -61,7 +62,7 @@ tritlib provides the building blocks for balanced ternary processor simulation:
61
62
  - Per-trit access (`__getitem__`) and immutable modification (`set`)
62
63
  - Sign extraction and absolute value
63
64
  - Consensus / anti-consensus (trit-level agreement detection)
64
- - Logic system dispatch independent of data types (LMODE selection)
65
+ - Logic system dispatch independent of data types (mirrors the per-instruction logic selector of the Setnex ISA)
65
66
  - Tekum floating-point decode/encode for FPU simulation
66
67
 
67
68
  ## Installation
@@ -70,7 +71,7 @@ tritlib provides the building blocks for balanced ternary processor simulation:
70
71
  pip install tritlib
71
72
  ```
72
73
 
73
- Requires Python ≥ 3.10. No external dependencies.
74
+ Requires Python ≥ 3.11. No external dependencies.
74
75
 
75
76
  ## Usage
76
77
 
@@ -104,42 +105,49 @@ print(int(a * b)) # 798
104
105
  q, r = divmod(Trits(13), Trits(4))
105
106
  assert int(q) * 4 + int(r) == 13
106
107
 
107
- # Division can have negative remainder
108
- q, r = divmod(Trits(20), Trits(6))
109
- assert int(q) * 6 + int(r) == 20
110
- print(int(q)) # 4
111
- print(int(r)) # -4
108
+ # The remainder is balanced: |r| <= |divisor| / 2, so it takes
109
+ # whichever sign minimises its magnitude — not the dividend's.
110
+ q, r = divmod(Trits(40), Trits(6))
111
+ assert int(q) * 6 + int(r) == 40
112
+ print(int(q)) # 7
113
+ print(int(r)) # -2
112
114
  ```
113
115
 
114
116
  ### Fixed-width words
115
117
 
116
118
  ```python
117
- from tritlib import Tryte
119
+ from tritlib import TritArray, Trybble, Tryte, Word
118
120
 
119
- w = Tryte(42, width=6)
121
+ # ISA-aligned width-locked types
122
+ print(len(Trybble(1))) # 3
123
+ print(len(Tryte(42))) # 9
124
+ print(len(Word(0))) # 27
125
+
126
+ # Arbitrary width via TritArray
127
+ w = TritArray(42, width=6)
120
128
  print(len(w)) # 6
121
129
  print(w[0]) # least significant trit
122
130
 
123
- # Saturating addition
124
- result = Tryte(300, 6).add_saturating(Tryte(300, 6))
131
+ # Saturating addition (any TritArray subclass)
132
+ result = TritArray(300, 6).add_saturating(TritArray(300, 6))
125
133
  print(int(result)) # 364 (clamped to max)
126
134
 
127
135
  # Carry detection
128
- result, carry = Tryte(300, 6).add_with_carry(Tryte(65, 6))
136
+ result, carry = TritArray(300, 6).add_with_carry(TritArray(65, 6))
129
137
  print(carry) # P (positive overflow)
130
138
  ```
131
139
 
132
140
  ### Floating-point (Tekum format)
133
141
 
134
142
  ```python
135
- from tritlib import TFloat, Tryte
143
+ from tritlib import TFloat, TritArray
136
144
 
137
145
  # Encode from Python float
138
146
  tf = TFloat(3.14, width=20)
139
147
  print(float(tf)) # ≈ 3.14
140
148
 
141
- # Decode from a register word
142
- register = Tryte(1640, 8)
149
+ # Decode from a register word (even width ≥ 8)
150
+ register = TritArray(1640, 8)
143
151
  tf = TFloat(register)
144
152
  print(float(tf)) # 1.0
145
153
 
@@ -148,6 +156,7 @@ a = TFloat(2.5, width=20)
148
156
  b = TFloat(1.3, width=20)
149
157
  print(float(a + b)) # ≈ 3.8
150
158
  print(float(a * b)) # ≈ 3.25
159
+ print(float(a / b)) # ≈ 1.923
151
160
 
152
161
  # Special values
153
162
  print(TFloat(0.0, width=8)) # 0
@@ -193,7 +202,7 @@ from tritlib.logic.plural import pluralize
193
202
  # Evaluate Z → Z across all five logic systems
194
203
  result = pluralize(lambda a, b: a.imply(b), Z, Z)
195
204
  print(result.divergent) # True — logics disagree
196
- print(result.groups) # {Z: ['K3', 'BI3', 'RM3'], P: ['L3', 'HT']}
205
+ print(result.groups) # {Trit(0): ['BI3', 'K3', 'RM3'], Trit(1): ['HT', 'L3']}
197
206
  print(result.consensus) # None — no unanimous agreement
198
207
  ```
199
208
 
@@ -204,7 +213,10 @@ src/tritlib/
204
213
  ├── __init__.py
205
214
  ├── trit.py # Trit type
206
215
  ├── trits.py # Trits arbitrary-precision type
207
- ├── tryte.py # Tryte fixed-width type
216
+ ├── tritarray.py # TritArray generic fixed-width type
217
+ ├── trybble.py # Trybble (T3, 3-trit subclass)
218
+ ├── tryte.py # Tryte (T9, 9-trit subclass)
219
+ ├── word.py # Word (T27, 27-trit subclass)
208
220
  ├── tfloat.py # TFloat Tekum floating-point type
209
221
  ├── context.py # Logic mode context manager
210
222
  └── logic/
@@ -239,8 +251,7 @@ python -m pytest
239
251
  - [x] Logic context manager (`with logic_mode(L3()): result = a & b`)
240
252
  - [x] Logical pluralism: evaluate across all systems, report convergence/divergence
241
253
  - [x] Tekum balanced ternary floating-point (encode, decode, arithmetic via float)
242
- - [x] Native trit-level `__add__` and `__mul__` (Tekum truncation = rounding)
243
- - [ ] Native trit-level `__truediv__` (currently converts via Python `float`)
254
+ - [x] Native trit-level `__add__`, `__mul__` and `__truediv__` (Tekum truncation = rounding)
244
255
  - [ ] Educational step-by-step computation output (carry propagation, partial products)
245
256
 
246
257
  ## License
@@ -249,7 +260,7 @@ MIT — see LICENSE file for details.
249
260
 
250
261
  ## Links
251
262
 
252
- - Repository: https://codeberg.org/setnex-org/tritlib
253
- - PyPI: https://pypi.org/project/tritlib/
254
- - Tekum paper: https://arxiv.org/abs/2512.10964
255
- - Setnex balanced ternary ISA: https://setnex.org
263
+ - Repository: <https://codeberg.org/setnex-org/tritlib>
264
+ - PyPI: <https://pypi.org/project/tritlib/>
265
+ - Tekum paper: <https://arxiv.org/abs/2512.10964>
266
+ - Setnex balanced ternary ISA: <https://setnex.org>