tritlib 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.
- tritlib-0.1.0/.gitignore +5 -0
- tritlib-0.1.0/LICENSE +7 -0
- tritlib-0.1.0/PKG-INFO +82 -0
- tritlib-0.1.0/README.md +63 -0
- tritlib-0.1.0/pyproject.toml +25 -0
- tritlib-0.1.0/src/tritlib/__init__.py +13 -0
- tritlib-0.1.0/src/tritlib/logic/__init__.py +14 -0
- tritlib-0.1.0/src/tritlib/logic/base.py +28 -0
- tritlib-0.1.0/src/tritlib/logic/bi3.py +35 -0
- tritlib-0.1.0/src/tritlib/logic/heyting.py +25 -0
- tritlib-0.1.0/src/tritlib/logic/kleene.py +14 -0
- tritlib-0.1.0/src/tritlib/logic/lukasiewicz.py +22 -0
- tritlib-0.1.0/src/tritlib/logic/rm3.py +23 -0
- tritlib-0.1.0/src/tritlib/trit.py +113 -0
- tritlib-0.1.0/src/tritlib/trits.py +215 -0
- tritlib-0.1.0/src/tritlib/tryte.py +217 -0
- tritlib-0.1.0/tests/test_logic.py +92 -0
- tritlib-0.1.0/tests/test_logic_kleene.py +21 -0
- tritlib-0.1.0/tests/test_trit.py +73 -0
- tritlib-0.1.0/tests/test_trits.py +100 -0
- tritlib-0.1.0/tests/test_tryte.py +119 -0
tritlib-0.1.0/.gitignore
ADDED
tritlib-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright © 2026, Eric Tellier
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders X be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
|
tritlib-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tritlib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Balanced ternary arithmetic and multi-valued logic library
|
|
5
|
+
Author-email: Eric Tellier <eric.tellier@newick.fr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: balanced-ternary,base3,kleene-logic,ternary,trit
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# tritlib
|
|
21
|
+
|
|
22
|
+
Balanced ternary arithmetic and multi-valued logic library for Python.
|
|
23
|
+
|
|
24
|
+
## What is balanced ternary?
|
|
25
|
+
|
|
26
|
+
Balanced ternary is a numeral system in base 3 where each digit (called a *trit*) takes values from {−1, 0, +1} instead of the conventional {0, 1, 2}. It allows representing negative numbers without a sign bit, and negation is as simple as inverting every trit. Donald Knuth called it *"perhaps the prettiest number system of all."*
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- `Trit` — immutable single‑trit type with arithmetic and logic operators
|
|
31
|
+
- `Trits` — arbitrary‑length balanced ternary integers
|
|
32
|
+
- `Tryte` — fixed‑width ternary words
|
|
33
|
+
- Multiple ternary logic systems: Kleene (K3), Łukasiewicz (L3), Heyting (HT) and more
|
|
34
|
+
- Step‑by‑step arithmetic (carry propagation, partial products) for educational use
|
|
35
|
+
- Pure Python, no dependencies, type‑hinted
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install tritlib
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from tritlib.trit import Trit, P, Z, N
|
|
47
|
+
|
|
48
|
+
# Create trits
|
|
49
|
+
t = Trit(1) # +1
|
|
50
|
+
print(t) # "+"
|
|
51
|
+
print(-t) # "-"
|
|
52
|
+
|
|
53
|
+
# Predefined constants
|
|
54
|
+
assert P == Trit(1)
|
|
55
|
+
assert Z == Trit(0)
|
|
56
|
+
assert N == Trit(-1)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Roadmap
|
|
60
|
+
|
|
61
|
+
- [x] `Trit` type with immutability, arithmetic, hashing
|
|
62
|
+
- [x] Trit‑level logic (Kleene K3)
|
|
63
|
+
- [x] `Trits` arbitrary‑precision integers
|
|
64
|
+
- [x] `Tryte` fixed‑width words
|
|
65
|
+
- [x] Additional logic systems (L3, HT, BI3, Post)
|
|
66
|
+
- [ ] Conversion utilities (int, str, float, binary‑coded ternary)
|
|
67
|
+
- [ ] Educational step‑by‑step computation output
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://codeberg.org/newick_2/tritlib.git
|
|
73
|
+
cd tritlib
|
|
74
|
+
python -m venv .venv
|
|
75
|
+
source .venv/bin/activate
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
python -m pytest
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT License — see LICENSE file for details.
|
tritlib-0.1.0/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# tritlib
|
|
2
|
+
|
|
3
|
+
Balanced ternary arithmetic and multi-valued logic library for Python.
|
|
4
|
+
|
|
5
|
+
## What is balanced ternary?
|
|
6
|
+
|
|
7
|
+
Balanced ternary is a numeral system in base 3 where each digit (called a *trit*) takes values from {−1, 0, +1} instead of the conventional {0, 1, 2}. It allows representing negative numbers without a sign bit, and negation is as simple as inverting every trit. Donald Knuth called it *"perhaps the prettiest number system of all."*
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- `Trit` — immutable single‑trit type with arithmetic and logic operators
|
|
12
|
+
- `Trits` — arbitrary‑length balanced ternary integers
|
|
13
|
+
- `Tryte` — fixed‑width ternary words
|
|
14
|
+
- Multiple ternary logic systems: Kleene (K3), Łukasiewicz (L3), Heyting (HT) and more
|
|
15
|
+
- Step‑by‑step arithmetic (carry propagation, partial products) for educational use
|
|
16
|
+
- Pure Python, no dependencies, type‑hinted
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install tritlib
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from tritlib.trit import Trit, P, Z, N
|
|
28
|
+
|
|
29
|
+
# Create trits
|
|
30
|
+
t = Trit(1) # +1
|
|
31
|
+
print(t) # "+"
|
|
32
|
+
print(-t) # "-"
|
|
33
|
+
|
|
34
|
+
# Predefined constants
|
|
35
|
+
assert P == Trit(1)
|
|
36
|
+
assert Z == Trit(0)
|
|
37
|
+
assert N == Trit(-1)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Roadmap
|
|
41
|
+
|
|
42
|
+
- [x] `Trit` type with immutability, arithmetic, hashing
|
|
43
|
+
- [x] Trit‑level logic (Kleene K3)
|
|
44
|
+
- [x] `Trits` arbitrary‑precision integers
|
|
45
|
+
- [x] `Tryte` fixed‑width words
|
|
46
|
+
- [x] Additional logic systems (L3, HT, BI3, Post)
|
|
47
|
+
- [ ] Conversion utilities (int, str, float, binary‑coded ternary)
|
|
48
|
+
- [ ] Educational step‑by‑step computation output
|
|
49
|
+
|
|
50
|
+
## Development
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git clone https://codeberg.org/newick_2/tritlib.git
|
|
54
|
+
cd tritlib
|
|
55
|
+
python -m venv .venv
|
|
56
|
+
source .venv/bin/activate
|
|
57
|
+
pip install -e ".[dev]"
|
|
58
|
+
python -m pytest
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT License — see LICENSE file for details.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tritlib"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Balanced ternary arithmetic and multi-valued logic library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">= 3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Eric Tellier", email = "eric.tellier@newick.fr"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
keywords = ["ternary", "balanced-ternary", "trit", "base3", "kleene-logic"]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
dev = ["pytest", "pytest-cov"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tritlib: A Python library for trinary arithmetic and logic operations.
|
|
3
|
+
|
|
4
|
+
This package provides classes for working with trits (ternary digits), trits numbers, and fixed-width trytes.
|
|
5
|
+
It supports arithmetic operations, comparisons, and conversions between strings, integers, and trits.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .trit import Trit, N, Z, P
|
|
9
|
+
from .trits import Trits
|
|
10
|
+
from .tryte import Tryte
|
|
11
|
+
from .logic import Lukasiewicz, RM3, BI3, HT, Kleene
|
|
12
|
+
|
|
13
|
+
__all__ = ["Trit", "Trits", "Tryte", "N", "Z", "P", "Lukasiewicz", "RM3", "BI3", "HT", "Kleene"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tritlib.logic: Ternary logic operations.
|
|
3
|
+
|
|
4
|
+
This subpackage provides implementations of various ternary logic systems:
|
|
5
|
+
Lukasiewicz, RM3, BI3, Heyting, and Kleene.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .lukasiewicz import Lukasiewicz
|
|
9
|
+
from .rm3 import RM3
|
|
10
|
+
from .bi3 import BI3
|
|
11
|
+
from .heyting import HT
|
|
12
|
+
from .kleene import Kleene
|
|
13
|
+
|
|
14
|
+
__all__ = ["Lukasiewicz", "RM3", "BI3", "HT", "Kleene"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from tritlib import Trit
|
|
3
|
+
class TernaryLogic(ABC):
|
|
4
|
+
"""
|
|
5
|
+
Abstract base class for ternary logic operations.
|
|
6
|
+
Defines methods for logical NOT, AND, OR, and IMPLY operations on trits.
|
|
7
|
+
Subclasses must implement the `imply_op` method.
|
|
8
|
+
|
|
9
|
+
Methods:
|
|
10
|
+
not_op(a): Returns the logical NOT of a trit.
|
|
11
|
+
and_op(a, b): Returns the logical AND of two trits.
|
|
12
|
+
or_op(a, b): Returns the logical OR of two trits.
|
|
13
|
+
imply_op(a, b): Abstract method for logical implication (to be implemented by subclasses).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def not_op(self, a: Trit) -> Trit:
|
|
17
|
+
return -a
|
|
18
|
+
|
|
19
|
+
def and_op(self, a: Trit, b: Trit) -> Trit:
|
|
20
|
+
return a & b
|
|
21
|
+
|
|
22
|
+
def or_op(self, a: Trit, b: Trit) -> Trit:
|
|
23
|
+
return a | b
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit: ...
|
|
27
|
+
|
|
28
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from tritlib.logic.base import TernaryLogic
|
|
2
|
+
from tritlib import N, P,Z, Trit
|
|
3
|
+
|
|
4
|
+
class BI3(TernaryLogic):
|
|
5
|
+
"""
|
|
6
|
+
Implements BI3 ternary logic operations.
|
|
7
|
+
Provides specific implementations for logical AND, OR, and IMPLY operations.
|
|
8
|
+
|
|
9
|
+
Methods:
|
|
10
|
+
and_op(a, b): Returns the BI3 logical AND of two trits.
|
|
11
|
+
or_op(a, b): Returns the BI3 logical OR of two trits.
|
|
12
|
+
imply_op(a, b): Returns the BI3 logical implication of two trits.
|
|
13
|
+
"""
|
|
14
|
+
def and_op(self, a: Trit, b: Trit) -> Trit:
|
|
15
|
+
if a == N or b == N or (a == b == Z):
|
|
16
|
+
return N
|
|
17
|
+
elif a == b == P:
|
|
18
|
+
return P
|
|
19
|
+
return Z
|
|
20
|
+
|
|
21
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit:
|
|
22
|
+
print(f"BI3.imply_op called: a={a} ({a.value}), b={b} ({b.value}), b==Z: {b==Z}")
|
|
23
|
+
if a == Z or b == Z:
|
|
24
|
+
return Z
|
|
25
|
+
elif b == P:
|
|
26
|
+
return P
|
|
27
|
+
else:
|
|
28
|
+
return -a
|
|
29
|
+
|
|
30
|
+
def or_op(self, a: Trit, b: Trit) -> Trit:
|
|
31
|
+
if a == b == N :
|
|
32
|
+
return N
|
|
33
|
+
if a == P or b == P or (a == b == Z):
|
|
34
|
+
return P
|
|
35
|
+
return Z
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from tritlib.logic.base import TernaryLogic
|
|
2
|
+
from tritlib.trit import N, P,Z, Trit
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HT(TernaryLogic):
|
|
6
|
+
"""
|
|
7
|
+
Implements Heyting ternary logic operations.
|
|
8
|
+
Provides specific implementations for logical NOT and IMPLY operations.
|
|
9
|
+
|
|
10
|
+
Methods:
|
|
11
|
+
not_op(a): Returns the Heyting logical NOT of a trit.
|
|
12
|
+
imply_op(a, b): Returns the Heyting logical implication of two trits.
|
|
13
|
+
"""
|
|
14
|
+
def not_op(self, a:Trit) -> Trit:
|
|
15
|
+
return P if a == N else N
|
|
16
|
+
|
|
17
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit:
|
|
18
|
+
print("HT")
|
|
19
|
+
if b == P:
|
|
20
|
+
return P
|
|
21
|
+
elif b == Z:
|
|
22
|
+
return Z if a == P else P
|
|
23
|
+
else :
|
|
24
|
+
return self.not_op(a)
|
|
25
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from tritlib.logic.base import TernaryLogic
|
|
2
|
+
from tritlib.trit import Trit
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Kleene(TernaryLogic):
|
|
6
|
+
"""
|
|
7
|
+
Implements Kleene ternary logic operations.
|
|
8
|
+
Provides a specific implementation for the logical IMPLY operation.
|
|
9
|
+
|
|
10
|
+
Methods:
|
|
11
|
+
imply_op(a, b): Returns the Kleene logical implication of two trits.
|
|
12
|
+
"""
|
|
13
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit:
|
|
14
|
+
return -a | b
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from tritlib.logic.base import TernaryLogic
|
|
2
|
+
from tritlib.trit import Trit, P, Z
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Lukasiewicz(TernaryLogic):
|
|
6
|
+
"""
|
|
7
|
+
Implements Lukasiewicz ternary logic operations.
|
|
8
|
+
Provides specific implementations for logical NOT and IMPLY operations.
|
|
9
|
+
|
|
10
|
+
Methods:
|
|
11
|
+
not_op(a): Returns the Lukasiewicz logical NOT of a trit.
|
|
12
|
+
imply_op(a, b): Returns the Lukasiewicz logical implication of two trits.
|
|
13
|
+
"""
|
|
14
|
+
def not_op(self, a: Trit) -> Trit:
|
|
15
|
+
return -a
|
|
16
|
+
|
|
17
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit:
|
|
18
|
+
if a == b == Z:
|
|
19
|
+
return P
|
|
20
|
+
else:
|
|
21
|
+
return -a | b
|
|
22
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from tritlib.logic.base import TernaryLogic
|
|
2
|
+
from tritlib.trit import Trit, P, Z, N
|
|
3
|
+
|
|
4
|
+
class RM3(TernaryLogic):
|
|
5
|
+
"""
|
|
6
|
+
Implements RM3 ternary logic operations.
|
|
7
|
+
Provides specific implementations for logical NOT and IMPLY operations.
|
|
8
|
+
|
|
9
|
+
Methods:
|
|
10
|
+
not_op(a): Returns the RM3 logical NOT of a trit.
|
|
11
|
+
imply_op(a, b): Returns the RM3 logical implication of two trits.
|
|
12
|
+
"""
|
|
13
|
+
def imply_op(self, a: Trit, b: Trit) -> Trit:
|
|
14
|
+
if b == P :
|
|
15
|
+
return P
|
|
16
|
+
elif b == Z:
|
|
17
|
+
return -a
|
|
18
|
+
else :
|
|
19
|
+
return P if a == N else N
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def not_op(self, a: Trit) -> Trit:
|
|
23
|
+
return -a
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from functools import total_ordering
|
|
2
|
+
|
|
3
|
+
@total_ordering
|
|
4
|
+
class Trit:
|
|
5
|
+
"""
|
|
6
|
+
Represents an immutable trinary digit (trit) with possible values: N (-1), Z (0), or P (1).
|
|
7
|
+
Provides arithmetic and logical operations for trits, including addition, multiplication,
|
|
8
|
+
negation, and bitwise operations.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
_value (int): Internal value of the trit (-1, 0, or 1).
|
|
12
|
+
|
|
13
|
+
Properties:
|
|
14
|
+
value (int): Returns the integer value of the trit.
|
|
15
|
+
|
|
16
|
+
Methods:
|
|
17
|
+
__repr__(): Returns the official string representation of the trit.
|
|
18
|
+
__str__(): Returns the user-friendly string representation ('-', '0', or '+').
|
|
19
|
+
__eq__(other): Checks equality with another trit.
|
|
20
|
+
__hash__(): Returns the hash of the trit.
|
|
21
|
+
__neg__(): Returns the negation of the trit.
|
|
22
|
+
__mul__(other): Multiplies two trits.
|
|
23
|
+
half_add(other): Performs a half-addition with another trit, returning sum and carry.
|
|
24
|
+
full_add(other, carry): Performs a full-addition with another trit and a carry, returning sum and carry.
|
|
25
|
+
__invert__(): Returns the negation of the trit (alias for __neg__).
|
|
26
|
+
__and__(other): Returns the logical AND of two trits (minimum value).
|
|
27
|
+
__or__(other): Returns the logical OR of two trits (maximum value).
|
|
28
|
+
__lt__(other): Compares two trits for ordering.
|
|
29
|
+
|
|
30
|
+
Constants:
|
|
31
|
+
N (Trit): Represents the trit value -1.
|
|
32
|
+
Z (Trit): Represents the trit value 0.
|
|
33
|
+
P (Trit): Represents the trit value 1.
|
|
34
|
+
"""
|
|
35
|
+
__slots__ = ("_value",)
|
|
36
|
+
|
|
37
|
+
def __init__(self, value: int):
|
|
38
|
+
if not -2 < value < 2:
|
|
39
|
+
raise ValueError
|
|
40
|
+
|
|
41
|
+
object.__setattr__(self,"_value", value)
|
|
42
|
+
|
|
43
|
+
def __repr__(self) -> str:
|
|
44
|
+
return f"Trit({self._value})"
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
if self._value > 0:
|
|
48
|
+
return "+"
|
|
49
|
+
elif self._value == 0:
|
|
50
|
+
return "0"
|
|
51
|
+
else :
|
|
52
|
+
return "-"
|
|
53
|
+
|
|
54
|
+
def __eq__(self, other) -> bool:
|
|
55
|
+
if not isinstance(other, Trit):
|
|
56
|
+
return NotImplemented
|
|
57
|
+
return self.value == other.value
|
|
58
|
+
|
|
59
|
+
def __hash__(self) -> int:
|
|
60
|
+
return hash(self._value)
|
|
61
|
+
|
|
62
|
+
def __setattr__(self, name, value):
|
|
63
|
+
raise AttributeError("Trit is immutable")
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def value(self) -> int:
|
|
67
|
+
return self._value
|
|
68
|
+
|
|
69
|
+
def __neg__(self) :
|
|
70
|
+
return Trit(-self._value)
|
|
71
|
+
|
|
72
|
+
def __mul__(self, other) :
|
|
73
|
+
if not isinstance(other, Trit):
|
|
74
|
+
return NotImplemented
|
|
75
|
+
return Trit(self.value * other.value)
|
|
76
|
+
|
|
77
|
+
def half_add(self, other):
|
|
78
|
+
if not isinstance(other, Trit):
|
|
79
|
+
return NotImplemented
|
|
80
|
+
if self.value == other.value :
|
|
81
|
+
s = -self
|
|
82
|
+
c = self
|
|
83
|
+
else:
|
|
84
|
+
s = Trit(self.value + other.value)
|
|
85
|
+
c = Z
|
|
86
|
+
return (s,c)
|
|
87
|
+
|
|
88
|
+
def full_add(self, other, carry):
|
|
89
|
+
if not (isinstance(other, Trit) and isinstance(carry, Trit)):
|
|
90
|
+
return NotImplemented
|
|
91
|
+
s1, c1 = self.half_add(other)
|
|
92
|
+
s2, c2 = s1.half_add(carry)
|
|
93
|
+
return s2, Trit(c1.value+c2.value)
|
|
94
|
+
|
|
95
|
+
def __invert__(self):
|
|
96
|
+
return self.__neg__()
|
|
97
|
+
|
|
98
|
+
def __and__(self, other):
|
|
99
|
+
if not isinstance(other, Trit):
|
|
100
|
+
return NotImplemented
|
|
101
|
+
return Trit(min(self._value, other._value))
|
|
102
|
+
|
|
103
|
+
def __or__(self, other):
|
|
104
|
+
if not isinstance(other, Trit):
|
|
105
|
+
return NotImplemented
|
|
106
|
+
return Trit(max(self._value, other._value))
|
|
107
|
+
|
|
108
|
+
def __lt__(self, other):
|
|
109
|
+
return self.value < other.value
|
|
110
|
+
|
|
111
|
+
N = Trit(-1)
|
|
112
|
+
Z = Trit(0)
|
|
113
|
+
P = Trit(1)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
from functools import total_ordering
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
from tritlib.trit import Trit, N, Z, P
|
|
4
|
+
from itertools import zip_longest
|
|
5
|
+
|
|
6
|
+
def _str_to_trits(s: str):
|
|
7
|
+
trit_dict = {"+": P, "-": N, "0": Z}
|
|
8
|
+
trits = []
|
|
9
|
+
for char in reversed(s):
|
|
10
|
+
if char not in trit_dict:
|
|
11
|
+
raise ValueError
|
|
12
|
+
else:
|
|
13
|
+
trits.append(trit_dict.get(char))
|
|
14
|
+
return trits
|
|
15
|
+
|
|
16
|
+
@total_ordering
|
|
17
|
+
class Trits:
|
|
18
|
+
"""
|
|
19
|
+
Represents an immutable number in a balanced ternary system using trits (ternary digits: P, Z, N).
|
|
20
|
+
Supports arithmetic operations, comparisons, and conversions between strings and integers.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
_trits (tuple[Trit]): Internal tuple of Trit objects representing the number.
|
|
24
|
+
|
|
25
|
+
Class Methods:
|
|
26
|
+
_from_trits(trits): Creates a Trits object from a list of Trit objects.
|
|
27
|
+
_from_le_trits(trits): Creates a Trits object from a list of Trit objects in little-endian order.
|
|
28
|
+
from_str(str_value): Creates a Trits object from a string representation (e.g., "+-0").
|
|
29
|
+
|
|
30
|
+
Properties:
|
|
31
|
+
_le_trits (list[Trit]): Returns the trits in little-endian order.
|
|
32
|
+
|
|
33
|
+
Methods:
|
|
34
|
+
__hash__(): Returns the hash of the Trits object.
|
|
35
|
+
__str__(): Returns the string representation of the Trits number.
|
|
36
|
+
__init__(n): Initializes a Trits object from a string or integer.
|
|
37
|
+
__eq__(other): Checks equality with another Trits object.
|
|
38
|
+
__lt__(other): Compares two Trits objects for ordering.
|
|
39
|
+
__add__(other): Adds two Trits numbers.
|
|
40
|
+
__sub__(other): Subtracts two Trits numbers.
|
|
41
|
+
__mul__(other): Multiplies two Trits numbers.
|
|
42
|
+
__floordiv__(other): Performs floor division between two Trits numbers.
|
|
43
|
+
to_int(): Converts the Trits number to an integer.
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
>>> trits = Trits.from_str("+-0")
|
|
47
|
+
>>> print(trits)
|
|
48
|
+
+-0
|
|
49
|
+
"""
|
|
50
|
+
__slots__ = ("_trits",)
|
|
51
|
+
|
|
52
|
+
def __hash__(self) -> int:
|
|
53
|
+
return hash(self._trits)
|
|
54
|
+
|
|
55
|
+
def __setattr__(self, name, value):
|
|
56
|
+
raise AttributeError("Trits is immutable")
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def _from_trits(cls, trits: list[Trit]):
|
|
60
|
+
while len(trits)>1 and trits[-1] == Z:
|
|
61
|
+
trits.pop()
|
|
62
|
+
obj = cls.__new__(cls)
|
|
63
|
+
if len(trits) == 0:
|
|
64
|
+
trits = [Z]
|
|
65
|
+
object.__setattr__(obj,"_trits",tuple(trits))
|
|
66
|
+
return obj
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def _from_le_trits(cls, trits: list[Trit]):
|
|
70
|
+
trits.reverse()
|
|
71
|
+
return cls._from_trits(trits)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def _le_trits(self):
|
|
75
|
+
a = list(self.trits)
|
|
76
|
+
a.reverse()
|
|
77
|
+
return a
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def from_str(cls, str_value: str):
|
|
81
|
+
return cls._from_trits(_str_to_trits(str_value))
|
|
82
|
+
|
|
83
|
+
def __str__(self):
|
|
84
|
+
trit_dict = {P: "+", N: "-", Z: "0"}
|
|
85
|
+
return "".join(trit_dict[t] for t in reversed(self._trits))
|
|
86
|
+
|
|
87
|
+
def __init__(self, n):
|
|
88
|
+
if isinstance(n, str):
|
|
89
|
+
object.__setattr__(self, "_trits", tuple(_str_to_trits(n)))
|
|
90
|
+
elif isinstance(n, int):
|
|
91
|
+
if n == 0:
|
|
92
|
+
object.__setattr__(self, "_trits", (Z,))
|
|
93
|
+
return
|
|
94
|
+
_trits = []
|
|
95
|
+
while n != 0:
|
|
96
|
+
n, r = divmod(n, 3)
|
|
97
|
+
if r == 2 :
|
|
98
|
+
n += 1
|
|
99
|
+
_trits.append(N)
|
|
100
|
+
elif r == 1:
|
|
101
|
+
_trits.append(P)
|
|
102
|
+
else :
|
|
103
|
+
_trits.append(Z)
|
|
104
|
+
object.__setattr__(self, "_trits", tuple(_trits))
|
|
105
|
+
else:
|
|
106
|
+
return NotImplemented
|
|
107
|
+
|
|
108
|
+
def __repr__(self):
|
|
109
|
+
return f"Trits(\"{str(self)}\")"
|
|
110
|
+
|
|
111
|
+
def __int__(self) -> int:
|
|
112
|
+
acc = 0
|
|
113
|
+
# Horner's method
|
|
114
|
+
for trit in reversed(self._trits):
|
|
115
|
+
acc = acc * 3 + trit.value
|
|
116
|
+
|
|
117
|
+
return acc
|
|
118
|
+
|
|
119
|
+
def __list__(self) -> list:
|
|
120
|
+
return list(self._trits)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def trits(self) -> Tuple[Trit]:
|
|
124
|
+
return self._trits
|
|
125
|
+
|
|
126
|
+
def __eq__(self, other):
|
|
127
|
+
if not isinstance(other, Trits):
|
|
128
|
+
return NotImplemented
|
|
129
|
+
return self.trits == other.trits
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def __add__(self, other):
|
|
133
|
+
if not isinstance(other, Trits):
|
|
134
|
+
return NotImplemented
|
|
135
|
+
_trits = []
|
|
136
|
+
c = Z
|
|
137
|
+
for a,b in zip_longest(self.trits, other.trits, fillvalue=Z):
|
|
138
|
+
s, c = a.full_add(b, c)
|
|
139
|
+
_trits.append(s)
|
|
140
|
+
if c != Z:
|
|
141
|
+
_trits.append(c)
|
|
142
|
+
return Trits._from_trits(_trits)
|
|
143
|
+
|
|
144
|
+
def __neg__(self):
|
|
145
|
+
return Trits._from_trits([-t for t in self._trits])
|
|
146
|
+
|
|
147
|
+
def __sub__(self, other):
|
|
148
|
+
if not isinstance(other, Trits):
|
|
149
|
+
return NotImplemented
|
|
150
|
+
return self + -other
|
|
151
|
+
|
|
152
|
+
def __mul__(self, other):
|
|
153
|
+
if not isinstance(other, Trits):
|
|
154
|
+
return NotImplemented
|
|
155
|
+
result = Trits._from_trits([Z])
|
|
156
|
+
for i,t in enumerate(self._trits):
|
|
157
|
+
if t == Z:
|
|
158
|
+
continue
|
|
159
|
+
partial = [Z] * i + [t*a for a in other._trits]
|
|
160
|
+
p = Trits._from_trits(partial)
|
|
161
|
+
result = result + p
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
def __len__(self):
|
|
165
|
+
return len(self._trits)
|
|
166
|
+
|
|
167
|
+
def __lt__(self, other):
|
|
168
|
+
if not isinstance(other, Trits):
|
|
169
|
+
return NotImplemented
|
|
170
|
+
if len(self) < len(other):
|
|
171
|
+
return other.trits[-1] == P
|
|
172
|
+
elif len(self) > len(other):
|
|
173
|
+
return self.trits[-1] == N
|
|
174
|
+
else:
|
|
175
|
+
for t1, t2 in zip(reversed(self.trits), reversed(other.trits)):
|
|
176
|
+
if t1 < t2:
|
|
177
|
+
return True
|
|
178
|
+
elif t1 > t2:
|
|
179
|
+
return False
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def __divmod__(self, other):
|
|
183
|
+
if not isinstance(other, Trits):
|
|
184
|
+
return NotImplemented
|
|
185
|
+
if other == Trits(0):
|
|
186
|
+
raise ZeroDivisionError
|
|
187
|
+
if self._trits == (Z,):
|
|
188
|
+
return Trits(0), Trits(0)
|
|
189
|
+
if len(self) < len(other):
|
|
190
|
+
return Trits(0), self
|
|
191
|
+
|
|
192
|
+
s = self._le_trits
|
|
193
|
+
d = other._le_trits
|
|
194
|
+
partial_rest = s[:len(d)-1]
|
|
195
|
+
rem = s[len(d)-1:]
|
|
196
|
+
q = []
|
|
197
|
+
while len(rem) > 0:
|
|
198
|
+
partial_rest += [rem.pop(0)]
|
|
199
|
+
if d[0]*partial_rest[0] == P:
|
|
200
|
+
q.append(P)
|
|
201
|
+
pr = Trits._from_le_trits(partial_rest) - other
|
|
202
|
+
partial_rest = pr._le_trits
|
|
203
|
+
elif d[0]*partial_rest[0] == N:
|
|
204
|
+
q.append(N)
|
|
205
|
+
pr = Trits._from_le_trits(partial_rest) + other
|
|
206
|
+
partial_rest = pr._le_trits
|
|
207
|
+
else:
|
|
208
|
+
q.append(Z)
|
|
209
|
+
return Trits._from_le_trits(q), Trits._from_le_trits(partial_rest)
|
|
210
|
+
|
|
211
|
+
def __floordiv__(self, other):
|
|
212
|
+
return divmod(self, other)[0]
|
|
213
|
+
|
|
214
|
+
def __mod__(self, other):
|
|
215
|
+
return divmod(self, other)[1]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from functools import total_ordering
|
|
2
|
+
from tritlib.trits import Trits
|
|
3
|
+
from tritlib.trit import Z, N, P, Trit
|
|
4
|
+
|
|
5
|
+
@total_ordering
|
|
6
|
+
class Tryte:
|
|
7
|
+
"""
|
|
8
|
+
Represents a fixed-width trit-based number system (tryte).
|
|
9
|
+
Supports arithmetic operations, comparisons, and conversions between trits and integers.
|
|
10
|
+
The class is immutable and handles overflow by truncating to the specified width.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
_trits (tuple[Trit]): Internal tuple of Trit objects representing the tryte.
|
|
14
|
+
_width (int): Fixed width of the tryte.
|
|
15
|
+
|
|
16
|
+
Properties:
|
|
17
|
+
width (int): Returns the width of the tryte.
|
|
18
|
+
trits (list[Trit]): Returns the list of Trit objects representing the tryte.
|
|
19
|
+
_le_trits (list[Trit]): Returns the trits in little-endian order.
|
|
20
|
+
|
|
21
|
+
Class Methods:
|
|
22
|
+
_from_trits(trits, width): Creates a Tryte object from a list of Trit objects and a width.
|
|
23
|
+
from_le_trits(trits, width): Creates a Tryte object from a list of Trit objects in little-endian order and a width.
|
|
24
|
+
|
|
25
|
+
Methods:
|
|
26
|
+
__hash__(): Returns the hash of the Tryte object.
|
|
27
|
+
__init__(value, width): Initializes a Tryte object from a value (int or Trits) and a width.
|
|
28
|
+
__int__(): Converts the Tryte number to an integer.
|
|
29
|
+
to_trits(): Converts the Tryte object to a Trits object.
|
|
30
|
+
resize(width): Returns a new Tryte object with the specified width.
|
|
31
|
+
__len__(): Returns the number of trits in the tryte.
|
|
32
|
+
__eq__(other): Checks equality with another Tryte object.
|
|
33
|
+
__lt__(other): Compares two Tryte objects for ordering.
|
|
34
|
+
__repr__(): Returns the official string representation of the Tryte object.
|
|
35
|
+
__str__(): Returns the string representation of the Tryte number.
|
|
36
|
+
__add__(other): Adds two Tryte numbers.
|
|
37
|
+
__neg__(): Returns the negation of the Tryte number.
|
|
38
|
+
__sub__(other): Subtracts two Tryte numbers.
|
|
39
|
+
__mul__(other): Multiplies two Tryte numbers.
|
|
40
|
+
add_with_carry(other): Adds two Tryte numbers and returns the result and carry flag.
|
|
41
|
+
__divmod__(other): Performs division and modulus operations between two Tryte numbers.
|
|
42
|
+
__floordiv__(other): Performs floor division between two Tryte numbers.
|
|
43
|
+
__mod__(other): Performs modulus operation between two Tryte numbers.
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
>>> tryte = Tryte(10, 5)
|
|
47
|
+
>>> print(tryte)
|
|
48
|
+
+-000
|
|
49
|
+
"""
|
|
50
|
+
__slots__ = ("_trits", "_width")
|
|
51
|
+
|
|
52
|
+
def __hash__(self) -> int:
|
|
53
|
+
return hash(self._trits)
|
|
54
|
+
|
|
55
|
+
def __setattr__(self, name, value):
|
|
56
|
+
raise AttributeError("Trits is immutable")
|
|
57
|
+
|
|
58
|
+
def __init__(self, value, width: int):
|
|
59
|
+
trits = Trits(value)
|
|
60
|
+
if len(trits) > width:
|
|
61
|
+
raise OverflowError
|
|
62
|
+
else:
|
|
63
|
+
trits = list(trits.trits) + [Z] * (width - len(trits.trits))
|
|
64
|
+
object.__setattr__(self, "_trits", tuple(trits))
|
|
65
|
+
object.__setattr__(self, "_width", width)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def _from_trits(cls, trits: list[Trit], width: int):
|
|
69
|
+
# Do not raise OverflowError to simulate hardware behaviour
|
|
70
|
+
trits = trits + [Z]*(width - len(trits))
|
|
71
|
+
obj = cls.__new__(cls)
|
|
72
|
+
object.__setattr__(obj,"_trits",tuple(trits[:width]))
|
|
73
|
+
object.__setattr__(obj,"_width",width)
|
|
74
|
+
return obj
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def width(self) -> int:
|
|
78
|
+
return self._width
|
|
79
|
+
|
|
80
|
+
def __int__(self) -> int:
|
|
81
|
+
acc = 0
|
|
82
|
+
# Horner's method
|
|
83
|
+
for trit in reversed(self._trits):
|
|
84
|
+
acc = acc * 3 + trit.value
|
|
85
|
+
return acc
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def trits(self) -> list[Trit]:
|
|
89
|
+
return list(self._trits)
|
|
90
|
+
|
|
91
|
+
def to_trits(self) -> Trits:
|
|
92
|
+
s = self.trits
|
|
93
|
+
while s[-1] == Z and len(s)>1:
|
|
94
|
+
s= s[:-1]
|
|
95
|
+
return Trits._from_trits(s)
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def _le_trits(self) -> list[Trit]:
|
|
99
|
+
s = list(self.trits)
|
|
100
|
+
r = []
|
|
101
|
+
while len(s)>0:
|
|
102
|
+
a = s.pop()
|
|
103
|
+
if len(r) == 0 and a == Z:
|
|
104
|
+
continue
|
|
105
|
+
r.append(a)
|
|
106
|
+
return [Z] if len(r) == 0 else r
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def from_le_trits(cls, trits: list[Trit], width: int):
|
|
110
|
+
trits.reverse()
|
|
111
|
+
return cls._from_trits(trits, width)
|
|
112
|
+
|
|
113
|
+
def resize(self, width):
|
|
114
|
+
return Tryte._from_trits(self.trits, width)
|
|
115
|
+
|
|
116
|
+
def __len__(self) -> int:
|
|
117
|
+
return len(self._trits)
|
|
118
|
+
|
|
119
|
+
def __eq__(self, other) -> bool:
|
|
120
|
+
if len(self) != len(other):
|
|
121
|
+
raise ValueError
|
|
122
|
+
return self.trits == other.trits
|
|
123
|
+
|
|
124
|
+
def __lt__(self, other):
|
|
125
|
+
if len(self) != len(other):
|
|
126
|
+
raise ValueError
|
|
127
|
+
for t1, t2 in zip(reversed(self.trits), reversed(other.trits)):
|
|
128
|
+
if t1 < t2:
|
|
129
|
+
return True
|
|
130
|
+
elif t1 > t2:
|
|
131
|
+
return False
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def __repr__(self):
|
|
135
|
+
return f'Tryte({int(self)}, {self.width})'
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def __str__(self):
|
|
139
|
+
trit_dict = {P: "+", N: "-", Z: "0"}
|
|
140
|
+
return "".join(trit_dict[t] for t in reversed(self._trits))
|
|
141
|
+
|
|
142
|
+
def __add__(self, other):
|
|
143
|
+
if not isinstance(other, Tryte):
|
|
144
|
+
return NotImplemented
|
|
145
|
+
if len(self) != len(other):
|
|
146
|
+
raise ValueError
|
|
147
|
+
_trits = []
|
|
148
|
+
c = Z
|
|
149
|
+
for a,b in zip(self.trits, other.trits):
|
|
150
|
+
s, c = a.full_add(b, c)
|
|
151
|
+
_trits.append(s)
|
|
152
|
+
return Tryte._from_trits(_trits, self.width)
|
|
153
|
+
|
|
154
|
+
def __neg__(self):
|
|
155
|
+
return Tryte._from_trits([-t for t in self.trits], self.width)
|
|
156
|
+
|
|
157
|
+
def __sub__(self, other):
|
|
158
|
+
return self + -other
|
|
159
|
+
|
|
160
|
+
def __mul__(self, other):
|
|
161
|
+
if not isinstance(other, Tryte):
|
|
162
|
+
return NotImplemented
|
|
163
|
+
result = Tryte._from_trits([Z], self.width)
|
|
164
|
+
for i,t in enumerate(self._trits):
|
|
165
|
+
if t == Z:
|
|
166
|
+
continue
|
|
167
|
+
partial = [Z] * i + [t*a for a in other._trits]
|
|
168
|
+
p = Tryte._from_trits(partial, self.width)
|
|
169
|
+
result = result + p
|
|
170
|
+
return Tryte._from_trits(list(result.trits), self.width)
|
|
171
|
+
|
|
172
|
+
def add_with_carry(self, other):
|
|
173
|
+
if not isinstance(other, Tryte):
|
|
174
|
+
return NotImplemented
|
|
175
|
+
if len(self) != len(other):
|
|
176
|
+
raise ValueError
|
|
177
|
+
_trits = []
|
|
178
|
+
c = Z
|
|
179
|
+
for a,b in zip(self.trits, other.trits):
|
|
180
|
+
s, c = a.full_add(b, c)
|
|
181
|
+
_trits.append(s)
|
|
182
|
+
return Tryte._from_trits(_trits, self.width), (c != Z)
|
|
183
|
+
|
|
184
|
+
def __divmod__(self, other):
|
|
185
|
+
if not isinstance(other, Tryte):
|
|
186
|
+
return NotImplemented
|
|
187
|
+
if other == Tryte(0, self.width):
|
|
188
|
+
raise ZeroDivisionError
|
|
189
|
+
if self == Tryte(0, self.width):
|
|
190
|
+
return Tryte(0, self.width), Tryte(0, self.width)
|
|
191
|
+
if len(self) < len(other):
|
|
192
|
+
return Tryte(0, self.width), self
|
|
193
|
+
|
|
194
|
+
s = self._le_trits
|
|
195
|
+
d = other._le_trits
|
|
196
|
+
partial_rest = s[:len(d)-1]
|
|
197
|
+
rem = s[len(d)-1:]
|
|
198
|
+
q = []
|
|
199
|
+
while len(rem) > 0:
|
|
200
|
+
partial_rest += [rem.pop(0)]
|
|
201
|
+
if d[0]*partial_rest[0] == P:
|
|
202
|
+
q.append(P)
|
|
203
|
+
pr = Tryte.from_le_trits(partial_rest, self.width) - other
|
|
204
|
+
partial_rest = pr._le_trits
|
|
205
|
+
elif d[0]*partial_rest[0] == N:
|
|
206
|
+
q.append(N)
|
|
207
|
+
pr = Tryte.from_le_trits(partial_rest, self.width) + other
|
|
208
|
+
partial_rest = pr._le_trits
|
|
209
|
+
else:
|
|
210
|
+
q.append(Z)
|
|
211
|
+
return Tryte.from_le_trits(q, self.width), Tryte.from_le_trits(partial_rest, self.width)
|
|
212
|
+
|
|
213
|
+
def __floordiv__(self, other):
|
|
214
|
+
return divmod(self, other)[0]
|
|
215
|
+
|
|
216
|
+
def __mod__(self, other):
|
|
217
|
+
return divmod(self, other)[1]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from tritlib import Z, N, P, RM3, BI3, Lukasiewicz, HT, Kleene
|
|
2
|
+
|
|
3
|
+
def test_logic_kleene():
|
|
4
|
+
logic = Kleene()
|
|
5
|
+
assert logic.and_op(N,P) == N
|
|
6
|
+
|
|
7
|
+
def test_kleene_imply():
|
|
8
|
+
logic = Kleene()
|
|
9
|
+
assert logic.imply_op(N,N) == P
|
|
10
|
+
assert logic.imply_op(Z,N) == Z
|
|
11
|
+
assert logic.imply_op(P,N) == N
|
|
12
|
+
assert logic.imply_op(N,Z) == P
|
|
13
|
+
assert logic.imply_op(Z,Z) == Z
|
|
14
|
+
assert logic.imply_op(P,Z) == Z
|
|
15
|
+
assert logic.imply_op(N,P) == P
|
|
16
|
+
assert logic.imply_op(Z,P) == P
|
|
17
|
+
assert logic.imply_op(P,P) == P
|
|
18
|
+
|
|
19
|
+
def test_lukasiewicz_imply():
|
|
20
|
+
logic = Lukasiewicz()
|
|
21
|
+
assert logic.imply_op(N,N) == P
|
|
22
|
+
assert logic.imply_op(Z,N) == Z
|
|
23
|
+
assert logic.imply_op(P,N) == N
|
|
24
|
+
assert logic.imply_op(N,Z) == P
|
|
25
|
+
assert logic.imply_op(Z,Z) == P
|
|
26
|
+
assert logic.imply_op(P,Z) == Z
|
|
27
|
+
assert logic.imply_op(N,P) == P
|
|
28
|
+
assert logic.imply_op(Z,P) == P
|
|
29
|
+
assert logic.imply_op(P,P) == P
|
|
30
|
+
|
|
31
|
+
def test_heyting():
|
|
32
|
+
logic = HT()
|
|
33
|
+
assert logic.not_op(N) == P
|
|
34
|
+
assert logic.not_op(Z) == N
|
|
35
|
+
assert logic.not_op(P) == N
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
assert logic.imply_op(N,N) == P
|
|
39
|
+
assert logic.imply_op(Z,N) == N
|
|
40
|
+
assert logic.imply_op(P,N) == N
|
|
41
|
+
assert logic.imply_op(N,Z) == P
|
|
42
|
+
assert logic.imply_op(Z,Z) == P
|
|
43
|
+
assert logic.imply_op(P,Z) == Z
|
|
44
|
+
assert logic.imply_op(N,P) == P
|
|
45
|
+
assert logic.imply_op(Z,P) == P
|
|
46
|
+
assert logic.imply_op(P,P) == P
|
|
47
|
+
|
|
48
|
+
def test_rmingle3():
|
|
49
|
+
logic = RM3()
|
|
50
|
+
assert logic.imply_op(N,N) == P
|
|
51
|
+
assert logic.imply_op(Z,N) == N
|
|
52
|
+
assert logic.imply_op(P,N) == N
|
|
53
|
+
assert logic.imply_op(N,Z) == P
|
|
54
|
+
assert logic.imply_op(Z,Z) == Z
|
|
55
|
+
assert logic.imply_op(P,Z) == N
|
|
56
|
+
assert logic.imply_op(N,P) == P
|
|
57
|
+
assert logic.imply_op(Z,P) == P
|
|
58
|
+
assert logic.imply_op(P,P) == P
|
|
59
|
+
|
|
60
|
+
def test_paraconsistance():
|
|
61
|
+
logic = BI3()
|
|
62
|
+
print(type(logic).__mro__)
|
|
63
|
+
print(type(logic).imply_op)
|
|
64
|
+
assert logic.and_op(N,N) == N
|
|
65
|
+
assert logic.and_op(N,Z) == N
|
|
66
|
+
assert logic.and_op(N,P) == N
|
|
67
|
+
assert logic.and_op(Z,N) == N
|
|
68
|
+
assert logic.and_op(Z,Z) == N
|
|
69
|
+
assert logic.and_op(Z,P) == Z
|
|
70
|
+
assert logic.and_op(P,N) == N
|
|
71
|
+
assert logic.and_op(P,Z) == Z
|
|
72
|
+
assert logic.and_op(P,P) == P
|
|
73
|
+
|
|
74
|
+
assert logic.or_op(N,N) == N
|
|
75
|
+
assert logic.or_op(N,Z) == Z
|
|
76
|
+
assert logic.or_op(N,P) == P
|
|
77
|
+
assert logic.or_op(Z,N) == Z
|
|
78
|
+
assert logic.or_op(Z,Z) == P
|
|
79
|
+
assert logic.or_op(Z,P) == P
|
|
80
|
+
assert logic.or_op(P,N) == P
|
|
81
|
+
assert logic.or_op(P,Z) == P
|
|
82
|
+
assert logic.or_op(P,P) == P
|
|
83
|
+
|
|
84
|
+
assert logic.imply_op(N,N) == P
|
|
85
|
+
assert logic.imply_op(Z,N) == Z
|
|
86
|
+
assert logic.imply_op(P,N) == N
|
|
87
|
+
assert logic.imply_op(N,Z) == Z
|
|
88
|
+
assert logic.imply_op(Z,Z) == Z
|
|
89
|
+
assert logic.imply_op(P,Z) == Z
|
|
90
|
+
assert logic.imply_op(N,P) == P
|
|
91
|
+
assert logic.imply_op(Z,P) == Z
|
|
92
|
+
assert logic.imply_op(P,P) == P
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from tritlib.trit import Z, N, P
|
|
2
|
+
|
|
3
|
+
def test_and():
|
|
4
|
+
assert (P & P) == P
|
|
5
|
+
assert (P & Z) == Z
|
|
6
|
+
assert (P & N) == N
|
|
7
|
+
assert (Z & Z) == Z
|
|
8
|
+
assert (Z & N) == N
|
|
9
|
+
|
|
10
|
+
def test_or():
|
|
11
|
+
assert (N | N) == N
|
|
12
|
+
assert (N | Z) == Z
|
|
13
|
+
assert (N | P) == P
|
|
14
|
+
assert (Z | Z) == Z
|
|
15
|
+
assert (Z | P) == P
|
|
16
|
+
|
|
17
|
+
def test_not():
|
|
18
|
+
assert ~P == N
|
|
19
|
+
assert ~Z == Z
|
|
20
|
+
assert ~N == P
|
|
21
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from tritlib.trit import Trit, N, P, Z
|
|
2
|
+
import pytest
|
|
3
|
+
def test_tritP():
|
|
4
|
+
t = Trit(1)
|
|
5
|
+
assert str(t) == "+"
|
|
6
|
+
assert t == P
|
|
7
|
+
assert repr(t) == "Trit(1)"
|
|
8
|
+
|
|
9
|
+
def test_tritN():
|
|
10
|
+
t = Trit(-1)
|
|
11
|
+
assert str(t) == "-"
|
|
12
|
+
assert t == N
|
|
13
|
+
assert repr(t) == "Trit(-1)"
|
|
14
|
+
|
|
15
|
+
def test_tritZ():
|
|
16
|
+
t = Trit(0)
|
|
17
|
+
assert str(t) == "0"
|
|
18
|
+
assert t == Z
|
|
19
|
+
assert repr(t) == "Trit(0)"
|
|
20
|
+
|
|
21
|
+
def test_trit_invalid():
|
|
22
|
+
with pytest.raises(ValueError):
|
|
23
|
+
Trit(2)
|
|
24
|
+
with pytest.raises(ValueError):
|
|
25
|
+
Trit(-3)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_hash():
|
|
29
|
+
assert hash(Trit(1)) == hash(P)
|
|
30
|
+
assert hash(Trit(0)) == hash(Z)
|
|
31
|
+
assert hash(Trit(-1)) == hash(N)
|
|
32
|
+
|
|
33
|
+
def test_immutable():
|
|
34
|
+
t = Trit(0)
|
|
35
|
+
with pytest.raises(AttributeError):
|
|
36
|
+
t._value = -1
|
|
37
|
+
|
|
38
|
+
def test_negation():
|
|
39
|
+
assert -P == N
|
|
40
|
+
assert -N == P
|
|
41
|
+
assert -Z == Z
|
|
42
|
+
|
|
43
|
+
def test_multiplication():
|
|
44
|
+
assert P * P == P
|
|
45
|
+
assert P * N == N
|
|
46
|
+
assert N * N == P
|
|
47
|
+
assert Z * P == Z
|
|
48
|
+
assert Z * Z == Z
|
|
49
|
+
|
|
50
|
+
def test_half_add():
|
|
51
|
+
# Pas de retenue
|
|
52
|
+
s, c = P.half_add(N)
|
|
53
|
+
assert s == Z
|
|
54
|
+
assert c == Z
|
|
55
|
+
|
|
56
|
+
# Retenue négative
|
|
57
|
+
s, c = N.half_add(N)
|
|
58
|
+
assert s == P
|
|
59
|
+
assert c == N
|
|
60
|
+
|
|
61
|
+
# Zéro neutre
|
|
62
|
+
s, c = P.half_add(Z)
|
|
63
|
+
assert s == P
|
|
64
|
+
assert c == Z
|
|
65
|
+
|
|
66
|
+
def full_add():
|
|
67
|
+
# sans retenue
|
|
68
|
+
s, c = P.full_add(N, Z)
|
|
69
|
+
assert s == Z and c == Z
|
|
70
|
+
|
|
71
|
+
# avec retenue qui propage
|
|
72
|
+
s, c = P.full_add(P, P)
|
|
73
|
+
assert s == Z and c == P
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from tritlib.trits import Trits
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
def test_trits_init():
|
|
5
|
+
assert Trits(0) == Trits.from_str("0")
|
|
6
|
+
assert int(Trits(5)) == 5
|
|
7
|
+
assert int(Trits(-5)) == -5
|
|
8
|
+
assert int(Trits(42)) == 42
|
|
9
|
+
|
|
10
|
+
def test_trits_init_from_str():
|
|
11
|
+
assert Trits(-1) == Trits.from_str("-")
|
|
12
|
+
assert Trits(1) == Trits.from_str("+")
|
|
13
|
+
assert Trits(0) == Trits.from_str("0")
|
|
14
|
+
|
|
15
|
+
assert Trits(2) == Trits.from_str("+-")
|
|
16
|
+
assert Trits(3) == Trits.from_str("+0")
|
|
17
|
+
assert Trits(5) == Trits.from_str("+--")
|
|
18
|
+
assert Trits(-2) == Trits.from_str("-+")
|
|
19
|
+
assert Trits(-3) == Trits.from_str("-0")
|
|
20
|
+
assert Trits(-5) == Trits.from_str("-++")
|
|
21
|
+
|
|
22
|
+
assert Trits(5) == Trits("+--")
|
|
23
|
+
|
|
24
|
+
def test_trits_to_str():
|
|
25
|
+
assert str(Trits(-1)) == "-"
|
|
26
|
+
assert str(Trits(1)) == "+"
|
|
27
|
+
assert str(Trits(0)) == "0"
|
|
28
|
+
|
|
29
|
+
assert str(Trits(2)) == "+-"
|
|
30
|
+
assert str(Trits(3)) == "+0"
|
|
31
|
+
assert str(Trits(5)) == "+--"
|
|
32
|
+
assert str(Trits(-2)) == "-+"
|
|
33
|
+
assert str(Trits(-3)) == "-0"
|
|
34
|
+
assert str(Trits(-5)) == "-++"
|
|
35
|
+
|
|
36
|
+
def tests_trits_len():
|
|
37
|
+
assert len(Trits(-1)) == 1
|
|
38
|
+
assert len(Trits(0)) == 1
|
|
39
|
+
assert len(Trits(1)) == 1
|
|
40
|
+
assert len(Trits(-2)) == 2
|
|
41
|
+
assert len(Trits(-5)) == 3
|
|
42
|
+
assert len(Trits(5)) == 3
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_trits_add():
|
|
46
|
+
for i in range(-10,10):
|
|
47
|
+
for j in range(-10, 10):
|
|
48
|
+
assert int(Trits(i) + Trits(j)) == i+j
|
|
49
|
+
|
|
50
|
+
def test_trits_sub():
|
|
51
|
+
for i in range(-10,10):
|
|
52
|
+
for j in range(-10, 10):
|
|
53
|
+
assert int(Trits(i) - Trits(j)) == i-j
|
|
54
|
+
|
|
55
|
+
def test_trits_mul():
|
|
56
|
+
for i in range(-100,10):
|
|
57
|
+
for j in range(-10, 100):
|
|
58
|
+
assert int(Trits(i) * Trits(j)) == i*j
|
|
59
|
+
|
|
60
|
+
def test_trits_lt():
|
|
61
|
+
assert Trits(-1) < Trits(0) < Trits(1)
|
|
62
|
+
for i in range(-10,10):
|
|
63
|
+
for j in range(i+1, i+11):
|
|
64
|
+
assert Trits(i) < Trits(j)
|
|
65
|
+
|
|
66
|
+
def test_trits_le():
|
|
67
|
+
assert Trits(42) <= Trits(42)
|
|
68
|
+
assert Trits(0) <= Trits(20)
|
|
69
|
+
assert Trits(-19) <= Trits(14)
|
|
70
|
+
|
|
71
|
+
def test_trits_gt():
|
|
72
|
+
assert Trits(1) > Trits(0) > Trits(-1)
|
|
73
|
+
for i in range(-10,10):
|
|
74
|
+
for j in range(i-1, i-11):
|
|
75
|
+
assert Trits(i) > Trits(j)
|
|
76
|
+
|
|
77
|
+
def test_trits_ge():
|
|
78
|
+
assert Trits(42) >= Trits(42)
|
|
79
|
+
assert Trits(20) >= Trits(0)
|
|
80
|
+
assert Trits(-13) >= Trits(-20)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_divmod():
|
|
84
|
+
q, r = divmod(Trits(13), Trits(4))
|
|
85
|
+
assert int(q) * 4 + int(r) == 13
|
|
86
|
+
q, r = divmod(Trits(7), Trits(4))
|
|
87
|
+
assert int(q) == 2 and int(r) == -1
|
|
88
|
+
q, r = divmod(Trits(-7), Trits(4))
|
|
89
|
+
assert int(q) * 4 + int(r) == -7
|
|
90
|
+
|
|
91
|
+
q, r = divmod(Trits(0), Trits(5))
|
|
92
|
+
assert int(q) == 0 and int(r) == 0
|
|
93
|
+
|
|
94
|
+
with pytest.raises(ZeroDivisionError):
|
|
95
|
+
divmod(Trits(4),Trits(0))
|
|
96
|
+
|
|
97
|
+
q, r = divmod(Trits(5), Trits(15))
|
|
98
|
+
assert int(q) == 0 and int(r) == 5
|
|
99
|
+
q, r = divmod(Trits(14), Trits(15))
|
|
100
|
+
assert int(q) == 1 and int(r) == -1
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from tritlib.trits import Trits
|
|
2
|
+
from tritlib.tryte import Tryte
|
|
3
|
+
from tritlib.trit import Z,N,P
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
def test_tryte():
|
|
7
|
+
a = Tryte(0, width = 6)
|
|
8
|
+
assert a.trits == [Z]*6
|
|
9
|
+
b = Tryte(2, width = 6)
|
|
10
|
+
assert b.trits == [N, P] + [Z] * 4
|
|
11
|
+
|
|
12
|
+
with pytest.raises(OverflowError):
|
|
13
|
+
Tryte(-400, width=6)
|
|
14
|
+
with pytest.raises(OverflowError):
|
|
15
|
+
Tryte(400, width=6)
|
|
16
|
+
|
|
17
|
+
assert Tryte(42, width=6) == Tryte("+---0",6)
|
|
18
|
+
|
|
19
|
+
def test_tryte_lt():
|
|
20
|
+
assert Tryte(42, width=6) < Tryte(45, width=6)
|
|
21
|
+
assert Tryte(-19, width=6) < Tryte(45, width=6)
|
|
22
|
+
assert Tryte(-42, width=6) < Tryte(-14, width=6)
|
|
23
|
+
assert Tryte(45, width=6) > Tryte(-42, width=6)
|
|
24
|
+
assert Tryte(45, width=6) > Tryte(42, width=6)
|
|
25
|
+
assert Tryte(-15, width=6) > Tryte(-42, width=6)
|
|
26
|
+
assert Tryte(-42, width=6) <= Tryte(-42, width=6)
|
|
27
|
+
assert Tryte(42, width=6) >= Tryte(42, width=6)
|
|
28
|
+
|
|
29
|
+
def test_tryte_width():
|
|
30
|
+
assert len(Tryte(42,6)) == 6
|
|
31
|
+
with pytest.raises(ValueError):
|
|
32
|
+
_ = Tryte(42,6) > Tryte(42,9)
|
|
33
|
+
with pytest.raises(ValueError):
|
|
34
|
+
_ = Tryte(42,6) == Tryte(42,9)
|
|
35
|
+
|
|
36
|
+
def test_tryte_add():
|
|
37
|
+
assert Tryte(42,6) == Tryte(31,6) + Tryte(11,6)
|
|
38
|
+
assert Tryte(300,6) + Tryte(300,6)
|
|
39
|
+
assert (Tryte(332,6),False) == Tryte(300,6).add_with_carry(Tryte(32,6))
|
|
40
|
+
assert (Tryte(-364,6),True) == Tryte(300,6).add_with_carry(Tryte(65,6))
|
|
41
|
+
for i in range(-10,10):
|
|
42
|
+
for j in range(-10,10):
|
|
43
|
+
assert Tryte(j,6) + Tryte(i,6) == Tryte(i+j, 6)
|
|
44
|
+
|
|
45
|
+
def test_tryte_neg():
|
|
46
|
+
assert Tryte(4,6) == -Tryte(-4,6)
|
|
47
|
+
assert Tryte(0,6) == -Tryte(0,6)
|
|
48
|
+
assert Tryte(-10,6) == -Tryte(10,6)
|
|
49
|
+
|
|
50
|
+
def test_tryte_sub():
|
|
51
|
+
assert Tryte(6,6) - Tryte(7,6) == Tryte(-1,6)
|
|
52
|
+
assert Tryte(20,6) - Tryte(4,6) == Tryte(16,6)
|
|
53
|
+
assert Tryte(10,6) - Tryte(24,6) == Tryte(-14,6)
|
|
54
|
+
for i in range(-10,10):
|
|
55
|
+
for j in range(-10,10):
|
|
56
|
+
assert Tryte(j,6) - Tryte(i,6) == Tryte(j-i, 6)
|
|
57
|
+
|
|
58
|
+
def test_tryte_mul():
|
|
59
|
+
for i in range(-10,10):
|
|
60
|
+
for j in range(-10,10):
|
|
61
|
+
assert Tryte(j,6) * Tryte(i,6) == Tryte(i*j, 6)
|
|
62
|
+
|
|
63
|
+
def test_tryte_to_str():
|
|
64
|
+
assert str(Tryte(-1,6)) == "00000-"
|
|
65
|
+
assert str(Tryte(1,6)) == "00000+"
|
|
66
|
+
assert str(Tryte(0,6)) == "000000"
|
|
67
|
+
assert str(Tryte(2,6)) == "0000+-"
|
|
68
|
+
assert str(Tryte(3,6)) == "0000+0"
|
|
69
|
+
assert str(Tryte(5,6)) == "000+--"
|
|
70
|
+
assert str(Tryte(-2,6)) == "0000-+"
|
|
71
|
+
assert str(Tryte(-3,6)) == "0000-0"
|
|
72
|
+
assert str(Tryte(-5,6)) == "000-++"
|
|
73
|
+
|
|
74
|
+
def test_tryte_to_trits():
|
|
75
|
+
for i in range(-15,15):
|
|
76
|
+
assert Tryte(i,6).to_trits() == Trits(i)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_le_trits():
|
|
80
|
+
assert Tryte(-1,6)._le_trits == [N]
|
|
81
|
+
assert Tryte(2,6)._le_trits == [P,N]
|
|
82
|
+
assert Tryte(5,6)._le_trits == [P,N,N]
|
|
83
|
+
assert Tryte(5,9)._le_trits == [P,N,N]
|
|
84
|
+
assert Tryte(-5,6)._le_trits == [N,P,P]
|
|
85
|
+
assert Tryte(-5,9)._le_trits == [N,P,P]
|
|
86
|
+
|
|
87
|
+
assert Tryte.from_le_trits([N,P,P],6) == Tryte(-5,6)
|
|
88
|
+
assert Tryte.from_le_trits([P,N],6) == Tryte(2,6)
|
|
89
|
+
assert Tryte.from_le_trits([P,N,N],6) == Tryte(5,6)
|
|
90
|
+
assert Tryte.from_le_trits([N,P,P],9) == Tryte(-5,9)
|
|
91
|
+
|
|
92
|
+
def test_resize():
|
|
93
|
+
for j in range(-10,10):
|
|
94
|
+
for i in range(7,10):
|
|
95
|
+
a = Tryte(j, 6)
|
|
96
|
+
assert a.resize(i) == Tryte(j,i)
|
|
97
|
+
|
|
98
|
+
le_trits = [N,P,P,Z,P]
|
|
99
|
+
assert Tryte.from_le_trits(le_trits,6).resize(3) == Tryte.from_le_trits(le_trits[:3],3)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_divmod():
|
|
103
|
+
q, r = divmod(Tryte(13,6), Tryte(4,6))
|
|
104
|
+
assert int(q) * 4 + int(r) == 13
|
|
105
|
+
q, r = divmod(Tryte(7,6), Tryte(4,6))
|
|
106
|
+
assert int(q) == 2 and int(r) == -1
|
|
107
|
+
q, r = divmod(Tryte(-7,6), Tryte(4,6))
|
|
108
|
+
assert int(q) * 4 + int(r) == -7
|
|
109
|
+
|
|
110
|
+
q, r = divmod(Tryte(0,6), Tryte(5,6))
|
|
111
|
+
assert int(q) == 0 and int(r) == 0
|
|
112
|
+
|
|
113
|
+
with pytest.raises(ZeroDivisionError):
|
|
114
|
+
divmod(Tryte(4,6),Tryte(0,6))
|
|
115
|
+
|
|
116
|
+
q, r = divmod(Tryte(5,6), Tryte(15,6))
|
|
117
|
+
assert int(q) == 0 and int(r) == 5
|
|
118
|
+
q, r = divmod(Tryte(14,6), Tryte(15,6))
|
|
119
|
+
assert int(q) == 1 and int(r) == -1
|