├── conftest.py ├── requirements-dev.txt ├── images ├── omega_demo.png ├── transfinite_demo.png └── transfinite_demo_2.png ├── transfinite ├── __init__.py ├── util.py ├── ordinal_factors.py ├── factorisation.py └── ordinal.py ├── .travis.yml ├── changelog.md ├── .pre-commit-config.yaml ├── LICENSE ├── setup.py ├── .gitignore ├── README.md ├── tests ├── test_factorisation.py └── test_ordinal.py └── notebooks └── ordinal_arithmetic_basics.ipynb /conftest.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pytest>=5.3.2 2 | pre-commit 3 | -------------------------------------------------------------------------------- /images/omega_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajcr/transfinite/HEAD/images/omega_demo.png -------------------------------------------------------------------------------- /images/transfinite_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajcr/transfinite/HEAD/images/transfinite_demo.png -------------------------------------------------------------------------------- /images/transfinite_demo_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajcr/transfinite/HEAD/images/transfinite_demo_2.png -------------------------------------------------------------------------------- /transfinite/__init__.py: -------------------------------------------------------------------------------- 1 | from .ordinal import Ordinal 2 | from .factorisation import factors 3 | 4 | w = Ordinal() 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "3.7" 5 | install: 6 | - pip install -r requirements-dev.txt 7 | - pip install -e . 8 | - pre-commit install 9 | script: 10 | - pre-commit run -a 11 | - pytest 12 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.5.2] - 2020-03-13 11 | ### Fixed 12 | - Fixed bug in `__rpow__` when evaluating `n**(w**k*c)` 13 | - Improvements to code readability 14 | 15 | ## [0.5.1] - 2020-03-07 16 | ### Fixed 17 | - Fixed issue when raising finite power to transfinite power (https://github.com/ajcr/transfinite/issues/6) 18 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.3.0 4 | hooks: 5 | - id: end-of-file-fixer 6 | - id: trailing-whitespace 7 | - repo: https://github.com/pre-commit/mirrors-pylint 8 | rev: 'v2.3.1' 9 | hooks: 10 | - id: pylint 11 | args: [ 12 | --disable, invalid-name, 13 | --disable, unidiomatic-typecheck, 14 | --disable, bad-continuation, 15 | --disable, missing-docstring, 16 | --disable, import-error, 17 | --disable, duplicate-code, 18 | --disable, consider-using-ternary, 19 | --disable, line-too-long, 20 | ] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Alex Riley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | LONG_DESCRIPTION = """Transfinite ordinal arithmetic up to epsilon-zero. 4 | 5 | Implements ordinal addition, multiplication and factorization, with ordinals 6 | automatically rendered as LaTeX inside Jupyter notebooks and consoles 7 | for easy readability. 8 | """ 9 | 10 | setup( 11 | name="transfinite", 12 | version="0.5.2", 13 | description="Transfinite ordinals for Python", 14 | long_description=LONG_DESCRIPTION, 15 | author="Alex Riley", 16 | license="MIT", 17 | packages=["transfinite"], 18 | zip_safe=False, 19 | classifiers=[ 20 | "Topic :: Software Development :: Libraries :: Python Modules", 21 | "License :: OSI Approved :: MIT License", 22 | "Programming Language :: Python :: 3.6", 23 | "Programming Language :: Python :: 3.7", 24 | "Programming Language :: Python :: 3.8", 25 | "Programming Language :: Python :: 3.9", 26 | ], 27 | keywords="set-theory ordinals arithmetic factorization LaTeX", 28 | python_requires=">=3.6.0", 29 | project_urls={ 30 | "Source": "https://github.com/ajcr/transfinite/", 31 | "Tracker": "https://github.com/ajcr/transfinite/issues", 32 | }, 33 | ) 34 | -------------------------------------------------------------------------------- /transfinite/util.py: -------------------------------------------------------------------------------- 1 | from itertools import groupby 2 | from operator import itemgetter 3 | 4 | def is_finite_ordinal(n): 5 | """ 6 | Return True if n is a finite ordinal (non-negative int). 7 | 8 | """ 9 | return isinstance(n, int) and n >= 0 10 | 11 | 12 | def exp_by_squaring(x, n): 13 | """ 14 | Compute x**n using exponentiation by squaring. 15 | 16 | """ 17 | if n == 0: 18 | return 1 19 | if n == 1: 20 | return x 21 | if n % 2 == 0: 22 | return exp_by_squaring(x * x, n // 2) 23 | return exp_by_squaring(x * x, (n - 1) // 2) * x 24 | 25 | 26 | def as_latex(ordinal): 27 | """ 28 | Convert the Ordinal object to a LaTeX string. 29 | 30 | """ 31 | if isinstance(ordinal, int): 32 | return str(ordinal) 33 | term = r"\omega" 34 | if ordinal.exponent != 1: 35 | term += f"^{{{as_latex(ordinal.exponent)}}}" 36 | if ordinal.copies != 1: 37 | term += rf"\cdot{as_latex(ordinal.copies)}" 38 | if ordinal.addend != 0: 39 | term += f"+{as_latex(ordinal.addend)}" 40 | return term 41 | 42 | 43 | def multiply_factors(factors): 44 | """ 45 | Return the product of the factors. 46 | 47 | """ 48 | prod = 1 49 | for ordinal, exponent in factors: 50 | prod *= ordinal ** exponent 51 | return prod 52 | 53 | 54 | def group_factors(factors): 55 | """ 56 | Return the factors with the exponents of consecutive equal 57 | ordinals added together. 58 | 59 | """ 60 | grouped_factors = groupby(factors, key=itemgetter(0)) 61 | return [(ordinal, sum(exp for _, exp in fs)) for ordinal, fs in grouped_factors] 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # transfinite 2 | 3 | [![PyPI version](https://badge.fury.io/py/transfinite.svg)](https://badge.fury.io/py/transfinite) 4 | 5 | Transfinite [ordinal arithmetic](https://en.wikipedia.org/wiki/Ordinal_arithmetic) and factorisation up to the first [epsilon number](https://en.wikipedia.org/wiki/Epsilon_numbers_(mathematics)). 6 | 7 | ## Installation 8 | 9 | Works with Python 3. Can be installed via pip using: 10 | 11 | ``` 12 | pip install transfinite 13 | ``` 14 | 15 | ## Usage 16 | 17 | For a basic introduction to ordinal arithmetic, look at Wikipedia or see the notebook [here](https://github.com/ajcr/transfinite/blob/master/notebooks/ordinal_arithmetic_basics.ipynb). 18 | 19 | Here's a quick demonstration of the library in Jupyter's qtconsole (note that the variable `w` is the first transfinite number). First, some ordinal arithmetic: 20 | 21 | ![alt tag](https://github.com/ajcr/transfinite/blob/master/images/transfinite_demo.png) 22 | 23 | The Ordinal class implements several methods which can be used to check properties of the ordinal: 24 | 25 | - `Ordinal.is_limit()`, returns True if the ordinal is a [limit ordinal](https://en.wikipedia.org/wiki/Limit_ordinal). 26 | - `Ordinal.is_successor()`, returns True if the ordinal is a [successor ordinal](https://en.wikipedia.org/wiki/Successor_ordinal). 27 | - `Ordinal.is_gamma()`, returns True if the ordinal is [additively indecomposable](https://en.wikipedia.org/wiki/Additively_indecomposable_ordinal). 28 | - `Ordinal.is_delta()`, returns True if the ordinal is [multiplicatively indecomposable](https://en.wikipedia.org/wiki/Additively_indecomposable_ordinal#Multiplicatively_indecomposable). 29 | - `Ordinal.is_prime()`, returns True if the ordinal is prime. 30 | 31 | [Ordinal factorisation](https://en.wikipedia.org/wiki/Ordinal_arithmetic#Factorization_into_primes) into prime ordinals is also implemented. Any composite ordinal `a` can be written as a product of two or more prime ordinals less than `a`: 32 | 33 | ![alt tag](https://github.com/ajcr/transfinite/blob/master/images/transfinite_demo_2.png) 34 | 35 | Note that finite ordinals are not factorised using this method. 36 | -------------------------------------------------------------------------------- /transfinite/ordinal_factors.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | 3 | from transfinite.util import ( 4 | as_latex, 5 | group_factors, 6 | is_finite_ordinal, 7 | multiply_factors, 8 | ) 9 | 10 | 11 | class OrdinalFactors(Sequence): 12 | """ 13 | Factors of an ordinal. 14 | 15 | Since ordinal multiplication is non-commutative, the order 16 | that the factors are multiplied in matters, hence this is 17 | a Sequence object. 18 | 19 | The object must be initialised from a initial sequence of 20 | (ordinal, exponent) pairs. 21 | 22 | The object has a _repr_latex_ method so that the sequence 23 | of ordinals and exponents can be rendered in an easy to 24 | read way in Jupyter. 25 | 26 | """ 27 | def __init__(self, factors): 28 | self.factors = group_factors(factors) 29 | 30 | def __iter__(self): 31 | return iter(self.factors) 32 | 33 | def __contains__(self, other): 34 | return other in (ordinal for ordinal, _ in self.factors) 35 | 36 | def __len__(self): 37 | return len(self.factors) 38 | 39 | def __getitem__(self, item): 40 | return self.factors[item] 41 | 42 | def product(self): 43 | return multiply_factors(self.factors) 44 | 45 | def __str__(self): 46 | return str(self.factors) 47 | 48 | def __repr__(self): 49 | return f"OrdinalFactors({str(self)})" 50 | 51 | def _repr_latex_(self): 52 | latex = self.as_latex() 53 | return f"${latex}$" 54 | 55 | def as_latex(self): 56 | """ 57 | Returns a LaTeX string of the factors. 58 | 59 | Put parentheses around a factor if its exponent is 60 | greater than 1, or if its addend is greater than 0: 61 | 62 | (w, 1) -> w 63 | (w, 2) -> (w)^2 64 | (w + 1, 1) -> (w + 1) 65 | (w + 1, 2) -> (w + 1)^2 66 | (w**2 + 1, 1) -> (w^2 + 1) 67 | (w**2 + 1, 3) -> (w^2 + 1)^3 68 | 69 | Note that since factors are prime, there are some cases 70 | that do not need to be covered here (e.g. w*n). 71 | """ 72 | fs_latex = [] 73 | 74 | for ordinal, exponent in self.factors: 75 | 76 | if is_finite_ordinal(ordinal): 77 | fs_latex.append(str(ordinal)) 78 | continue 79 | 80 | latex_ordinal = as_latex(ordinal) 81 | 82 | if ordinal.addend > 0 or exponent > 1: 83 | f = rf"\left({latex_ordinal}\right)" 84 | else: 85 | f = latex_ordinal 86 | 87 | if exponent > 1: 88 | f += f"^{{{exponent}}}" 89 | 90 | fs_latex.append(f) 91 | 92 | return r"\cdot".join(fs_latex) 93 | -------------------------------------------------------------------------------- /transfinite/factorisation.py: -------------------------------------------------------------------------------- 1 | from transfinite.ordinal import Ordinal 2 | from transfinite.ordinal_factors import OrdinalFactors 3 | from transfinite.util import is_finite_ordinal 4 | 5 | 6 | def subtract(a, b): 7 | """ 8 | Given ordinals a > b, return the ordinal c such that b + c == a 9 | 10 | """ 11 | if a <= b: 12 | raise ValueError("First argument must be greater than second argument") 13 | 14 | if is_finite_ordinal(a): 15 | return a - b 16 | 17 | if is_finite_ordinal(b) or a.exponent > b.exponent: 18 | return a 19 | 20 | if a.exponent == b.exponent and a.copies == b.copies: 21 | return subtract(a.addend, b.addend) 22 | 23 | # Here we know that a.copies > b.copies 24 | return Ordinal(a.exponent, a.copies - b.copies, a.addend) 25 | 26 | 27 | def divide_terms_by_ordinal(terms, ordinal): 28 | """ 29 | Divide each term in the list by the specified ordinal. 30 | 31 | """ 32 | return [Ordinal(subtract(t.exponent, ordinal.exponent), t.copies) for t in terms] 33 | 34 | 35 | def ordinal_terms(ordinal): 36 | """ 37 | Return a list containing all terms of the ordinal. 38 | 39 | For example: 40 | 41 | w**w**w + w**3 + w*7 + 9 42 | 43 | becomes the list 44 | 45 | [w**w**w, w**3, w*7, 9] 46 | 47 | """ 48 | terms = [] 49 | 50 | while not is_finite_ordinal(ordinal): 51 | term = Ordinal(exponent=ordinal.exponent, copies=ordinal.copies) 52 | terms.append(term) 53 | ordinal = ordinal.addend 54 | 55 | if ordinal: 56 | terms.append(ordinal) 57 | 58 | return terms 59 | 60 | 61 | def factorise_term(term): 62 | """ 63 | Write a single transfinite ordinal term (addend=0) as a product of primes. 64 | 65 | For example: 66 | 67 | w**(w**w*7 + w*3 + 2) 68 | 69 | becomes 70 | 71 | [(w**w**w, 7), (w**w, 3), (w, 2)] 72 | 73 | """ 74 | factors_ = [] 75 | 76 | for t in ordinal_terms(term.exponent): 77 | if is_finite_ordinal(t): 78 | factors_.append((Ordinal(), t)) 79 | else: 80 | factors_.append((Ordinal(exponent=Ordinal(t.exponent)), t.copies)) 81 | 82 | if term.copies > 1: 83 | factors_.append((term.copies, 1)) 84 | 85 | return factors_ 86 | 87 | 88 | def factorise_term_successor(ordinal_term): 89 | """ 90 | Given a term (w**e * c) return the factors of its successor. 91 | 92 | Note that since (w**e + 1) is prime, the method returns this 93 | ordinal as a factor, as well as the copies if not 1: 94 | 95 | term -> successor -> factors 96 | w -> w + 1 -> [(w + 1, 1)] 97 | w*7 -> w*7 + 1 -> [(w + 1, 1), (7, 1)] 98 | 99 | """ 100 | factors_ = [(Ordinal(exponent=ordinal_term.exponent, addend=1), 1)] 101 | 102 | if ordinal_term.copies > 1: 103 | factors_.append((ordinal_term.copies, 1)) 104 | 105 | return factors_ 106 | 107 | 108 | def factors(ordinal): 109 | """ 110 | Return the prime factors of the ordinal. 111 | 112 | Note: finite integers are not broken into prime factors. 113 | """ 114 | terms = ordinal_terms(ordinal) 115 | 116 | # If the ordinal is a limit ordinal, it has the terms: 117 | # 118 | # terms = A + B + ... + X (A > B > ... > X > 0) 119 | # 120 | # We want to factor out X if X > 1, leaving: 121 | # 122 | # terms = A' + B' + ... + 1 (X*A' == A, X*B' == B, ...) 123 | # 124 | # The factors of X are the first factors of the ordinal. 125 | # Note the finite term 1 is not explicitly included in the list. 126 | 127 | if len(terms) == 1 or not is_finite_ordinal(terms[-1]): 128 | least_term = terms.pop() 129 | terms = divide_terms_by_ordinal(terms, least_term) 130 | factors_ = factorise_term(least_term) 131 | 132 | elif terms[-1] > 1: 133 | factors_ = [(terms.pop(), 1)] 134 | 135 | else: 136 | terms.pop() 137 | factors_ = [] 138 | 139 | # At this stage, terms is a list of infinite ordinals: 140 | # 141 | # terms = A' + B' + ... + C' (A' > B' > C' >= w) 142 | # 143 | # This represents the successor ordinal: 144 | # 145 | # A' + B' + ... + C' + 1 146 | # 147 | # The loop below repeatedly removes the least term C' then adds 148 | # factors of (C' + 1) to the list of factors, then divides C' 149 | # into the remaining terms (A', B', ...). 150 | 151 | while terms: 152 | least_term = terms.pop() 153 | factors_ += factorise_term_successor(least_term) 154 | terms = divide_terms_by_ordinal(terms, least_term) 155 | 156 | return OrdinalFactors(factors_) 157 | -------------------------------------------------------------------------------- /tests/test_factorisation.py: -------------------------------------------------------------------------------- 1 | from itertools import groupby 2 | 3 | import pytest 4 | 5 | from transfinite import w 6 | from transfinite.util import is_finite_ordinal, multiply_factors 7 | from transfinite.factorisation import ( 8 | factors, 9 | subtract, 10 | factorise_term, 11 | factorise_term_successor, 12 | ) 13 | 14 | 15 | def has_equal_consecutive_elements(seq): 16 | """ 17 | Return True if the sequence has one or more pairs of consecutive equal elements. 18 | 19 | """ 20 | return any(first == second for first, second in zip(seq, seq[1:])) 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "a,b", 25 | [ 26 | (7, 3), 27 | (w, 3), 28 | (w + 1, 3), 29 | (w**2, 1), 30 | (w**2*5, 3), 31 | (w + 1, w), 32 | (w**3, w**2), 33 | (w**3, w), 34 | (w**3, w + 1), 35 | (w**3 + w**2, w), 36 | (w**3 + w**2 + 1, w), 37 | (w**3 + 1, w**3), 38 | (w**3 + w, w**3 + 5), 39 | (w**3 + w + 1, w**3 + 5), 40 | (w**3*4, w**3*2), 41 | (w**3*4 + w**2, w**3*2), 42 | (w**3*4 + w**2, w**3*2 + w + 1), 43 | ], 44 | ) 45 | def test_subtract(a, b): 46 | assert b + subtract(a, b) == a 47 | 48 | 49 | @pytest.mark.parametrize( 50 | "a", 51 | [ 52 | w, 53 | w*7, 54 | w**2, 55 | w**(w + 1), 56 | w**(w + 1)*9, 57 | w**w**2, 58 | w**(w**2 + w*3), 59 | w**(w**w + w*3 + 6), 60 | w**(w**w + w*3 + 6)*19, 61 | w**(w**w**2*2 + w*3 + 6)*19, 62 | w**(w**(w + 1) + w + 6)*19, 63 | w**(w*2), 64 | w**(w*3 + 1), 65 | w**(w**w*8 + w*3 + 1), 66 | ], 67 | ) 68 | def test_factorise_term(a): 69 | fs = factorise_term(a) 70 | assert multiply_factors(fs) == a 71 | assert all(is_finite_ordinal(f) and f > 1 or f.is_prime() for f, _ in fs) 72 | 73 | 74 | @pytest.mark.parametrize( 75 | "a", [w, w*3, w**5, w**3*7, w**(w*3), w**(w*3)*2] 76 | ) 77 | def test_factorise_term_successor(a): 78 | fs = factorise_term_successor(a) 79 | assert multiply_factors(fs) == a + 1 80 | assert all(is_finite_ordinal(f) and f > 1 or f.is_prime() for f, _ in fs) 81 | 82 | 83 | @pytest.mark.parametrize( 84 | "a", 85 | [ 86 | w, 87 | w + 1, 88 | w + 2, 89 | w*5 + 16, 90 | w**2, 91 | w**2*17 + 3, 92 | w**4*8, 93 | w**w, 94 | w**w + 1, 95 | w**w + w, 96 | w**(w + 1), 97 | w**(w + 1)*5, 98 | w**(w + 1) + 1, 99 | w**(w + 1)*8 + 1, 100 | w**w**2 + w**w, 101 | w**w**2 + w**w + w**3, 102 | w**w**2 + w**w + w**3*7, 103 | w**w**2 + w**w + 1, 104 | w**w**2 + w**w + 21, 105 | w**w**2 + w**w*7 + 21, 106 | w**w**2*13 + w**w*7 + 21, 107 | w**w**w + w**w**2 + w**(w*7) + 21, 108 | w**w**w*4 + w**w**2 + w**(w*7) + 21, 109 | w**w**(w*4) + w**(w + 1) + w**w + 13, 110 | w**w**(w*4) + w**(w**2 + 1)*7 + w**w + 13, 111 | w**w**(w*4 + 2)*11 + w**(w**2 + w + 1)*7 + w**w + 13, 112 | (w + 1)**3, 113 | (w**2 + 1)**5, 114 | w**(w**(w**7*6 + w + 42)*1729 + w**9 + 88)*3 + w**w**w*5, 115 | (w**(w**(w**7*6 + w + 42)*1729 + w**9 + 88)*3 + w**w**w*5)**2, 116 | (w**(w**(w**7*6 + w + 42)*2) + w**w**w*5 + 33)**2, 117 | (w**(w**(w**7*6)*2) + w)**2, 118 | ], 119 | ) 120 | def test_factors(a): 121 | fs = factors(a) 122 | 123 | ordinals = [a for a, _ in fs] 124 | 125 | # Check the factorisation is correct 126 | assert all(is_finite_ordinal(a) and a > 1 or a.is_prime() for a in ordinals), "Not all factors are prime" 127 | assert fs.product() == a, "Incorrect factorisation" 128 | 129 | # Check the grouping and order of factors is correct 130 | grouper = groupby(ordinals, key=lambda x: is_finite_ordinal(x) or x.is_successor()) 131 | groups = [(is_successor, list(ords)) for is_successor, ords in grouper] 132 | 133 | assert len(groups) <= 2, "Factors not ordered by limit/successor" 134 | 135 | # If there are only successor ordinals as factors there is no need to check further 136 | if len(groups) == 1 and groups[0][0]: 137 | assert not has_equal_consecutive_elements(groups[0][1]), "Successor factors has consecutive equal ordinals" 138 | return 139 | 140 | # Otherwise, there are both limit/successor factors: limit must come first and be sorted 141 | assert (groups[0][0] is False), "Limit ordinals do not occur before successor ordinals" 142 | assert groups[0][1] == sorted(groups[0][1], reverse=True), "Successor ordinals not in descending order" 143 | assert not has_equal_consecutive_elements(groups[0][1]), "Limit factors contain equal consecutive ordinals" 144 | -------------------------------------------------------------------------------- /transfinite/ordinal.py: -------------------------------------------------------------------------------- 1 | from functools import total_ordering 2 | 3 | from transfinite.util import is_finite_ordinal, as_latex, exp_by_squaring 4 | 5 | 6 | class OrdinalConstructionError(Exception): 7 | pass 8 | 9 | 10 | @total_ordering 11 | class Ordinal: 12 | """ 13 | An infinite ordinal less than epsilon_0. 14 | 15 | This class describes an ordinal in Cantor Normal Form using 16 | the following attributes: 17 | 18 | (exponent) 19 | ^ 20 | w . (copies) + (addend) 21 | 22 | w denotes the first infinite ordinal, (copies) is an integer, 23 | (exponent) and (addend) can be either an integer or an instance of 24 | this Ordinal class. 25 | 26 | """ 27 | 28 | def __init__(self, exponent=1, copies=1, addend=0): 29 | 30 | if exponent == 0 or not is_ordinal(exponent): 31 | raise OrdinalConstructionError("exponent must be an Ordinal or an integer greater than 0") 32 | 33 | if copies == 0 or not is_finite_ordinal(copies): 34 | raise OrdinalConstructionError("copies must be an integer greater than 0") 35 | 36 | if not is_ordinal(addend): 37 | raise OrdinalConstructionError("addend must be an Ordinal or a non-negative integer") 38 | 39 | if isinstance(addend, Ordinal) and addend.exponent >= exponent: 40 | raise OrdinalConstructionError("addend.exponent must be less than self.exponent") 41 | 42 | self.exponent = exponent 43 | self.copies = copies 44 | self.addend = addend 45 | 46 | def is_limit(self): 47 | """ 48 | Return true if ordinal is a limit ordinal. 49 | 50 | """ 51 | if is_finite_ordinal(self.addend): 52 | return self.addend == 0 53 | return self.addend.is_limit() 54 | 55 | def is_successor(self): 56 | """ 57 | Return true if ordinal is a successor ordinal. 58 | 59 | """ 60 | return not self.is_limit() 61 | 62 | def is_gamma(self): 63 | """ 64 | Return true if ordinal is additively indecomposable. 65 | 66 | These are ordinals of the form w**a for a > 0. 67 | """ 68 | return self.copies == 1 and self.addend == 0 69 | 70 | def is_delta(self): 71 | """ 72 | Return true if ordinal is multiplicatively indecomposable. 73 | 74 | These are ordinals of the form w**w**a for an ordinal a 75 | which is either 0 or such that w**a is a gamma ordinal. 76 | """ 77 | return self.is_gamma() and ( 78 | self.exponent == 1 79 | or not is_finite_ordinal(self.exponent) 80 | and self.exponent.is_gamma() 81 | ) 82 | 83 | def is_prime(self): 84 | """ 85 | Return true if ordinal cannot be factored into smaller ordinals, 86 | both greater than 1: 87 | 88 | * ordinal is the successor of a gamma ordinal, or 89 | * ordinal is a delta ordinal 90 | 91 | """ 92 | return self.copies == 1 and self.addend == 1 or self.is_delta() 93 | 94 | def _repr_latex_(self): 95 | return f"${as_latex(self)}$" 96 | 97 | def __repr__(self): 98 | return str(self) 99 | 100 | def __str__(self): 101 | term = "w" 102 | 103 | # Only use parentheses for exponent if finite and greater than 1, 104 | # or its addend is nonzero or its copies is greater than 1. 105 | 106 | if self.exponent == 1: 107 | pass 108 | 109 | elif ( 110 | is_finite_ordinal(self.exponent) 111 | or self.exponent.copies == 1 112 | and self.exponent.addend == 0 113 | ): 114 | term += f"**{self.exponent}" 115 | 116 | else: 117 | term += f"**({self.exponent})" 118 | 119 | if self.copies != 1: 120 | term += f"*{self.copies}" 121 | 122 | if self.addend != 0: 123 | term += f" + {self.addend}" 124 | 125 | return term 126 | 127 | def __hash__(self): 128 | return hash(self.as_tuple()) 129 | 130 | def __eq__(self, other): 131 | if isinstance(other, Ordinal): 132 | return self.as_tuple() == other.as_tuple() 133 | return False 134 | 135 | def __lt__(self, other): 136 | if isinstance(other, Ordinal): 137 | return self.as_tuple() < other.as_tuple() 138 | if is_finite_ordinal(other): 139 | return False 140 | return NotImplemented 141 | 142 | def __add__(self, other): 143 | 144 | if not is_ordinal(other): 145 | return NotImplemented 146 | 147 | # (w**a*b + c) + x == w**a*b + (c + x) 148 | if is_finite_ordinal(other) or self.exponent > other.exponent: 149 | return Ordinal(self.exponent, self.copies, self.addend + other) 150 | 151 | # (w**a*b + c) + (w**a*d + e) == w**a*(b + d) + e 152 | if self.exponent == other.exponent: 153 | return Ordinal(self.exponent, self.copies + other.copies, other.addend) 154 | 155 | # other is strictly greater than self 156 | return other 157 | 158 | def __radd__(self, other): 159 | 160 | if not is_finite_ordinal(other): 161 | return NotImplemented 162 | 163 | # n + a == a 164 | return self 165 | 166 | def __mul__(self, other): 167 | 168 | if not is_ordinal(other): 169 | return NotImplemented 170 | 171 | if other == 0: 172 | return 0 173 | 174 | # (w**a*b + c) * n == w**a * (b*n) + c 175 | if is_finite_ordinal(other): 176 | return Ordinal(self.exponent, self.copies * other, self.addend) 177 | 178 | # (w**a*b + c) * (w**x*y + z) == w**(a + x)*y + (c*z + (w**a*b + c)*z) 179 | return Ordinal( 180 | self.exponent + other.exponent, 181 | other.copies, 182 | self.addend * other.addend + self * other.addend, 183 | ) 184 | 185 | def __rmul__(self, other): 186 | 187 | if not is_finite_ordinal(other): 188 | return NotImplemented 189 | 190 | if other == 0: 191 | return 0 192 | 193 | # n * (w**a*b + c) == w**a*b + (n*c) 194 | return Ordinal(self.exponent, self.copies, other * self.addend) 195 | 196 | def __pow__(self, other): 197 | 198 | if not is_ordinal(other): 199 | return NotImplemented 200 | 201 | # Finite powers are computed using repeated multiplication 202 | if is_finite_ordinal(other): 203 | return exp_by_squaring(self, other) 204 | 205 | # (w**a*b + c) ** (w**x*y + z) == (w**(a * w**x * y)) * (w**a*b + c)**z 206 | return Ordinal(self.exponent * Ordinal(other.exponent, other.copies)) * self**other.addend 207 | 208 | def __rpow__(self, other): 209 | 210 | if not is_finite_ordinal(other): 211 | return NotImplemented 212 | 213 | # 0**a == 0 and 1**a == 1 214 | if other in (0, 1): 215 | return other 216 | 217 | # n**(w*c + a) == (w**c) * (n**a) 218 | if self.exponent == 1: 219 | return Ordinal(self.copies, other ** self.addend) 220 | 221 | # n**(w**m*c + a) == w**(w**(m-1) * c) * n**a 222 | if is_finite_ordinal(self.exponent): 223 | return Ordinal(Ordinal(self.exponent - 1, self.copies)) * other**self.addend 224 | 225 | # n**(w**a*c + b) == w**(w**a*c) * n**b 226 | return Ordinal(Ordinal(self.exponent, self.copies)) * other**self.addend 227 | 228 | def as_tuple(self): 229 | """ 230 | Return the ordinal as a tuple of (exponent, copies, addend). 231 | 232 | """ 233 | return self.exponent, self.copies, self.addend 234 | 235 | 236 | def is_ordinal(a): 237 | """ 238 | Return True if a is a finite or infinite ordinal. 239 | 240 | """ 241 | return is_finite_ordinal(a) or isinstance(a, Ordinal) 242 | -------------------------------------------------------------------------------- /notebooks/ordinal_arithmetic_basics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Ordinal arithmetic: the basics" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This a brief (and non-rigorous, non-set-theoretic) overview of ordinals and their arithmetic. All the code examples here use the [`transfinite`](https://github.com/ajcr/transfinite) module.\n", 15 | "\n", 16 | "## What are ordinals?\n", 17 | "\n", 18 | "*Finite* ordinals can be considered to be very much like positive integers. Ordinals can be used to count, the ordinal $1$ denotes one 'stick'. The ordinal $2$ denotes two successive sticks, and so on:\n", 19 | "\n", 20 | "$$ 1 = | $$\n", 21 | "$$ 2 = |\\ | $$\n", 22 | "$$ 3 = |\\ |\\ | $$\n", 23 | "$$ \\vdots $$\n", 24 | "\n", 25 | "What if we were given an *infinite* collection of these sticks, and we arranged them one after the other in a sequence than extended forever?\n", 26 | "\n", 27 | "This is a new ordinal, denoted using the Greek letter $\\omega$ (*omega*):\n", 28 | "\n", 29 | "$$ \\omega = |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots $$\n", 30 | "\n", 31 | "We can introduce this new infinite ordinal into our Python session:" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 2, 37 | "metadata": { 38 | "collapsed": true 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "from transfinite import w" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 3, 48 | "metadata": { 49 | "collapsed": false 50 | }, 51 | "outputs": [ 52 | { 53 | "data": { 54 | "text/latex": [ 55 | "$\\omega$" 56 | ], 57 | "text/plain": [ 58 | "\\omega" 59 | ] 60 | }, 61 | "execution_count": 3, 62 | "metadata": {}, 63 | "output_type": "execute_result" 64 | } 65 | ], 66 | "source": [ 67 | "w" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "As you may have already guessed, $\\omega$ is greater than any finite ordinal we can ever describe:" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 4, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [ 84 | { 85 | "data": { 86 | "text/plain": [ 87 | "True" 88 | ] 89 | }, 90 | "execution_count": 4, 91 | "metadata": {}, 92 | "output_type": "execute_result" 93 | } 94 | ], 95 | "source": [ 96 | "99991234689995343742 < w" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "## Ordinal addition" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "Once we've transcended the finite ordinals and hit $\\omega$, can we go further?\n", 111 | "\n", 112 | "Yes! We just add more sticks to to the end of our \"never-ending\" sequence of sticks:\n", 113 | "\n", 114 | "$$ \\omega + \\color{red}{1} = |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots\\ \\color{red}{|} $$\n", 115 | "$$ \\omega + \\color{red}{2} = |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots\\ \\color{red}{|\\ |} $$\n", 116 | "$$ \\omega + \\color{red}{3} = |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots\\ \\color{red}{|\\ |\\ |} $$\n", 117 | "$$ \\vdots $$\n", 118 | "\n", 119 | "Unlike $\\omega$, these ordinals have a \"last\" stick. We've created new ordinals through ordinal addition." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 5, 125 | "metadata": { 126 | "collapsed": false 127 | }, 128 | "outputs": [ 129 | { 130 | "data": { 131 | "text/latex": [ 132 | "$\\omega + 3$" 133 | ], 134 | "text/plain": [ 135 | "\\omega + 3" 136 | ] 137 | }, 138 | "execution_count": 5, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "w + 3" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "Each of these ordinals is greater than the last:" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 6, 157 | "metadata": { 158 | "collapsed": false 159 | }, 160 | "outputs": [ 161 | { 162 | "data": { 163 | "text/plain": [ 164 | "True" 165 | ] 166 | }, 167 | "execution_count": 6, 168 | "metadata": {}, 169 | "output_type": "execute_result" 170 | } 171 | ], 172 | "source": [ 173 | "w+1 < w+2 < w+3" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "metadata": {}, 179 | "source": [ 180 | "So far this looks very similar to arithmetic on integers: adding integers together creates a new larger integer.\n", 181 | "\n", 182 | "Ordinal arithmetic however, is not quite the same. It is *non-commutative*: the order we add ordinals together matters.\n", 183 | "\n", 184 | "For instance, if we start with the ordinal $1$ and add $\\omega$ what do we get?\n", 185 | "\n", 186 | "$$ \\color{red}{1} + \\omega = \\color{red}{|\\ } |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots $$\n", 187 | "\n", 188 | "This ordering of sticks looks exactly like the ordering we had for $\\omega$: it is a never-ending sequence of sticks with no \"last\" stick. We have the same ordinal, not a larger one." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 7, 194 | "metadata": { 195 | "collapsed": false 196 | }, 197 | "outputs": [ 198 | { 199 | "data": { 200 | "text/latex": [ 201 | "$\\omega$" 202 | ], 203 | "text/plain": [ 204 | "\\omega" 205 | ] 206 | }, 207 | "execution_count": 7, 208 | "metadata": {}, 209 | "output_type": "execute_result" 210 | } 211 | ], 212 | "source": [ 213 | "1 + w" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "(This demonstrates that a collection of items arranged in the order $\\omega + 1$ dose **not** contain more items than collection arranged in the order $\\omega$. These two ordinals have the same *cardinality*, but that's a separate topic.)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "The largest ordinal we've seen so far is $\\omega + 3$. We can continue to add sticks onto the end to create larger ordinals. Where do end up?\n", 228 | "\n", 229 | "$$ \\omega + \\color{red}{\\omega} = |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots\\ \\color{red}{|\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\ |\\dots}$$\n", 230 | "\n", 231 | "A larger ordinal!" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 8, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [ 241 | { 242 | "data": { 243 | "text/latex": [ 244 | "$\\omega\\cdot2$" 245 | ], 246 | "text/plain": [ 247 | "\\omega\\cdot2" 248 | ] 249 | }, 250 | "execution_count": 8, 251 | "metadata": {}, 252 | "output_type": "execute_result" 253 | } 254 | ], 255 | "source": [ 256 | "w + w" 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "...and we can keep going:" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 9, 269 | "metadata": { 270 | "collapsed": false 271 | }, 272 | "outputs": [ 273 | { 274 | "data": { 275 | "text/latex": [ 276 | "$\\omega\\cdot2 + 1$" 277 | ], 278 | "text/plain": [ 279 | "\\omega\\cdot2 + 1" 280 | ] 281 | }, 282 | "execution_count": 9, 283 | "metadata": {}, 284 | "output_type": "execute_result" 285 | } 286 | ], 287 | "source": [ 288 | "w + w + 1" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "## Ordinal multiplication\n", 296 | "\n", 297 | "You may have noticed that $\\omega + \\omega$ was also displayed as $\\omega \\cdot 2$ above. This is ordinal multiplation.\n", 298 | "\n", 299 | "You can think of $\\omega \\cdot n$ as meaning \"make $n$ copies of $\\omega$\". The more copies of $\\omega$ you place after one-another, the larger the ordinal:" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": 10, 305 | "metadata": { 306 | "collapsed": false 307 | }, 308 | "outputs": [ 309 | { 310 | "data": { 311 | "text/plain": [ 312 | "True" 313 | ] 314 | }, 315 | "execution_count": 10, 316 | "metadata": {}, 317 | "output_type": "execute_result" 318 | } 319 | ], 320 | "source": [ 321 | "w*3 < w*4" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "Like addition, multiplication of ordinals is not commutative in general. For example $ 2 \\cdot \\omega$ can be read as \"make $\\omega$ copies of $2$\":\n", 329 | "\n", 330 | "$$ 2 \\cdot \\omega = |\\ |\\ \\ \\color{red}{|\\ |\\ }\\ |\\ |\\ \\ \\color{red}{|\\ |\\ }\\dots$$\n", 331 | "\n", 332 | "This sequence of sticks is indistinguishable from $\\omega$:" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": 11, 338 | "metadata": { 339 | "collapsed": false 340 | }, 341 | "outputs": [ 342 | { 343 | "data": { 344 | "text/latex": [ 345 | "$\\omega$" 346 | ], 347 | "text/plain": [ 348 | "\\omega" 349 | ] 350 | }, 351 | "execution_count": 11, 352 | "metadata": {}, 353 | "output_type": "execute_result" 354 | } 355 | ], 356 | "source": [ 357 | "2 * w" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "What happens if we demand $\\omega$ many copies of the ordinal $\\omega$? " 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": 12, 370 | "metadata": { 371 | "collapsed": false 372 | }, 373 | "outputs": [ 374 | { 375 | "data": { 376 | "text/latex": [ 377 | "$\\omega^{2}$" 378 | ], 379 | "text/plain": [ 380 | "\\omega^{2}" 381 | ] 382 | }, 383 | "execution_count": 12, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "w * w" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "Or $\\omega$ copies of this ordinal?" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 13, 402 | "metadata": { 403 | "collapsed": false 404 | }, 405 | "outputs": [ 406 | { 407 | "data": { 408 | "text/latex": [ 409 | "$\\omega^{3}$" 410 | ], 411 | "text/plain": [ 412 | "\\omega^{3}" 413 | ] 414 | }, 415 | "execution_count": 13, 416 | "metadata": {}, 417 | "output_type": "execute_result" 418 | } 419 | ], 420 | "source": [ 421 | "w * w * w" 422 | ] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": {}, 427 | "source": [ 428 | "Even larger ordinals! Larger than any number of finite copies of $\\omega$. And of course, we can go further than merely $\\omega$ copies:" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 14, 434 | "metadata": { 435 | "collapsed": false 436 | }, 437 | "outputs": [ 438 | { 439 | "data": { 440 | "text/latex": [ 441 | "$\\omega^{3} + \\omega^{2}$" 442 | ], 443 | "text/plain": [ 444 | "\\omega^{3} + \\omega^{2}" 445 | ] 446 | }, 447 | "execution_count": 14, 448 | "metadata": {}, 449 | "output_type": "execute_result" 450 | } 451 | ], 452 | "source": [ 453 | "(w*w) * (w+1) # w+1 many copies of w*w" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "Again, watch out for lack of commutativity:" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 15, 466 | "metadata": { 467 | "collapsed": false 468 | }, 469 | "outputs": [ 470 | { 471 | "data": { 472 | "text/latex": [ 473 | "$\\omega^{3}$" 474 | ], 475 | "text/plain": [ 476 | "\\omega^{3}" 477 | ] 478 | }, 479 | "execution_count": 15, 480 | "metadata": {}, 481 | "output_type": "execute_result" 482 | } 483 | ], 484 | "source": [ 485 | "(w+1) * (w*w) # w*w many copies of w+1" 486 | ] 487 | }, 488 | { 489 | "cell_type": "markdown", 490 | "metadata": {}, 491 | "source": [ 492 | "## Ordinal exponentiation\n", 493 | "\n", 494 | "The results from the computation bring to exponentiation. Ordinals can be raised to the power of any other ordinals:" 495 | ] 496 | }, 497 | { 498 | "cell_type": "code", 499 | "execution_count": 16, 500 | "metadata": { 501 | "collapsed": false 502 | }, 503 | "outputs": [ 504 | { 505 | "data": { 506 | "text/latex": [ 507 | "$\\omega^{7}$" 508 | ], 509 | "text/plain": [ 510 | "\\omega^{7}" 511 | ] 512 | }, 513 | "execution_count": 16, 514 | "metadata": {}, 515 | "output_type": "execute_result" 516 | } 517 | ], 518 | "source": [ 519 | "w ** 7" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 17, 525 | "metadata": { 526 | "collapsed": false 527 | }, 528 | "outputs": [ 529 | { 530 | "data": { 531 | "text/latex": [ 532 | "$\\omega^{\\omega}$" 533 | ], 534 | "text/plain": [ 535 | "\\omega^{\\omega}" 536 | ] 537 | }, 538 | "execution_count": 17, 539 | "metadata": {}, 540 | "output_type": "execute_result" 541 | } 542 | ], 543 | "source": [ 544 | "w ** w" 545 | ] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": 18, 550 | "metadata": { 551 | "collapsed": false 552 | }, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/latex": [ 557 | "$\\omega^{\\omega + 9}$" 558 | ], 559 | "text/plain": [ 560 | "\\omega^{\\omega + 9}" 561 | ] 562 | }, 563 | "execution_count": 18, 564 | "metadata": {}, 565 | "output_type": "execute_result" 566 | } 567 | ], 568 | "source": [ 569 | "w ** (w + 9)" 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": 19, 575 | "metadata": { 576 | "collapsed": false 577 | }, 578 | "outputs": [ 579 | { 580 | "data": { 581 | "text/latex": [ 582 | "$\\omega^{\\omega^{\\omega}}$" 583 | ], 584 | "text/plain": [ 585 | "\\omega^{\\omega^{\\omega}}" 586 | ] 587 | }, 588 | "execution_count": 19, 589 | "metadata": {}, 590 | "output_type": "execute_result" 591 | } 592 | ], 593 | "source": [ 594 | "w**w**w" 595 | ] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "metadata": {}, 600 | "source": [ 601 | "The intuition is easier to see with fininite ordinals.\n", 602 | "\n", 603 | "For example, $2 ^ \\omega$ is the same as doubling a sequence of two sticks $\\omega$-many times:" 604 | ] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "execution_count": 20, 609 | "metadata": { 610 | "collapsed": false 611 | }, 612 | "outputs": [ 613 | { 614 | "data": { 615 | "text/latex": [ 616 | "$\\omega$" 617 | ], 618 | "text/plain": [ 619 | "\\omega" 620 | ] 621 | }, 622 | "execution_count": 20, 623 | "metadata": {}, 624 | "output_type": "execute_result" 625 | } 626 | ], 627 | "source": [ 628 | "2**w" 629 | ] 630 | }, 631 | { 632 | "cell_type": "code", 633 | "execution_count": 21, 634 | "metadata": { 635 | "collapsed": false 636 | }, 637 | "outputs": [ 638 | { 639 | "data": { 640 | "text/latex": [ 641 | "$\\omega\\cdot2$" 642 | ], 643 | "text/plain": [ 644 | "\\omega\\cdot2" 645 | ] 646 | }, 647 | "execution_count": 21, 648 | "metadata": {}, 649 | "output_type": "execute_result" 650 | } 651 | ], 652 | "source": [ 653 | "2 ** (w+1)" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": {}, 659 | "source": [ 660 | "We can also double the sequence $\\omega + 1$ times. You can see that this last result holds since $ 2^{\\omega + 1} = 2^{\\omega}\\cdot2^1 = \\omega \\cdot 2 $." 661 | ] 662 | }, 663 | { 664 | "cell_type": "markdown", 665 | "metadata": {}, 666 | "source": [ 667 | "Here's a question to check our understanding so far: what is the result of $2^{\\omega ^ {\\omega}}$ and why?" 668 | ] 669 | }, 670 | { 671 | "cell_type": "code", 672 | "execution_count": null, 673 | "metadata": { 674 | "collapsed": true 675 | }, 676 | "outputs": [], 677 | "source": [] 678 | } 679 | ], 680 | "metadata": { 681 | "kernelspec": { 682 | "display_name": "Python 3", 683 | "language": "python", 684 | "name": "python3" 685 | }, 686 | "language_info": { 687 | "codemirror_mode": { 688 | "name": "ipython", 689 | "version": 3 690 | }, 691 | "file_extension": ".py", 692 | "mimetype": "text/x-python", 693 | "name": "python", 694 | "nbconvert_exporter": "python", 695 | "pygments_lexer": "ipython3", 696 | "version": "3.6.0" 697 | } 698 | }, 699 | "nbformat": 4, 700 | "nbformat_minor": 2 701 | } 702 | -------------------------------------------------------------------------------- /tests/test_ordinal.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import operator 3 | 4 | import pytest 5 | 6 | from transfinite.ordinal import Ordinal, OrdinalConstructionError 7 | from transfinite.util import as_latex 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "kwargs", 12 | [ 13 | {"exponent": 0}, 14 | {"exponent": -1}, 15 | {"exponent": -3.14}, 16 | {"exponent": "0"}, 17 | {"copies": 0}, 18 | {"copies": -1}, 19 | {"copies": -3.14}, 20 | {"copies": "0"}, 21 | {"copies": Ordinal()}, 22 | {"addend": -1}, 23 | {"addend": -3.14}, 24 | {"addend": "0"}, 25 | {"exponent": 1, "addend": Ordinal(exponent=1)}, 26 | {"exponent": Ordinal(), "addend": Ordinal(exponent=Ordinal())}, 27 | ], 28 | ) 29 | def test_invalid_args_to_class(kwargs): 30 | with pytest.raises(OrdinalConstructionError): 31 | Ordinal(**kwargs) 32 | 33 | 34 | @pytest.mark.parametrize( 35 | "a", 36 | [ 37 | # w 38 | Ordinal(), 39 | # w**2 40 | Ordinal(exponent=2), 41 | # w**w 42 | Ordinal(exponent=Ordinal()), 43 | # w**2 * 5 44 | Ordinal(exponent=2, copies=5), 45 | # w**2 + w 46 | Ordinal(exponent=2, addend=Ordinal()), 47 | ], 48 | ) 49 | def test_equals(a): 50 | assert a == copy.deepcopy(a) 51 | 52 | 53 | @pytest.mark.parametrize( 54 | "a,b", 55 | [ 56 | # w != w**2 57 | (Ordinal(), Ordinal(exponent=2)), 58 | # w != w*2 59 | (Ordinal(), Ordinal(copies=2)), 60 | # w == w + 2 61 | (Ordinal(), Ordinal(addend=2)), 62 | ], 63 | ) 64 | def test_not_equals(a, b): 65 | assert a != b 66 | 67 | 68 | @pytest.mark.parametrize( 69 | "a,b", 70 | [ 71 | # 13 < w 72 | (13, Ordinal()), 73 | # w < w**2 74 | (Ordinal(), Ordinal(exponent=2)), 75 | # w < w + 2 76 | (Ordinal(), Ordinal(addend=2)), 77 | # w < w*9 78 | (Ordinal(), Ordinal(copies=9)), 79 | # w < w**w 80 | (Ordinal(), Ordinal(exponent=Ordinal())), 81 | # w**3 < w**5 82 | (Ordinal(exponent=3), Ordinal(exponent=5)), 83 | # w**3 < w**w 84 | (Ordinal(exponent=3), Ordinal(exponent=Ordinal())), 85 | # w**(w+1) < w**w**w 86 | ( 87 | Ordinal(exponent=Ordinal(addend=1)), 88 | Ordinal(exponent=Ordinal(exponent=Ordinal())), 89 | ), 90 | # w**(w*100) < w**w**w 91 | ( 92 | Ordinal(exponent=Ordinal(copies=100)), 93 | Ordinal(exponent=Ordinal(exponent=Ordinal())), 94 | ), 95 | # w**w**2 < w**w**w 96 | ( 97 | Ordinal(exponent=Ordinal(exponent=2)), 98 | Ordinal(exponent=Ordinal(exponent=Ordinal())), 99 | ), 100 | # w**(w+2) + w*3 < w**(w+3) 101 | ( 102 | Ordinal(exponent=Ordinal(addend=2), addend=Ordinal(copies=3)), 103 | Ordinal(exponent=Ordinal(exponent=Ordinal(addend=3))), 104 | ), 105 | ], 106 | ) 107 | def test_less_than(a, b): 108 | assert a < b 109 | 110 | 111 | @pytest.mark.parametrize( 112 | "a,b", 113 | [ 114 | # w**5 > w**2 115 | (Ordinal(exponent=5), Ordinal(exponent=2)), 116 | # w**w > w 117 | (Ordinal(exponent=Ordinal()), Ordinal()), 118 | # w**2 > w + 99 119 | (Ordinal(exponent=2), Ordinal(addend=99)), 120 | # w**w > w + 99 121 | (Ordinal(exponent=Ordinal()), Ordinal(addend=99)), 122 | # w**(w**w + 3) > w + 99 123 | (Ordinal(exponent=Ordinal(exponent=Ordinal(), addend=3)), Ordinal(addend=99)), 124 | # w**(w**w + w) > w**(w**w + 2) + 99 125 | ( 126 | Ordinal(exponent=Ordinal(exponent=Ordinal(), addend=Ordinal())), 127 | Ordinal(exponent=Ordinal(exponent=Ordinal(), addend=2), addend=99), 128 | ), 129 | # w**w > 16 130 | (Ordinal(exponent=Ordinal()), 16), 131 | ], 132 | ) 133 | def test_greater_than(a, b): 134 | assert a > b 135 | 136 | 137 | @pytest.mark.parametrize( 138 | "a,expected_str,expected_latex", 139 | [ 140 | # w 141 | (Ordinal(), "w", r"\omega"), 142 | # w**5 143 | (Ordinal(exponent=5), "w**5", r"\omega^{5}"), 144 | # w**w 145 | (Ordinal(exponent=Ordinal()), "w**w", r"\omega^{\omega}"), 146 | # w**(w + 1) 147 | (Ordinal(exponent=Ordinal(addend=1)), "w**(w + 1)", r"\omega^{\omega+1}"), 148 | # w**(w*3) 149 | ( 150 | Ordinal(exponent=Ordinal(copies=3)), 151 | "w**(w*3)", 152 | r"\omega^{\omega\cdot3}", 153 | ), 154 | # w + 99 155 | (Ordinal(addend=99), "w + 99", r"\omega+99"), 156 | # w**(w**5 + w*3 + 66) * 5 157 | ( 158 | Ordinal( 159 | exponent=Ordinal(exponent=5, addend=Ordinal(copies=3, addend=66)), 160 | copies=5, 161 | ), 162 | "w**(w**5 + w*3 + 66)*5", 163 | r"\omega^{\omega^{5}+\omega\cdot3+66}\cdot5", 164 | ), 165 | ], 166 | ) 167 | def test_as_string(a, expected_str, expected_latex): 168 | assert str(a) == expected_str 169 | assert as_latex(a) == expected_latex 170 | 171 | 172 | @pytest.mark.parametrize( 173 | "a,b,expected", 174 | [ 175 | # (w) + 0 == w 176 | (Ordinal(), 0, Ordinal()), 177 | # 0 + (w) == w 178 | (0, Ordinal(), Ordinal()), 179 | # (w) + 1 == w + 1 180 | (Ordinal(), 1, Ordinal(addend=1)), 181 | # (w**3 + w) + 2 == w**3 + w + 2 182 | ( 183 | Ordinal(exponent=Ordinal(exponent=3), addend=Ordinal()), 184 | 2, 185 | Ordinal(exponent=Ordinal(exponent=3), addend=Ordinal(addend=2)), 186 | ), 187 | # 1 + w == w 188 | (1, Ordinal(), Ordinal()), 189 | # w + w**3 == w**3 190 | (Ordinal(), Ordinal(exponent=3), Ordinal(exponent=3)), 191 | # (w) + (w) == w*2 192 | (Ordinal(), Ordinal(), Ordinal(copies=2)), 193 | # (w*12 + 2) + (w*7) == w*19 194 | ( 195 | Ordinal(copies=12, addend=2), 196 | Ordinal(copies=7), 197 | Ordinal(copies=19), 198 | ), 199 | # (w**2 + w + 2) + 2 == w**2 + w + 4 200 | ( 201 | Ordinal(exponent=2, addend=Ordinal(addend=2)), 202 | 2, 203 | Ordinal(exponent=2, addend=Ordinal(addend=4)), 204 | ), 205 | # (w**2 + w + 2) + (w*7 + 3) == w**2 + w*8 + 3 206 | ( 207 | Ordinal(exponent=2, addend=Ordinal(addend=2)), 208 | Ordinal(copies=7, addend=3), 209 | Ordinal(exponent=2, addend=Ordinal(copies=8, addend=3)), 210 | ), 211 | # (w**2 + w + 2) + (w**9 + w*7 + 3) == w**9 + w*7 + 3 212 | ( 213 | Ordinal(exponent=2, addend=Ordinal(addend=2)), 214 | Ordinal(copies=7, addend=3), 215 | Ordinal(exponent=2, addend=Ordinal(copies=8, addend=3)), 216 | ), 217 | # (w**w + w*12 + 2) + (w**w + w*7) == w**w*2 + w*7 218 | ( 219 | Ordinal(exponent=Ordinal(), addend=Ordinal(copies=12, addend=2)), 220 | Ordinal(exponent=Ordinal(), addend=Ordinal(copies=7)), 221 | Ordinal(exponent=Ordinal(), copies=2, addend=Ordinal(copies=7)), 222 | ), 223 | # (w**5 + w**3 + 1) + (w**4 + w**2) == w**5 + w**4 + w**2 224 | ( 225 | Ordinal(exponent=5, addend=Ordinal(exponent=3, addend=1)), 226 | Ordinal(exponent=4, addend=Ordinal(exponent=2)), 227 | Ordinal(exponent=5, addend=Ordinal(exponent=4, addend=Ordinal(exponent=2))), 228 | ), 229 | # (w**(w*5) + w) + (w**(w*5) + w) == w**(w*5)*2 + w 230 | ( 231 | Ordinal(exponent=Ordinal(copies=5), addend=Ordinal()), 232 | Ordinal(exponent=Ordinal(copies=5), addend=Ordinal()), 233 | Ordinal(exponent=Ordinal(copies=5), copies=2, addend=Ordinal()), 234 | ), 235 | ], 236 | ) 237 | def test_addition(a, b, expected): 238 | assert a + b == expected 239 | 240 | 241 | @pytest.mark.parametrize( 242 | "a,b,expected", 243 | [ 244 | # (w) * 0 == 0 245 | (Ordinal(), 0, 0), 246 | # 0 * (w) == 0 247 | (0, Ordinal(), 0), 248 | # (w) * 1 == w 249 | (Ordinal(), 1, Ordinal()), 250 | # (w) * 2 == w*2 251 | (Ordinal(), 2, Ordinal(copies=2)), 252 | # 2 * (w) == w 253 | (2, Ordinal(), Ordinal()), 254 | # 2 * (w + 1) == w + 2 255 | (2, Ordinal(addend=1), Ordinal(addend=2)), 256 | # (w + 1) * 2 == w*2 + 1 257 | (Ordinal(addend=1), 2, Ordinal(copies=2, addend=1)), 258 | # (w + 9) * 2 == w*2 + 9 259 | (Ordinal(addend=9), 2, Ordinal(copies=2, addend=9)), 260 | # (w + 9) * 3 == w*3 + 9 261 | (Ordinal(addend=9), 3, Ordinal(copies=3, addend=9)), 262 | # (w) * (w) == w**2 263 | (Ordinal(), Ordinal(), Ordinal(exponent=2)), 264 | # (w) * (w + 1) == w**2 + w 265 | (Ordinal(), Ordinal(addend=1), Ordinal(exponent=2, addend=Ordinal())), 266 | # (w + 1) * (w + 1) == w**2 + w + 1 267 | ( 268 | Ordinal(addend=1), 269 | Ordinal(addend=1), 270 | Ordinal(exponent=2, addend=Ordinal(addend=1)), 271 | ), 272 | # (w**2 + w + 1) * (w + 1) == w**3 + w**2 + w + 1 273 | ( 274 | Ordinal(exponent=2, addend=Ordinal(addend=1)), 275 | Ordinal(addend=1), 276 | Ordinal(exponent=3, addend=Ordinal(exponent=2, addend=Ordinal(addend=1))), 277 | ), 278 | # (w*3) * (w*3) == w**2*3 279 | ( 280 | Ordinal(copies=3), 281 | Ordinal(copies=3), 282 | Ordinal(exponent=2, copies=3), 283 | ), 284 | # (w**5) * (w) == w**6 285 | (Ordinal(exponent=5), Ordinal(), Ordinal(exponent=6)), 286 | # (w**2) * (w**7) == w**9 287 | (Ordinal(exponent=2), Ordinal(exponent=7), Ordinal(exponent=9)), 288 | # (w + 5) * (w + 2) == w**2 + w*2 + 5 289 | ( 290 | Ordinal(addend=5), 291 | Ordinal(addend=2), 292 | Ordinal(exponent=2, addend=Ordinal(copies=2, addend=5)), 293 | ), 294 | # (w**2 + w + 1) * (w*2 + 2) == w**3*2 + w**2*2 + w + 1 295 | ( 296 | Ordinal(exponent=2, addend=Ordinal(addend=1)), 297 | Ordinal(copies=2, addend=2), 298 | Ordinal( 299 | exponent=3, 300 | copies=2, 301 | addend=Ordinal(exponent=2, copies=2, addend=Ordinal(addend=1)), 302 | ), 303 | ), 304 | # (w**w) * (w) == w**(w+1) 305 | (Ordinal(exponent=Ordinal()), Ordinal(), Ordinal(exponent=Ordinal(addend=1))), 306 | # (w**w) * (w+2) == w**(w+1) + w**w*2 307 | ( 308 | Ordinal(exponent=Ordinal()), 309 | Ordinal(addend=2), 310 | Ordinal( 311 | exponent=Ordinal(addend=1), 312 | addend=Ordinal(exponent=Ordinal(), copies=2), 313 | ), 314 | ), 315 | # 3 * (w*2 + 4) == w*2 + 12 316 | (3, Ordinal(copies=2, addend=4), Ordinal(copies=2, addend=12)), 317 | # (w*3 + 4) * (w*3) == w**2*3 318 | ( 319 | Ordinal(copies=3, addend=4), 320 | Ordinal(copies=3), 321 | Ordinal(exponent=2, copies=3), 322 | ), 323 | # (w + 1) * (w**2) == w**3 324 | (Ordinal(addend=1), Ordinal(exponent=2), Ordinal(exponent=3)), 325 | # w**(w**(w*5) + w) * w**(w**(w*5) + w) == w**(w**(w*5)*2+w) 326 | ( 327 | Ordinal( 328 | exponent=Ordinal(exponent=Ordinal(copies=5), addend=Ordinal()) 329 | ), 330 | Ordinal( 331 | exponent=Ordinal(exponent=Ordinal(copies=5), addend=Ordinal()) 332 | ), 333 | Ordinal( 334 | exponent=Ordinal( 335 | exponent=Ordinal(copies=5), copies=2, addend=Ordinal() 336 | ) 337 | ), 338 | ), 339 | ], 340 | ) 341 | def test_multiplication(a, b, expected): 342 | assert a * b == expected 343 | 344 | 345 | @pytest.mark.parametrize( 346 | "a,b,expected", 347 | [ 348 | # (w) ** 0 == 0 349 | (Ordinal(), 0, 1), 350 | # (w) ** 1 == w 351 | (Ordinal(), 1, Ordinal()), 352 | # (w) ** 2 == w**2 353 | (Ordinal(), Ordinal(), Ordinal(exponent=Ordinal())), 354 | # (w) ** (w) == w**w 355 | (Ordinal(exponent=2), 2, Ordinal(exponent=4)), 356 | # (w + 1) ** 2 == w**2 + w + 1 357 | (Ordinal(addend=1), 2, Ordinal(exponent=2, addend=Ordinal(addend=1))), 358 | # (w + 1) ** 3 == w**3 + w**2 + w + 1 359 | ( 360 | Ordinal(addend=1), 361 | 3, 362 | Ordinal(exponent=3, addend=Ordinal(exponent=2, addend=Ordinal(addend=1))), 363 | ), 364 | # (w + 1) ** w == w**w 365 | (Ordinal(addend=1), Ordinal(), Ordinal(exponent=Ordinal())), 366 | # 0 ** (w) == 0 367 | (0, Ordinal(), 0), 368 | # 1 ** (w) == 1 369 | (1, Ordinal(), 1), 370 | # 2 ** (w) == w 371 | (2, Ordinal(), Ordinal()), 372 | # 7 ** (w) == w 373 | (7, Ordinal(), Ordinal()), 374 | # 2 ** (w*6) == w**6 375 | (2, Ordinal(copies=6), Ordinal(exponent=6)), 376 | # 2 ** (w + 1) == w*2 377 | (2, Ordinal(addend=1), Ordinal(copies=2)), 378 | # 5 ** (w + 2) == w*25 379 | (5, Ordinal(addend=2), Ordinal(copies=25)), 380 | # 2 ** (w**w) == w**w**w 381 | (2, Ordinal(exponent=Ordinal()), Ordinal(Ordinal(exponent=Ordinal()))), 382 | # 2 ** (w**9) == w**w**8 383 | (2, Ordinal(exponent=9), Ordinal(Ordinal(exponent=8))), 384 | # 2 ** (w**w + w*3 + 9) == w**(w**w + 3)*512 385 | ( 386 | 2, 387 | Ordinal(exponent=Ordinal(), addend=Ordinal(copies=3, addend=9)), 388 | Ordinal(exponent=Ordinal(exponent=Ordinal(), addend=3), copies=512), 389 | ), 390 | # 2 ** (w**w**w) == w**w**w**w 391 | ( 392 | 2, 393 | Ordinal(exponent=Ordinal(exponent=Ordinal())), 394 | Ordinal(exponent=Ordinal(exponent=Ordinal(exponent=Ordinal()))), 395 | ), 396 | # 3 ** (w**w + 1) == (w**w**w)*3 397 | ( 398 | 3, 399 | Ordinal(exponent=Ordinal(), addend=1), 400 | Ordinal(exponent=Ordinal(exponent=Ordinal()), copies=3), 401 | ), 402 | # 2**(w**2 + w) == w**(w + 1) 403 | (2, Ordinal(exponent=2, addend=Ordinal()), Ordinal(exponent=Ordinal(addend=1))), 404 | # 2**(w**2 + 3) == w**w * 8 405 | (2, Ordinal(exponent=2, addend=3), Ordinal(exponent=Ordinal(), copies=8)), 406 | # 2**(w**3 * 7) == w**(w**2 * 7) 407 | ( 408 | 2, 409 | Ordinal(exponent=3, copies=7), 410 | Ordinal(exponent=Ordinal(exponent=2, copies=7)), 411 | ), 412 | # 2**(w**3 * 7 + 5) == w**(w**2 * 7) * 32 413 | ( 414 | 2, 415 | Ordinal(exponent=3, copies=7, addend=5), 416 | Ordinal(exponent=Ordinal(exponent=2, copies=7), copies=32), 417 | ), 418 | # (w**(w**(w*5) + w**w + 2)) ** 2 == w**(w**(w*5)*2+w**w+2) 419 | ( 420 | Ordinal( 421 | exponent=Ordinal( 422 | exponent=Ordinal(copies=5), 423 | addend=Ordinal(exponent=Ordinal(), addend=2), 424 | ) 425 | ), 426 | 2, 427 | Ordinal( 428 | exponent=Ordinal( 429 | exponent=Ordinal(copies=5), 430 | copies=2, 431 | addend=Ordinal(exponent=Ordinal(), addend=2), 432 | ) 433 | ), 434 | ), 435 | # (w**(w**(w*5) + w**w + 2)) ** (w + 3) == w**(w**(w*5+1)+w**(w*5)*3+w**w+2) 436 | ( 437 | Ordinal( 438 | exponent=Ordinal( 439 | exponent=Ordinal(copies=5), 440 | addend=Ordinal(exponent=Ordinal(), addend=2), 441 | ) 442 | ), 443 | Ordinal(addend=3), 444 | Ordinal( 445 | exponent=Ordinal( 446 | exponent=Ordinal(copies=5, addend=1), 447 | addend=Ordinal( 448 | exponent=Ordinal(copies=5), 449 | copies=3, 450 | addend=Ordinal(exponent=Ordinal(), addend=2), 451 | ), 452 | ) 453 | ), 454 | ), 455 | ], 456 | ) 457 | def test_power(a, b, expected): 458 | assert a ** b == expected 459 | 460 | 461 | @pytest.mark.parametrize( 462 | "a,expected", 463 | [ 464 | (Ordinal(), True), 465 | (Ordinal(addend=1), False), 466 | (Ordinal(exponent=Ordinal(), addend=1), False), 467 | (Ordinal(exponent=Ordinal()), True), 468 | (Ordinal(exponent=Ordinal(), addend=Ordinal(addend=3)), False), 469 | (Ordinal(exponent=Ordinal(), addend=Ordinal(copies=3)), True), 470 | ], 471 | ) 472 | def test_is_limit(a, expected): 473 | assert a.is_limit() is expected 474 | 475 | 476 | @pytest.mark.parametrize( 477 | "op", 478 | [ 479 | operator.add, 480 | operator.mul, 481 | operator.pow, 482 | operator.lt, 483 | operator.gt, 484 | operator.ge, 485 | operator.le, 486 | ], 487 | ) 488 | @pytest.mark.parametrize( 489 | "a,expected_error", 490 | [(-2, TypeError), (2.1, TypeError), ("2", TypeError), (7j, TypeError)], 491 | ) 492 | def test_invalid_types_operation(op, a, expected_error): 493 | with pytest.raises(expected_error): 494 | op(Ordinal(), a) 495 | op(a, Ordinal()) 496 | 497 | 498 | @pytest.mark.parametrize( 499 | "a,expected", 500 | [ 501 | ## Primes 502 | # w 503 | (Ordinal(), True), 504 | # w + 1 505 | (Ordinal(addend=1), True), 506 | # w**2 + 1 507 | (Ordinal(exponent=2, addend=1), True), 508 | # w**3 + 1 509 | (Ordinal(exponent=3, addend=1), True), 510 | # w**w 511 | (Ordinal(exponent=Ordinal()), True), 512 | # w**w + 1 513 | (Ordinal(exponent=Ordinal(), addend=1), True), 514 | # w**(w + 1) + 1 515 | (Ordinal(exponent=Ordinal(addend=1), addend=1), True), 516 | # w**w**w + 1 517 | (Ordinal(exponent=Ordinal(exponent=Ordinal())), True), 518 | # w**w**(w+1) 519 | (Ordinal(exponent=Ordinal(exponent=Ordinal(addend=1))), True), 520 | # w**w**(w*2) 521 | (Ordinal(exponent=Ordinal(exponent=Ordinal(copies=2))), True), 522 | ## Composites 523 | # w + 2 [2 * (w+1)] 524 | (Ordinal(addend=2), False), 525 | # w*2 [w * 2] 526 | (Ordinal(copies=2), False), 527 | # w*2 + 1 [(w+1) * 2] 528 | (Ordinal(copies=2, addend=1), False), 529 | # w*2 + 3 [3 * (w+1) * 2] 530 | (Ordinal(copies=2, addend=3), False), 531 | # w**2 [w * w] 532 | (Ordinal(exponent=2), False), 533 | # w**2 + w [w * (w**2 + 1)] 534 | (Ordinal(exponent=2, addend=Ordinal()), False), 535 | # w**(w*2) [w**w * w**w] 536 | (Ordinal(exponent=Ordinal(copies=2)), False), 537 | # w**(w+1) [w**w * w] 538 | (Ordinal(exponent=Ordinal(addend=1)), False), 539 | ], 540 | ) 541 | def test_is_prime(a, expected): 542 | assert a.is_prime() is expected 543 | 544 | 545 | @pytest.mark.parametrize( 546 | "a,expected", 547 | [ 548 | # w 549 | (Ordinal(), True), 550 | # w + 1 551 | (Ordinal(addend=1), False), 552 | # w**2 + 1 553 | (Ordinal(exponent=2, addend=1), False), 554 | # w**w 555 | (Ordinal(exponent=Ordinal()), True), 556 | # w**w + 1 557 | (Ordinal(exponent=Ordinal(), addend=1), False), 558 | # w**(w + 1) 559 | (Ordinal(exponent=Ordinal(addend=1), addend=1), False), 560 | # w**w**w 561 | (Ordinal(exponent=Ordinal(exponent=Ordinal())), True), 562 | # w**w**w + 1 563 | (Ordinal(exponent=Ordinal(exponent=Ordinal()), addend=1), False), 564 | # w + 2 565 | (Ordinal(addend=2), False), 566 | # w*2 567 | (Ordinal(copies=2), False), 568 | # w*2 + 3 569 | (Ordinal(copies=2, addend=3), False), 570 | ], 571 | ) 572 | def test_is_gamma(a, expected): 573 | assert a.is_gamma() is expected 574 | --------------------------------------------------------------------------------