def test_factors(a): fs = factors(a) ordinals = [a for a, _ in fs] # Check the factorisation is correct assert all( is_finite_ordinal(a) and a > 1 or a.is_prime() for a in ordinals), "Not all factors are prime" assert fs.product() == a, "Incorrect factorisation" # Check the grouping and order of factors is correct grouper = groupby(ordinals, key=lambda x: is_finite_ordinal(x) or x.is_successor()) groups = [(is_successor, list(ords)) for is_successor, ords in grouper] assert len(groups) <= 2, "Factors not ordered by limit/successor" # If there are only successor ordinals as factors there is no need to check further if len(groups) == 1 and groups[0][0]: assert not has_equal_consecutive_elements( groups[0][1]), "Successor factors has consecutive equal ordinals" return # Otherwise, there are both limit/successor factors: limit must come first and be sorted assert (groups[0][0] is False), "Limit ordinals do not occur before successor ordinals" assert groups[0][1] == sorted( groups[0][1], reverse=True), "Successor ordinals not in descending order" assert not has_equal_consecutive_elements( groups[0][1]), "Limit factors contain equal consecutive ordinals"
def factorise_term(term): """ Write a single transfinite ordinal term (addend=0) as a product of primes. For example: w**(w**w*7 + w*3 + 2) becomes [(w**w**w, 7), (w**w, 3), (w, 2)] """ factors_ = [] for t in ordinal_terms(term.exponent): if is_finite_ordinal(t): factors_.append((Ordinal(), t)) else: factors_.append((Ordinal(exponent=Ordinal(t.exponent)), t.copies)) if term.copies > 1: factors_.append((term.copies, 1)) return factors_
def __radd__(self, other): if not is_finite_ordinal(other): return NotImplemented # n + a == a return self
def is_limit(self): """ Return true if ordinal is a limit ordinal. """ if is_finite_ordinal(self.addend): return self.addend == 0 return self.addend.is_limit()
def is_delta(self): """ Return true if ordinal is multiplicatively indecomposable. These are ordinals of the form w**w**a for an ordinal a which is either 0 or such that w**a is a gamma ordinal. """ return self.is_gamma() and (self.exponent == 1 or not is_finite_ordinal(self.exponent) and self.exponent.is_gamma())
def subtract(a, b): """ Given ordinals a > b, return the ordinal c such that b + c == a """ if a <= b: raise ValueError("First argument must be greater than second argument") if is_finite_ordinal(a): return a - b if is_finite_ordinal(b) or a.exponent > b.exponent: return a if a.exponent == b.exponent and a.copies == b.copies: return subtract(a.addend, b.addend) # Here we know that a.copies > b.copies return Ordinal(a.exponent, a.copies - b.copies, a.addend)
def __rmul__(self, other): if not is_finite_ordinal(other): return NotImplemented if other == 0: return 0 # n * (w**a*b + c) == w**a*b + (n*c) return Ordinal(self.exponent, self.copies, other * self.addend)
def __pow__(self, other): if not is_ordinal(other): return NotImplemented # Finite powers are computed using repeated multiplication if is_finite_ordinal(other): return exp_by_squaring(self, other) # (w**a*b + c) ** (w**x*y + z) == (w**(a * w**x * y)) * (w**a*b + c)**z return Ordinal(self.exponent * Ordinal( other.exponent, other.copies)) * self**other.addend
def __rpow__(self, other): if not is_finite_ordinal(other): return NotImplemented # 0**a == 0 and 1**a == 1 if other in (0, 1): return other # n**(w*c + a) == (w**c) * (n**a) if self.exponent == 1: return Ordinal(self.copies, other**self.addend) # n**(w**m*c + a) == w**(w**(m-1) * c) * n**a if is_finite_ordinal(self.exponent): return Ordinal(Ordinal(self.exponent - 1, self.copies)) * other**self.addend # n**(w**a*c + b) == w**(w**a*c) * n**b return Ordinal(Ordinal(self.exponent, self.copies)) * other**self.addend
def factors(ordinal): """ Return the prime factors of the ordinal. Note: finite integers are not broken into prime factors. """ terms = ordinal_terms(ordinal) # If the ordinal is a limit ordinal, it has the terms: # # terms = A + B + ... + X (A > B > ... > X > 0) # # We want to factor out X if X > 1, leaving: # # terms = A' + B' + ... + 1 (X*A' == A, X*B' == B, ...) # # The factors of X are the first factors of the ordinal. # Note the finite term 1 is not explicitly included in the list. if len(terms) == 1 or not is_finite_ordinal(terms[-1]): least_term = terms.pop() terms = divide_terms_by_ordinal(terms, least_term) factors_ = factorise_term(least_term) elif terms[-1] > 1: factors_ = [(terms.pop(), 1)] else: terms.pop() factors_ = [] # At this stage, terms is a list of infinite ordinals: # # terms = A' + B' + ... + C' (A' > B' > C' >= w) # # This represents the successor ordinal: # # A' + B' + ... + C' + 1 # # The loop below repeatedly removes the least term C' then adds # factors of (C' + 1) to the list of factors, then divides C' # into the remaining terms (A', B', ...). while terms: least_term = terms.pop() factors_ += factorise_term_successor(least_term) terms = divide_terms_by_ordinal(terms, least_term) return OrdinalFactors(factors_)
def __add__(self, other): if not is_ordinal(other): return NotImplemented # (w**a*b + c) + x == w**a*b + (c + x) if is_finite_ordinal(other) or self.exponent > other.exponent: return Ordinal(self.exponent, self.copies, self.addend + other) # (w**a*b + c) + (w**a*d + e) == w**a*(b + d) + e if self.exponent == other.exponent: return Ordinal(self.exponent, self.copies + other.copies, other.addend) # other is strictly greater than self return other
def __mul__(self, other): if not is_ordinal(other): return NotImplemented if other == 0: return 0 # (w**a*b + c) * n == w**a * (b*n) + c if is_finite_ordinal(other): return Ordinal(self.exponent, self.copies * other, self.addend) # (w**a*b + c) * (w**x*y + z) == w**(a + x)*y + (c*z + (w**a*b + c)*z) return Ordinal( self.exponent + other.exponent, other.copies, self.addend * other.addend + self * other.addend, )
def __init__(self, exponent=1, copies=1, addend=0): if exponent == 0 or not is_ordinal(exponent): raise OrdinalConstructionError( "exponent must be an Ordinal or an integer greater than 0") if copies == 0 or not is_finite_ordinal(copies): raise OrdinalConstructionError( "copies must be an integer greater than 0") if not is_ordinal(addend): raise OrdinalConstructionError( "addend must be an Ordinal or a non-negative integer") if isinstance(addend, Ordinal) and addend.exponent >= exponent: raise OrdinalConstructionError( "addend.exponent must be less than self.exponent") self.exponent = exponent self.copies = copies self.addend = addend
def as_latex(self): """ Returns a LaTeX string of the factors. Put parentheses around a factor if its exponent is greater than 1, or if its addend is greater than 0: (w, 1) -> w (w, 2) -> (w)^2 (w + 1, 1) -> (w + 1) (w + 1, 2) -> (w + 1)^2 (w**2 + 1, 1) -> (w^2 + 1) (w**2 + 1, 3) -> (w^2 + 1)^3 Note that since factors are prime, there are some cases that do not need to be covered here (e.g. w*n). """ fs_latex = [] for ordinal, exponent in self.factors: if is_finite_ordinal(ordinal): fs_latex.append(str(ordinal)) continue latex_ordinal = as_latex(ordinal) if ordinal.addend > 0 or exponent > 1: f = rf"\left({latex_ordinal}\right)" else: f = latex_ordinal if exponent > 1: f += f"^{{{exponent}}}" fs_latex.append(f) return r"\cdot".join(fs_latex)
def __str__(self): term = "w" # Only use parentheses for exponent if finite and greater than 1, # or its addend is nonzero or its copies is greater than 1. if self.exponent == 1: pass elif (is_finite_ordinal(self.exponent) or self.exponent.copies == 1 and self.exponent.addend == 0): term += f"**{self.exponent}" else: term += f"**({self.exponent})" if self.copies != 1: term += f"*{self.copies}" if self.addend != 0: term += f" + {self.addend}" return term
def ordinal_terms(ordinal): """ Return a list containing all terms of the ordinal. For example: w**w**w + w**3 + w*7 + 9 becomes the list [w**w**w, w**3, w*7, 9] """ terms = [] while not is_finite_ordinal(ordinal): term = Ordinal(exponent=ordinal.exponent, copies=ordinal.copies) terms.append(term) ordinal = ordinal.addend if ordinal: terms.append(ordinal) return terms
def test_factorise_term_successor(a): fs = factorise_term_successor(a) assert multiply_factors(fs) == a + 1 assert all(is_finite_ordinal(f) and f > 1 or f.is_prime() for f, _ in fs)
def is_ordinal(a): """ Return True if a is a finite or infinite ordinal. """ return is_finite_ordinal(a) or isinstance(a, Ordinal)
def __lt__(self, other): if isinstance(other, Ordinal): return self.as_tuple() < other.as_tuple() if is_finite_ordinal(other): return False return NotImplemented