예제 #1
0
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"
예제 #2
0
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_
예제 #3
0
    def __radd__(self, other):

        if not is_finite_ordinal(other):
            return NotImplemented

        # n + a == a
        return self
예제 #4
0
    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()
예제 #5
0
    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())
예제 #6
0
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)
예제 #7
0
    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)
예제 #8
0
    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
예제 #9
0
    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
예제 #10
0
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_)
예제 #11
0
    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
예제 #12
0
    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,
        )
예제 #13
0
    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
예제 #14
0
    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)
예제 #15
0
    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
예제 #16
0
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
예제 #17
0
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)
예제 #18
0
def is_ordinal(a):
    """
    Return True if a is a finite or infinite ordinal.

    """
    return is_finite_ordinal(a) or isinstance(a, Ordinal)
예제 #19
0
 def __lt__(self, other):
     if isinstance(other, Ordinal):
         return self.as_tuple() < other.as_tuple()
     if is_finite_ordinal(other):
         return False
     return NotImplemented