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.
- tritlib-2.1.2/CHANGELOG.md +348 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/PKG-INFO +46 -35
- {tritlib-1.2.3 → tritlib-2.1.2}/README.md +44 -33
- {tritlib-1.2.3 → tritlib-2.1.2}/pyproject.toml +11 -1
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/__init__.py +25 -1
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/base.py +10 -9
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/bochvar.py +3 -2
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/plural.py +5 -6
- tritlib-2.1.2/src/tritlib/tfloat.py +946 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/trit.py +26 -10
- tritlib-1.2.3/src/tritlib/tryte.py → tritlib-2.1.2/src/tritlib/tritarray.py +159 -119
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/trits.py +75 -35
- tritlib-2.1.2/src/tritlib/trybble.py +13 -0
- tritlib-2.1.2/src/tritlib/tryte.py +13 -0
- tritlib-2.1.2/src/tritlib/word.py +60 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat.py +286 -134
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat_add.py +24 -6
- tritlib-2.1.2/tests/test_tfloat_div.py +35 -0
- tritlib-2.1.2/tests/test_tritarray.py +277 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_trits.py +56 -31
- tritlib-2.1.2/tests/test_tryte.py +26 -0
- tritlib-2.1.2/tests/test_word.py +74 -0
- tritlib-1.2.3/CHANGELOG.md +0 -112
- tritlib-1.2.3/src/tritlib/tfloat.py +0 -675
- tritlib-1.2.3/tests/test_tryte.py +0 -247
- {tritlib-1.2.3 → tritlib-2.1.2}/.gitignore +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/LICENSE +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/context.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/__init__.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/heyting.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/kleene.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/lukasiewicz.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/src/tritlib/logic/rm3.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2/src/tritlib}/py.typed +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_context.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_logic.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_logic_kleene.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_plural.py +0 -0
- {tritlib-1.2.3 → tritlib-2.1.2}/tests/test_tfloat_mul.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: tritlib
|
|
3
|
-
Version: 1.2
|
|
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 —
|
|
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
|
-
- **`
|
|
38
|
-
- **`
|
|
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
|
|
45
|
-
|
|
46
|
-
| **Kleene (K3)**
|
|
47
|
-
| **Łukasiewicz (L3)** | Indeterminate (ontological)
|
|
48
|
-
| **Heyting (HT)**
|
|
49
|
-
| **B3 (Bochvar)** |
|
|
50
|
-
| **RM3 (R-mingle)**
|
|
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 (
|
|
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.
|
|
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
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
print(int(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 =
|
|
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) # {
|
|
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
|
-
├──
|
|
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 `
|
|
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>
|