The C3 algorithm, under control of a total order - Utilities

Skip to content
Back to top
View this page
Edit this page

# The C3 algorithm, under control of a total order

## Abstract

Python handles multiple inheritance by computing, for each class, a linear extension of the poset of all its super classes (the Method Resolution Order, MRO). The MRO is calculated recursively from local information (the ordered list of the direct super classes), with the so-called `C3` algorithm. This algorithm can fail if the local information is not consistent; worst, there exist hierarchies of classes with provably no consistent local information.

For large hierarchy of classes, like those derived from categories in Sage, maintaining consistent local information by hand does not scale and leads to unpredictable `C3` failures (the dreaded “could not find a consistent method resolution order”); a maintenance nightmare.

This module implements a final solution to this problem. Namely, it allows for building automatically the local information from the bare class hierarchy in such a way that guarantees that the `C3` algorithm will never fail.

Err, but you said that this was provably impossible? Well, not if one relaxes a bit the hypotheses; but that’s not something one would want to do by hand :-)

## The problem

Consider the following hierarchy of classes:

Sage

    sage: class A1(): pass
    sage: class A2():
    ....:     def foo(self): return 2
    sage: class A3(): pass
    sage: class A4():
    ....:     def foo(self): return 4
    sage: class A5(A2, A1):
    ....:     def foo(self): return 5
    sage: class A6(A4, A3): pass
    sage: class A7(A6, A5): pass

Python

    >>> from sage.all import *
    >>> class A1(): pass
    >>> class A2():
    ...     def foo(self): return Integer(2)
    >>> class A3(): pass
    >>> class A4():
    ...     def foo(self): return Integer(4)
    >>> class A5(A2, A1):
    ...     def foo(self): return Integer(5)
    >>> class A6(A4, A3): pass
    >>> class A7(A6, A5): pass

If `a` is an instance of `A7`, then Python needs to choose which implementation to use upon calling `a.foo()`: that of `A4` or `A5`, but obviously not that of `A2`. In Python, like in many other dynamic object oriented languages, this is achieved by calculating once for all a specific linear extension of the hierarchy of the super classes of each class, called its Method Resolution Order (MRO):

Sage

    sage: [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A4', 'A3', 'A5', 'A2', 'A1', 'object']

[Python]

    >>> from sage.all import *
    >>> [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A4', 'A3', 'A5', 'A2', 'A1', 'object']

Thus, in our example, the implementation in `A4` is chosen:

Sage

    sage: a = A7()
    sage: a.foo()
    4

Python

    >>> from sage.all import *
    >>> a = A7()
    >>> a.foo()
    4

Specifically, the MRO is calculated using the so-called `C3` algorithm which guarantees that the MRO respects not only inheritance, but also the order in which the bases (direct super classes) are given for each class.

However, for large hierarchies of classes with lots of multiple inheritance, like those derived from categories in Sage, this algorithm easily fails if the order of the bases is not chosen consistently (here for `A2` w.r.t. `A1`):

Sage

    sage: class B6(A1,A2): pass
    sage: class B7(B6,A5): pass
    Traceback (most recent call last):
    ...
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases A1, A2

[Python]

    >>> from sage.all import *
    >>> class B6(A1,A2): pass
    >>> class B7(B6,A5): pass
    Traceback (most recent call last):
    ...
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases A1, A2

There actually exist hierarchies of classes for which `C3` fails whatever order of the bases is chosen; the smallest such example, admittedly artificial, has ten classes (see below). Still, this highlights that this problem has to be tackled in a systematic way.

Fortunately, one can trick `C3`, without changing the inheritance semantic, by adding some super classes of `A` to the bases of `A`. In the following example, we completely force a given MRO by specifying all the super classes of `A` as bases:

Sage

    sage: class A7(A6, A5, A4, A3, A2, A1): pass
    sage: [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']

Python

    >>> from sage.all import *
    >>> class A7(A6, A5, A4, A3, A2, A1): pass
    >>> [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']

Luckily this can be optimized; here it is sufficient to add a single base to enforce the same MRO:

Sage

    sage: class A7(A6, A5, A4): pass
    sage: [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']

[Python]

    >>> from sage.all import *
    >>> class A7(A6, A5, A4): pass
    >>> [cls.__name__ for cls in A7.mro()]
    ['A7', 'A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'object']

## A strategy to solve the problem

We should recall at this point a design decision that we took for the hierarchy of classes derived from categories: the semantic shall only depend on the inheritance order, not on the specific MRO, and in particular not on the order of the bases (see On the order of super categories).

If a choice needs to be made (for example for efficiency reasons), then this should be done explicitly, on a method-by-method basis. In practice this design goal is not yet met.

Note

When managing large hierarchies of classes in other contexts this may be too strong a design decision.

The strategy we use for hierarchies of classes derived from categories is then:

1. To choose a global total order on the whole hierarchy of classes.

2. To control `C3` to get it to return MROs that follow this total order.

A basic approach for point 1., that will work for any hierarchy of classes, is to enumerate the classes while they are constructed (making sure that the bases of each class are enumerated before that class), and to order the classes according to that enumeration. A more conceptual ordering may be desirable, in particular to get deterministic and reproducible results. In the context of Sage, this is mostly relevant for those doctests displaying all the categories or classes that an object inherits from.

## Getting fine control on C3

This module is about point 2.

The natural approach would be to change the algorithm used by Python to compute the MRO. However, changing Python’s default algorithm just for our needs is obviously not an option, and there is currently no hook to customize specific classes to use a different algorithm. Pushing the addition of such a hook into stock Python would take too much time and effort.

Another approach would be to use the “adding bases” trick straightforwardly, putting the list of all the super classes of a class as its bases. However, this would have several drawbacks:

- It is not so elegant, in particular because it duplicates information: we already know through `A5` that `A7` is a subclass of `A1`. This duplication could be acceptable in our context because the hierarchy of classes is generated automatically from a conceptual hierarchy (the categories) which serves as single point of truth for calculating the bases of each class.

- It increases the complexity of the calculation of the MRO with `C3`. For example, for a linear hierarchy of classes, the complexity goes from \(O(n^2)\) to \(O(n^3)\) which is not acceptable.

- It increases the complexity of inspecting the classes. For example, the current implementation of the `dir` command in Python has no cache, and its complexity is linear in the number of maximal paths in the class hierarchy graph as defined by the bases. For a linear hierarchy, this is of complexity \(O(p_n)\) where \(p_n\) is the number of integer partitions of \(n\), which is exponential. And indeed, running `dir` for a typical class like `GradedHopfAlgebrasWithBasis(QQ).parent_class` with `37` super classes took \(18\) seconds with this approach.

  Granted: this mostly affects the `dir` command and could be blamed on its current implementation. With appropriate caching, it could be reimplemented to have a complexity roughly linear in the number of classes in the hierarchy. But this won’t happen any time soon in a stock Python.

This module refines this approach to make it acceptable, if not seamless. Given a hierarchy and a total order on this hierarchy, it calculates for each element of the hierarchy the smallest list of additional bases that forces `C3` to return the desired MRO. This is achieved by implementing an instrumented variant of the `C3` algorithm (which we call instrumented `C3`) that detects when `C3` is about to take a wrong decision and adds one base to force the right decision. Then, running the standard `C3` algorithm with the updated list of bases (which we call controlled `C3`) yields the desired MRO.

EXAMPLES:

As an experimentation and testing tool, we use a class `HierarchyElement` whose instances can be constructed from a hierarchy described by a poset, a digraph, or more generally a successor relation. By default, the desired MRO is sorted decreasingly. Another total order can be specified using a sorting key.

We consider the smallest poset describing a class hierarchy admitting no MRO whatsoever:

Sage

    sage: P = Poset({10: [9,8,7], 9: [6,1], 8: [5,2], 7: [4,3],                         # needs sage.graphs
    ....:            6: [3,2], 5: [3,1], 4: [2,1]},
    ....:           linear_extension=True, facade=True)

Python

    >>> from sage.all import *
    >>> P = Poset({Integer(10): [Integer(9),Integer(8),Integer(7)], Integer(9): [Integer(6),Integer(1)], Integer(8): [Integer(5),Integer(2)], Integer(7): [Integer(4),Integer(3)],                         # needs sage.graphs
    ...            Integer(6): [Integer(3),Integer(2)], Integer(5): [Integer(3),Integer(1)], Integer(4): [Integer(2),Integer(1)]},
    ...           linear_extension=True, facade=True)

And build a `HierarchyElement` from it:

Sage

    sage: from sage.misc.c3_controlled import HierarchyElement
    sage: x = HierarchyElement(10, P)                                                   # needs sage.graphs

[Python]

    >>> from sage.all import *
    >>> from sage.misc.c3_controlled import HierarchyElement
    >>> x = HierarchyElement(Integer(10), P)                                                   # needs sage.graphs

Here are its bases:

Sage

    sage: HierarchyElement(10, P)._bases                                                # needs sage.graphs
    [9, 8, 7]

Python

    >>> from sage.all import *
    >>> HierarchyElement(Integer(10), P)._bases                                                # needs sage.graphs
    [9, 8, 7]

Using the standard `C3` algorithm fails:

Sage

    sage: x.mro_standard                                                                # needs sage.graphs
    Traceback (most recent call last):
    ...
    ValueError: Cannot merge the items 3, 3, 2.

[Python]

    >>> from sage.all import *
    >>> x.mro_standard                                                                # needs sage.graphs
    Traceback (most recent call last):
    ...
    ValueError: Cannot merge the items 3, 3, 2.

We also get a failure when we relabel \(P\) according to another linear extension. For easy relabelling, we first need to set an appropriate default linear extension for \(P\):

Sage

    sage: linear_extension = list(reversed(IntegerRange(1, 11)))
    sage: P = P.with_linear_extension(linear_extension)                                 # needs sage.graphs
    sage: list(P)                                                                       # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Python

    >>> from sage.all import *
    >>> linear_extension = list(reversed(IntegerRange(Integer(1), Integer(11))))
    >>> P = P.with_linear_extension(linear_extension)                                 # needs sage.graphs
    >>> list(P)                                                                       # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Now we play with a specific linear extension of \(P\):

Sage

    sage: # needs sage.graphs
    sage: Q = P.linear_extension([10, 9, 8, 7, 6, 5, 4, 1, 2, 3]).to_poset()
    sage: Q.cover_relations()
    [[10, 9], [10, 8], [10, 7], [9, 6], [9, 3], [8, 5], [8, 2], [7, 4],
     [7, 1], [6, 2], [6, 1], [5, 3], [5, 1], [4, 3], [4, 2]]
    sage: x = HierarchyElement(10, Q)
    sage: x.mro_standard
    Traceback (most recent call last):
    ...
    ValueError: Cannot merge the items 2, 3, 3.

[Python]

    >>> from sage.all import *
    >>> # needs sage.graphs
    >>> Q = P.linear_extension([Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(1), Integer(2), Integer(3)]).to_poset()
    >>> Q.cover_relations()
    [[10, 9], [10, 8], [10, 7], [9, 6], [9, 3], [8, 5], [8, 2], [7, 4],
     [7, 1], [6, 2], [6, 1], [5, 3], [5, 1], [4, 3], [4, 2]]
    >>> x = HierarchyElement(Integer(10), Q)
    >>> x.mro_standard
    Traceback (most recent call last):
    ...
    ValueError: Cannot merge the items 2, 3, 3.

On the other hand, both the instrumented `C3` algorithm, and the controlled `C3` algorithm give the desired MRO:

Sage

    sage: x.mro                                                                         # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    sage: x.mro_controlled                                                              # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Python

    >>> from sage.all import *
    >>> x.mro                                                                         # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    >>> x.mro_controlled                                                              # needs sage.graphs
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

The above checks, and more, can be run with:

Sage

    sage: x._test_mro()                                                                 # needs sage.graphs

[Python]

    >>> from sage.all import *
    >>> x._test_mro()                                                                 # needs sage.graphs

In practice, the control was achieved by adding the following bases:

Sage

    sage: x._bases                                                                      # needs sage.graphs
    [9, 8, 7]
    sage: x._bases_controlled                                                           # needs sage.graphs
    [9, 8, 7, 6, 5]

Python

    >>> from sage.all import *
    >>> x._bases                                                                      # needs sage.graphs
    [9, 8, 7]
    >>> x._bases_controlled                                                           # needs sage.graphs
    [9, 8, 7, 6, 5]

Altogether, four bases were added for control:

Sage

    sage: sum(len(HierarchyElement(q, Q)._bases) for q in Q)                            # needs sage.graphs
    15
    sage: sum(len(HierarchyElement(q, Q)._bases_controlled) for q in Q)                 # needs sage.graphs
    19

[Python]

    >>> from sage.all import *
    >>> sum(len(HierarchyElement(q, Q)._bases) for q in Q)                            # needs sage.graphs
    15
    >>> sum(len(HierarchyElement(q, Q)._bases_controlled) for q in Q)                 # needs sage.graphs
    19

This information can also be recovered with:

Sage

    sage: x.all_bases_len()                                                             # needs sage.graphs
    15
    sage: x.all_bases_controlled_len()                                                  # needs sage.graphs
    19

Python

    >>> from sage.all import *
    >>> x.all_bases_len()                                                             # needs sage.graphs
    15
    >>> x.all_bases_controlled_len()                                                  # needs sage.graphs
    19

We now check that the `C3` algorithm fails for all linear extensions \(l\) of this poset, whereas both the instrumented and controlled `C3` algorithms succeed; along the way, we collect some statistics:

Sage

    sage: L = P.linear_extensions()                                                     # needs sage.graphs
    sage: stats = []
    sage: for l in L:                                                                   # needs sage.graphs sage.modules
    ....:     x = HierarchyElement(10, l.to_poset())
    ....:     try: # Check that x.mro_standard always fails with a ValueError
    ....:         x.mro_standard
    ....:     except ValueError:
    ....:         pass
    ....:     else:
    ....:         assert False
    ....:     assert x.mro            == list(P)
    ....:     assert x.mro_controlled == list(P)
    ....:     assert x.all_bases_len() == 15
    ....:     stats.append(x.all_bases_controlled_len()-x.all_bases_len())

[Python]

    >>> from sage.all import *
    >>> L = P.linear_extensions()                                                     # needs sage.graphs
    >>> stats = []
    >>> for l in L:                                                                   # needs sage.graphs sage.modules
    ...     x = HierarchyElement(Integer(10), l.to_poset())
    ...     try: # Check that x.mro_standard always fails with a ValueError
    ...         x.mro_standard
    ...     except ValueError:
    ...         pass
    ...     else:
    ...         assert False
    ...     assert x.mro            == list(P)
    ...     assert x.mro_controlled == list(P)
    ...     assert x.all_bases_len() == Integer(15)
    ...     stats.append(x.all_bases_controlled_len()-x.all_bases_len())

Depending on the linear extension \(l\) it was necessary to add between one and five bases for control; for example, \(216\) linear extensions required the addition of four bases:

Sage

    sage: sorted(Word(stats).evaluation_sparse())                                       # needs sage.combinat sage.graphs sage.modules
    [(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)]

Python

    >>> from sage.all import *
    >>> sorted(Word(stats).evaluation_sparse())                                       # needs sage.combinat sage.graphs sage.modules
    [(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)]

We now consider a hierarchy of categories:

Sage

    sage: from operator import attrgetter
    sage: x = HierarchyElement(Groups(), attrcall("super_categories"), attrgetter("_cmp_key"))
    sage: x.mro
    [Category of groups, Category of monoids, Category of semigroups,
     Category of inverse unital magmas, Category of unital magmas, Category of magmas,
     Category of sets, Category of sets with partial maps, Category of objects]
    sage: x.mro_standard
    [Category of groups, Category of monoids, Category of semigroups,
     Category of inverse unital magmas, Category of unital magmas, Category of magmas,
     Category of sets, Category of sets with partial maps, Category of objects]

[Python]

    >>> from sage.all import *
    >>> from operator import attrgetter
    >>> x = HierarchyElement(Groups(), attrcall("super_categories"), attrgetter("_cmp_key"))
    >>> x.mro
    [Category of groups, Category of monoids, Category of semigroups,
     Category of inverse unital magmas, Category of unital magmas, Category of magmas,
     Category of sets, Category of sets with partial maps, Category of objects]
    >>> x.mro_standard
    [Category of groups, Category of monoids, Category of semigroups,
     Category of inverse unital magmas, Category of unital magmas, Category of magmas,
     Category of sets, Category of sets with partial maps, Category of objects]

For a typical category, few bases, if any, need to be added to force `C3` to give the desired order:

Sage

    sage: C = FiniteFields()
    sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
    sage: x.mro == x.mro_standard
    False
    sage: x.all_bases_len()
    72
    sage: x.all_bases_controlled_len()
    76

    sage: C = GradedHopfAlgebrasWithBasis(QQ)
    sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
    sage: x._test_mro()
    sage: x.mro == x.mro_standard
    False
    sage: x.all_bases_len()
    114
    sage: x.all_bases_controlled_len()
    117

Python

    >>> from sage.all import *
    >>> C = FiniteFields()
    >>> x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
    >>> x.mro == x.mro_standard
    False
    >>> x.all_bases_len()
    72
    >>> x.all_bases_controlled_len()
    76

    >>> C = GradedHopfAlgebrasWithBasis(QQ)
    >>> x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
    >>> x._test_mro()
    >>> x.mro == x.mro_standard
    False
    >>> x.all_bases_len()
    114
    >>> x.all_bases_controlled_len()
    117

The following can be used to search through the Sage named categories for any that requires the addition of some bases. The output may change a bit when the category hierarchy is changed. As long as the list below does not change radically, it’s fine to just update this doctest:

Sage

    sage: from sage.categories.category import category_sample
    sage: sorted([C for C in category_sample()                                          # needs sage.combinat sage.graphs sage.modules sage.rings.number_field
    ....:         if len(C._super_categories_for_classes) != len(C.super_categories())],
    ....:        key=str)
    [Category of affine Weyl groups,
     Category of fields,
     Category of finite Weyl groups,
     Category of finite dimensional Hopf algebras with basis over Rational Field,
     Category of finite dimensional algebras with basis over Rational Field,
     Category of finite enumerated permutation groups,
     Category of number fields]

[Python]

    >>> from sage.all import *
    >>> from sage.categories.category import category_sample
    >>> sorted([C for C in category_sample()                                          # needs sage.combinat sage.graphs sage.modules sage.rings.number_field
    ...         if len(C._super_categories_for_classes) != len(C.super_categories())],
    ...        key=str)
    [Category of affine Weyl groups,
     Category of fields,
     Category of finite Weyl groups,
     Category of finite dimensional Hopf algebras with basis over Rational Field,
     Category of finite dimensional algebras with basis over Rational Field,
     Category of finite enumerated permutation groups,
     Category of number fields]

AUTHOR:

- Nicolas M. Thiery (2012-09): initial version.

sage.misc.c3_controlled.C3_merge(lists)

  Return the input lists merged using the `C3` algorithm.

  EXAMPLES:

  Sage

      sage: from sage.misc.c3_controlled import C3_merge
      sage: C3_merge([[3,2],[4,3,1]])
      [4, 3, 2, 1]
      sage: C3_merge([[3,2],[4,1]])
      [3, 2, 4, 1]

  Python

      >>> from sage.all import *
      >>> from sage.misc.c3_controlled import C3_merge
      >>> C3_merge([[Integer(3),Integer(2)],[Integer(4),Integer(3),Integer(1)]])
      [4, 3, 2, 1]
      >>> C3_merge([[Integer(3),Integer(2)],[Integer(4),Integer(1)]])
      [3, 2, 4, 1]

  This function is only used for testing and experimenting purposes, but exercised quite some by the other doctests in this file.

  It is an extract of `sage.misc.c3.C3_algorithm()`; the latter could be possibly rewritten to use this one to avoid duplication.

sage.misc.c3_controlled.C3_sorted_merge(lists, key=<cyfunction identity at 0x7f585a9d5390>)

  Return the sorted input lists merged using the `C3` algorithm, with a twist.

  INPUT:

  - `lists` – a non empty list (or iterable) of lists (or iterables), each sorted strictly decreasingly according to `key`

  - `key` – a function

  OUTPUT: a pair `(result, suggestion)`

  `result` is the sorted list obtained by merging the lists in `lists` while removing duplicates, and `suggestion` is a list such that applying `C3` on `lists` with its last list replaced by `suggestion` would return `result`.

  EXAMPLES:

  With the following input, `C3_merge()` returns right away a sorted list:

  Sage

      sage: from sage.misc.c3_controlled import C3_merge
      sage: C3_merge([[2],[1]])
      [2, 1]

  Python

      >>> from sage.all import *
      >>> from sage.misc.c3_controlled import C3_merge
      >>> C3_merge([[Integer(2)],[Integer(1)]])
      [2, 1]

  In that case, `C3_sorted_merge()` returns the same result, with the last line unchanged:

  Sage

      sage: from sage.misc.c3_controlled import C3_sorted_merge
      sage: C3_sorted_merge([[2],[1]])
      ([2, 1], [1])

  [Python]

      >>> from sage.all import *
      >>> from sage.misc.c3_controlled import C3_sorted_merge
      >>> C3_sorted_merge([[Integer(2)],[Integer(1)]])
      ([2, 1], [1])

  On the other hand, with the following input, `C3_merge()` returns a non sorted list:

  Sage

      sage: C3_merge([[1],[2]])
      [1, 2]

  Python

      >>> from sage.all import *
      >>> C3_merge([[Integer(1)],[Integer(2)]])
      [1, 2]

  Then, `C3_sorted_merge()` returns a sorted list, and suggests to replace the last line by `[2,1]`:

  Sage

      sage: C3_sorted_merge([[1],[2]])
      ([2, 1], [2, 1])

  [Python]

      >>> from sage.all import *
      >>> C3_sorted_merge([[Integer(1)],[Integer(2)]])
      ([2, 1], [2, 1])

  And indeed `C3_merge()` now returns the desired result:

  Sage

      sage: C3_merge([[1],[2,1]])
      [2, 1]

  Python

      >>> from sage.all import *
      >>> C3_merge([[Integer(1)],[Integer(2),Integer(1)]])
      [2, 1]

  From now on, we use this little wrapper that checks that `C3_merge()`, with the suggestion of `C3_sorted_merge()`, returns a sorted list:

  Sage

      sage: def C3_sorted_merge_check(lists):
      ....:     result, suggestion = C3_sorted_merge(lists)
      ....:     assert result == C3_merge(lists[:-1] + [suggestion])
      ....:     return result, suggestion

  [Python]

      >>> from sage.all import *
      >>> def C3_sorted_merge_check(lists):
      ...     result, suggestion = C3_sorted_merge(lists)
      ...     assert result == C3_merge(lists[:-Integer(1)] + [suggestion])
      ...     return result, suggestion

  Base cases:

  Sage

      sage: C3_sorted_merge_check([])
      Traceback (most recent call last):
      ...
      ValueError: The input should be a non empty list of lists (or iterables)
      sage: C3_sorted_merge_check([[]])
      ([], [])
      sage: C3_sorted_merge_check([[1]])
      ([1], [1])
      sage: C3_sorted_merge_check([[3,2,1]])
      ([3, 2, 1], [3, 2, 1])
      sage: C3_sorted_merge_check([[1],[1]])
      ([1], [1])
      sage: C3_sorted_merge_check([[3,2,1],[3,2,1]])
      ([3, 2, 1], [3, 2, 1])

  Python

      >>> from sage.all import *
      >>> C3_sorted_merge_check([])
      Traceback (most recent call last):
      ...
      ValueError: The input should be a non empty list of lists (or iterables)
      >>> C3_sorted_merge_check([[]])
      ([], [])
      >>> C3_sorted_merge_check([[Integer(1)]])
      ([1], [1])
      >>> C3_sorted_merge_check([[Integer(3),Integer(2),Integer(1)]])
      ([3, 2, 1], [3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(1)]])
      ([1], [1])
      >>> C3_sorted_merge_check([[Integer(3),Integer(2),Integer(1)],[Integer(3),Integer(2),Integer(1)]])
      ([3, 2, 1], [3, 2, 1])

  Exercise different states for the last line:

  Sage

      sage: C3_sorted_merge_check([[1],[2],[]])
      ([2, 1], [2, 1])
      sage: C3_sorted_merge_check([[1],[2], [1]])
      ([2, 1], [2, 1])

  [Python]

      >>> from sage.all import *
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(2)],[]])
      ([2, 1], [2, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(2)], [Integer(1)]])
      ([2, 1], [2, 1])

  Explore (all?) the different execution branches:

  Sage

      sage: C3_sorted_merge_check([[3,1],[4,2]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      sage: C3_sorted_merge_check([[4,1],[3,2]])
      ([4, 3, 2, 1], [3, 2, 1])
      sage: C3_sorted_merge_check([[3,2],[4,1]])
      ([4, 3, 2, 1], [4, 3, 1])
      sage: C3_sorted_merge_check([[1],[4,3,2]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      sage: C3_sorted_merge_check([[1],[3,2], []])
      ([3, 2, 1], [2, 1])
      sage: C3_sorted_merge_check([[1],[4,3,2], []])
      ([4, 3, 2, 1], [2, 1])
      sage: C3_sorted_merge_check([[1],[4,3,2], [2]])
      ([4, 3, 2, 1], [2, 1])
      sage: C3_sorted_merge_check([[2],[1],[4],[3]])
      ([4, 3, 2, 1], [3, 2, 1])
      sage: C3_sorted_merge_check([[2],[1],[4],[]])
      ([4, 2, 1], [4, 2, 1])
      sage: C3_sorted_merge_check([[2],[1],[3],[4]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      sage: C3_sorted_merge_check([[2],[1],[3,2,1],[3]])
      ([3, 2, 1], [3])
      sage: C3_sorted_merge_check([[2],[1],[2,1],[3]])
      ([3, 2, 1], [3, 2])

  Python

      >>> from sage.all import *
      >>> C3_sorted_merge_check([[Integer(3),Integer(1)],[Integer(4),Integer(2)]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(4),Integer(1)],[Integer(3),Integer(2)]])
      ([4, 3, 2, 1], [3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(3),Integer(2)],[Integer(4),Integer(1)]])
      ([4, 3, 2, 1], [4, 3, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(4),Integer(3),Integer(2)]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(3),Integer(2)], []])
      ([3, 2, 1], [2, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(4),Integer(3),Integer(2)], []])
      ([4, 3, 2, 1], [2, 1])
      >>> C3_sorted_merge_check([[Integer(1)],[Integer(4),Integer(3),Integer(2)], [Integer(2)]])
      ([4, 3, 2, 1], [2, 1])
      >>> C3_sorted_merge_check([[Integer(2)],[Integer(1)],[Integer(4)],[Integer(3)]])
      ([4, 3, 2, 1], [3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(2)],[Integer(1)],[Integer(4)],[]])
      ([4, 2, 1], [4, 2, 1])
      >>> C3_sorted_merge_check([[Integer(2)],[Integer(1)],[Integer(3)],[Integer(4)]])
      ([4, 3, 2, 1], [4, 3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(2)],[Integer(1)],[Integer(3),Integer(2),Integer(1)],[Integer(3)]])
      ([3, 2, 1], [3])
      >>> C3_sorted_merge_check([[Integer(2)],[Integer(1)],[Integer(2),Integer(1)],[Integer(3)]])
      ([3, 2, 1], [3, 2])

  Exercises adding one item when the last list has a single element; the second example comes from an actual poset:

  Sage

      sage: C3_sorted_merge_check([[5,4,2],[4,3],[5,4,1]])
      ([5, 4, 3, 2, 1], [5, 4, 3, 2, 1])
      sage: C3_sorted_merge_check([[6,4,2],[5,3],[6,5,1]])
      ([6, 5, 4, 3, 2, 1], [6, 5, 4, 3, 2, 1])

  [Python]

      >>> from sage.all import *
      >>> C3_sorted_merge_check([[Integer(5),Integer(4),Integer(2)],[Integer(4),Integer(3)],[Integer(5),Integer(4),Integer(1)]])
      ([5, 4, 3, 2, 1], [5, 4, 3, 2, 1])
      >>> C3_sorted_merge_check([[Integer(6),Integer(4),Integer(2)],[Integer(5),Integer(3)],[Integer(6),Integer(5),Integer(1)]])
      ([6, 5, 4, 3, 2, 1], [6, 5, 4, 3, 2, 1])

class sage.misc.c3_controlled.CmpKey

  Bases: `object`

  This class implements the lazy attribute `Category._cmp_key`.

  The comparison key `A._cmp_key` of a category is used to define an (almost) total order on non-join categories by setting, for two categories \(A\) and \(B\), \(A<B\) if `A._cmp_key > B._cmp_key`. This order in turn is used to give a normal form to join’s, and help toward having a consistent method resolution order for parent/element classes.

  The comparison key should satisfy the following properties:

  - If \(A\) is a subcategory of \(B\), then \(A < B\) (so that `A._cmp_key > B._cmp_key`). In particular, `Objects()` is the largest category.

  - If \(A != B\) and taking the join of \(A\) and \(B\) makes sense (e.g. taking the join of `Algebras(GF(5))` and `Algebras(QQ)` does not make sense), then \(A<B\) or \(B<A\).

  The rationale for the inversion above between \(A<B\) and `A._cmp_key > B._cmp_key` is that we want the order to be compatible with inclusion of categories, yet it’s easier in practice to create keys that get bigger and bigger while we go down the category hierarchy.

  This implementation applies to join-irreducible categories (i.e. categories that are not join categories). It returns a pair of integers `(flags, i)`, where `flags` is to be interpreted as a bit vector. The first bit is set if `self` is a facade set. The second bit is set if `self` is finite. And so on. The choice of the flags is adhoc and was primarily crafted so that the order between categories would not change too much upon integration of Issue #13589 and would be reasonably session independent. The number `i` is there to resolve ambiguities; it is session dependent, and is assigned increasingly when new categories are created.

  Note

  This is currently not implemented using a `lazy_attribute` for speed reasons only (the code is in Cython and takes advantage of the fact that Category objects always have a `__dict__` dictionary)

  Todo

  - Handle nicely (covariant) functorial constructions and axioms

  EXAMPLES:

  Sage

      sage: Objects()._cmp_key
      (0, 0)
      sage: SetsWithPartialMaps()._cmp_key
      (0, 1)
      sage: Sets()._cmp_key
      (0, 2)
      sage: Sets().Facade()._cmp_key
      (1, ...)
      sage: Sets().Finite()._cmp_key
      (2, ...)
      sage: Sets().Infinite()._cmp_key
      (4, ...)
      sage: EnumeratedSets()._cmp_key
      (8, ...)
      sage: FiniteEnumeratedSets()._cmp_key
      (10, ...)
      sage: SetsWithGrading()._cmp_key
      (16, ...)
      sage: Posets()._cmp_key
      (32, ...)
      sage: LatticePosets()._cmp_key
      (96, ...)
      sage: Crystals()._cmp_key
      (136, ...)
      sage: AdditiveMagmas()._cmp_key
      (256, ...)
      sage: Magmas()._cmp_key
      (4096, ...)
      sage: CommutativeAdditiveSemigroups()._cmp_key
      (256, ...)
      sage: Rings()._cmp_key
      (225536, ...)
      sage: Algebras(QQ)._cmp_key
      (225536, ...)
      sage: AlgebrasWithBasis(QQ)._cmp_key
      (227584, ...)
      sage: GradedAlgebras(QQ)._cmp_key
      (226560, ...)
      sage: GradedAlgebrasWithBasis(QQ)._cmp_key
      (228608, ...)

  Python

      >>> from sage.all import *
      >>> Objects()._cmp_key
      (0, 0)
      >>> SetsWithPartialMaps()._cmp_key
      (0, 1)
      >>> Sets()._cmp_key
      (0, 2)
      >>> Sets().Facade()._cmp_key
      (1, ...)
      >>> Sets().Finite()._cmp_key
      (2, ...)
      >>> Sets().Infinite()._cmp_key
      (4, ...)
      >>> EnumeratedSets()._cmp_key
      (8, ...)
      >>> FiniteEnumeratedSets()._cmp_key
      (10, ...)
      >>> SetsWithGrading()._cmp_key
      (16, ...)
      >>> Posets()._cmp_key
      (32, ...)
      >>> LatticePosets()._cmp_key
      (96, ...)
      >>> Crystals()._cmp_key
      (136, ...)
      >>> AdditiveMagmas()._cmp_key
      (256, ...)
      >>> Magmas()._cmp_key
      (4096, ...)
      >>> CommutativeAdditiveSemigroups()._cmp_key
      (256, ...)
      >>> Rings()._cmp_key
      (225536, ...)
      >>> Algebras(QQ)._cmp_key
      (225536, ...)
      >>> AlgebrasWithBasis(QQ)._cmp_key
      (227584, ...)
      >>> GradedAlgebras(QQ)._cmp_key
      (226560, ...)
      >>> GradedAlgebrasWithBasis(QQ)._cmp_key
      (228608, ...)

  For backward compatibility we currently want the following comparisons:

  Sage

      sage: EnumeratedSets()._cmp_key > Sets().Facade()._cmp_key
      True
      sage: AdditiveMagmas()._cmp_key > EnumeratedSets()._cmp_key
      True

      sage: Category.join([EnumeratedSets(), Sets().Facade()]).parent_class._an_element_.__module__
      'sage.categories.enumerated_sets'

      sage: CommutativeAdditiveSemigroups()._cmp_key < Magmas()._cmp_key
      True
      sage: VectorSpaces(QQ)._cmp_key < Rings()._cmp_key
      True
      sage: VectorSpaces(QQ)._cmp_key < Magmas()._cmp_key
      True

  [Python]

      >>> from sage.all import *
      >>> EnumeratedSets()._cmp_key > Sets().Facade()._cmp_key
      True
      >>> AdditiveMagmas()._cmp_key > EnumeratedSets()._cmp_key
      True

      >>> Category.join([EnumeratedSets(), Sets().Facade()]).parent_class._an_element_.__module__
      'sage.categories.enumerated_sets'

      >>> CommutativeAdditiveSemigroups()._cmp_key < Magmas()._cmp_key
      True
      >>> VectorSpaces(QQ)._cmp_key < Rings()._cmp_key
      True
      >>> VectorSpaces(QQ)._cmp_key < Magmas()._cmp_key
      True

class sage.misc.c3_controlled.CmpKeyNamed

  Bases: `object`

  This class implements the lazy attribute `CategoryWithParameters._cmp_key`.

  See also

  - `CmpKey`

  - `lazy_attribute`

  - `sage.categories.category.CategoryWithParameters`.

  Note

  - The value of the attribute depends only on the parameters of this category.

  - This is currently not implemented using a `lazy_attribute` for speed reasons only.

  EXAMPLES:

  Sage

      sage: Algebras(GF(3))._cmp_key == Algebras(GF(5))._cmp_key  # indirect doctest
      True
      sage: Algebras(ZZ)._cmp_key != Algebras(GF(5))._cmp_key
      True

  Python

      >>> from sage.all import *
      >>> Algebras(GF(Integer(3)))._cmp_key == Algebras(GF(Integer(5)))._cmp_key  # indirect doctest
      True
      >>> Algebras(ZZ)._cmp_key != Algebras(GF(Integer(5)))._cmp_key
      True

class sage.misc.c3_controlled.HierarchyElement(value, bases, key, from_value)

  Bases: `object`

  A class for elements in a hierarchy.

  This class is for testing and experimenting with various variants of the `C3` algorithm to compute a linear extension of the elements above an element in a hierarchy. Given the topic at hand, we use the following naming conventions. For \(x\) an element of the hierarchy, we call the elements just above \(x\) its bases, and the linear extension of all elements above \(x\) its MRO.

  By convention, the bases are given as lists of instances of `HierarchyElement`, and MROs are given a list of the corresponding values.

  INPUT:

  - `value` – an object

  - `succ` – a successor function, poset or digraph from which one can recover the successors of `value`

  - `key` – a function taking values as input (default: the identity) this function is used to compute comparison keys for sorting elements of the hierarchy.

  Note

  Constructing a `HierarchyElement` immediately constructs the whole hierarchy above it.

  EXAMPLES:

  See the introduction of this module `sage.misc.c3_controlled` for many examples. Here we consider a large example, originally taken from the hierarchy of categories above `HopfAlgebrasWithBasis`:

  Sage

      sage: from sage.misc.c3_controlled import HierarchyElement
      sage: G = DiGraph({                                                             # needs sage.graphs
      ....:     44 :  [43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     43 :  [42, 41, 40, 36, 35, 39, 38, 37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     42 :  [36, 35, 37, 30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     41 :  [40, 36, 35, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     40 :  [36, 35, 32, 31, 30, 29, 28, 27, 26, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     39 :  [38, 37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     38 :  [37, 33, 32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     37 :  [30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     36 :  [35, 30, 29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     35 :  [29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     34 :  [33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     33 :  [32, 31, 30, 29, 28, 27, 26, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     32 :  [31, 30, 29, 28, 27, 26, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     31 :  [30, 29, 28, 27, 26, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     30 :  [29, 28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     29 :  [28, 27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     28 :  [27, 26, 15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     27 :  [15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     26 :  [15, 14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     25 :  [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     24 :  [4, 2, 1, 0],
      ....:     23 :  [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     22 :  [21, 20, 18, 17, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     21 :  [20, 17, 4, 2, 1, 0],
      ....:     20 :  [4, 2, 1, 0],
      ....:     19 :  [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     18 :  [17, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     17 :  [4, 2, 1, 0],
      ....:     16 :  [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     15 :  [14, 12, 11, 9, 8, 5, 3, 2, 1, 0],
      ....:     14 :  [11, 3, 2, 1, 0],
      ....:     13 :  [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     12 :  [11, 9, 8, 5, 3, 2, 1, 0],
      ....:     11 :  [3, 2, 1, 0],
      ....:     10 :  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
      ....:     9 :  [8, 5, 3, 2, 1, 0],
      ....:     8 :  [3, 2, 1, 0],
      ....:     7 :  [6, 5, 4, 3, 2, 1, 0],
      ....:     6 :  [4, 3, 2, 1, 0],
      ....:     5 :  [3, 2, 1, 0],
      ....:     4 :  [2, 1, 0],
      ....:     3 :  [2, 1, 0],
      ....:     2 :  [1, 0],
      ....:     1 :  [0],
      ....:     0 :  [],
      ....:     })

      sage: # needs sage.combinat sage.graphs
      sage: x = HierarchyElement(44, G)
      sage: x.mro
      [44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
      sage: x.cls
      <class '44.cls'>
      sage: x.cls.mro()
      [<class '44.cls'>, <class '43.cls'>, <class '42.cls'>, <class '41.cls'>, <class '40.cls'>, <class '39.cls'>, <class '38.cls'>, <class '37.cls'>, <class '36.cls'>, <class '35.cls'>, <class '34.cls'>, <class '33.cls'>, <class '32.cls'>, <class '31.cls'>, <class '30.cls'>, <class '29.cls'>, <class '28.cls'>, <class '27.cls'>, <class '26.cls'>, <class '25.cls'>, <class '24.cls'>, <class '23.cls'>, <class '22.cls'>, <class '21.cls'>, <class '20.cls'>, <class '19.cls'>, <class '18.cls'>, <class '17.cls'>, <class '16.cls'>, <class '15.cls'>, <class '14.cls'>, <class '13.cls'>, <class '12.cls'>, <class '11.cls'>, <class '10.cls'>, <class '9.cls'>, <class '8.cls'>, <class '7.cls'>, <class '6.cls'>, <class '5.cls'>, <class '4.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <class '0.cls'>, <... 'object'>]

  Python

      >>> from sage.all import *
      >>> from sage.misc.c3_controlled import HierarchyElement
      >>> G = DiGraph({                                                             # needs sage.graphs
      ...     Integer(44) :  [Integer(43), Integer(42), Integer(41), Integer(40), Integer(39), Integer(38), Integer(37), Integer(36), Integer(35), Integer(34), Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(25), Integer(24), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(43) :  [Integer(42), Integer(41), Integer(40), Integer(36), Integer(35), Integer(39), Integer(38), Integer(37), Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(42) :  [Integer(36), Integer(35), Integer(37), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(41) :  [Integer(40), Integer(36), Integer(35), Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(40) :  [Integer(36), Integer(35), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(39) :  [Integer(38), Integer(37), Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(38) :  [Integer(37), Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(37) :  [Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(36) :  [Integer(35), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(35) :  [Integer(29), Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(34) :  [Integer(33), Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(25), Integer(24), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(33) :  [Integer(32), Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(32) :  [Integer(31), Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(31) :  [Integer(30), Integer(29), Integer(28), Integer(27), Integer(26), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(30) :  [Integer(29), Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(29) :  [Integer(28), Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(28) :  [Integer(27), Integer(26), Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(27) :  [Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(26) :  [Integer(15), Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(25) :  [Integer(24), Integer(23), Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(24) :  [Integer(4), Integer(2), Integer(1), Integer(0)],
      ...     Integer(23) :  [Integer(22), Integer(21), Integer(20), Integer(19), Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(22) :  [Integer(21), Integer(20), Integer(18), Integer(17), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(21) :  [Integer(20), Integer(17), Integer(4), Integer(2), Integer(1), Integer(0)],
      ...     Integer(20) :  [Integer(4), Integer(2), Integer(1), Integer(0)],
      ...     Integer(19) :  [Integer(18), Integer(17), Integer(16), Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(18) :  [Integer(17), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(17) :  [Integer(4), Integer(2), Integer(1), Integer(0)],
      ...     Integer(16) :  [Integer(15), Integer(14), Integer(13), Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(15) :  [Integer(14), Integer(12), Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(14) :  [Integer(11), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(13) :  [Integer(12), Integer(11), Integer(10), Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(12) :  [Integer(11), Integer(9), Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(11) :  [Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(10) :  [Integer(9), Integer(8), Integer(7), Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(9) :  [Integer(8), Integer(5), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(8) :  [Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(7) :  [Integer(6), Integer(5), Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(6) :  [Integer(4), Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(5) :  [Integer(3), Integer(2), Integer(1), Integer(0)],
      ...     Integer(4) :  [Integer(2), Integer(1), Integer(0)],
      ...     Integer(3) :  [Integer(2), Integer(1), Integer(0)],
      ...     Integer(2) :  [Integer(1), Integer(0)],
      ...     Integer(1) :  [Integer(0)],
      ...     Integer(0) :  [],
      ...     })

      >>> # needs sage.combinat sage.graphs
      >>> x = HierarchyElement(Integer(44), G)
      >>> x.mro
      [44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
      >>> x.cls
      <class '44.cls'>
      >>> x.cls.mro()
      [<class '44.cls'>, <class '43.cls'>, <class '42.cls'>, <class '41.cls'>, <class '40.cls'>, <class '39.cls'>, <class '38.cls'>, <class '37.cls'>, <class '36.cls'>, <class '35.cls'>, <class '34.cls'>, <class '33.cls'>, <class '32.cls'>, <class '31.cls'>, <class '30.cls'>, <class '29.cls'>, <class '28.cls'>, <class '27.cls'>, <class '26.cls'>, <class '25.cls'>, <class '24.cls'>, <class '23.cls'>, <class '22.cls'>, <class '21.cls'>, <class '20.cls'>, <class '19.cls'>, <class '18.cls'>, <class '17.cls'>, <class '16.cls'>, <class '15.cls'>, <class '14.cls'>, <class '13.cls'>, <class '12.cls'>, <class '11.cls'>, <class '10.cls'>, <class '9.cls'>, <class '8.cls'>, <class '7.cls'>, <class '6.cls'>, <class '5.cls'>, <class '4.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <class '0.cls'>, <... 'object'>]

  all_bases()

    Return the set of all instances of `HierarchyElement` above `self`, `self` included.

    EXAMPLES:

    Sage

        sage: # needs sage.graphs
        sage: from sage.misc.c3_controlled import HierarchyElement
        sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
        sage: HierarchyElement(1, P).all_bases()
        {1}
        sage: HierarchyElement(10, P).all_bases()  # random output
        {10, 5, 2, 1}
        sage: sorted([x.value for x in HierarchyElement(10, P).all_bases()])
        [1, 2, 5, 10]

    Python

        >>> from sage.all import *
        >>> # needs sage.graphs
        >>> from sage.misc.c3_controlled import HierarchyElement
        >>> P = Poset((divisors(Integer(30)), lambda x, y: y.divides(x)), facade=True)
        >>> HierarchyElement(Integer(1), P).all_bases()
        {1}
        >>> HierarchyElement(Integer(10), P).all_bases()  # random output
        {10, 5, 2, 1}
        >>> sorted([x.value for x in HierarchyElement(Integer(10), P).all_bases()])
        [1, 2, 5, 10]

  all_bases_controlled_len()

    Return the cumulated size of the controlled bases of the elements above `self` in the hierarchy.

    EXAMPLES:

    Sage

        sage: from sage.misc.c3_controlled import HierarchyElement
        sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)     # needs sage.graphs
        sage: HierarchyElement(30, P).all_bases_controlled_len()                    # needs sage.graphs
        13

    Python

        >>> from sage.all import *
        >>> from sage.misc.c3_controlled import HierarchyElement
        >>> P = Poset((divisors(Integer(30)), lambda x, y: y.divides(x)), facade=True)     # needs sage.graphs
        >>> HierarchyElement(Integer(30), P).all_bases_controlled_len()                    # needs sage.graphs
        13

  all_bases_len()

    Return the cumulated size of the bases of the elements above `self` in the hierarchy.

    EXAMPLES:

    Sage

        sage: from sage.misc.c3_controlled import HierarchyElement
        sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)     # needs sage.graphs
        sage: HierarchyElement(30, P).all_bases_len()                               # needs sage.graphs
        12

    Python

        >>> from sage.all import *
        >>> from sage.misc.c3_controlled import HierarchyElement
        >>> P = Poset((divisors(Integer(30)), lambda x, y: y.divides(x)), facade=True)     # needs sage.graphs
        >>> HierarchyElement(Integer(30), P).all_bases_len()                               # needs sage.graphs
        12

  bases()

    The bases of `self`.

    The bases are given as a list of instances of `HierarchyElement`, sorted decreasingly according to the `key` function.

    EXAMPLES:

    Sage

        sage: # needs sage.combinat sage.graphs
        sage: from sage.misc.c3_controlled import HierarchyElement
        sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
        sage: x = HierarchyElement(10, P)
        sage: x.bases
        [5, 2]
        sage: type(x.bases[0])
        <class 'sage.misc.c3_controlled.HierarchyElement'>
        sage: x.mro
        [10, 5, 2, 1]
        sage: x._bases_controlled
        [5, 2]

    Python

        >>> from sage.all import *
        >>> # needs sage.combinat sage.graphs
        >>> from sage.misc.c3_controlled import HierarchyElement
        >>> P = Poset((divisors(Integer(30)), lambda x, y: y.divides(x)), facade=True)
        >>> x = HierarchyElement(Integer(10), P)
        >>> x.bases
        [5, 2]
        >>> type(x.bases[Integer(0)])
        <class 'sage.misc.c3_controlled.HierarchyElement'>
        >>> x.mro
        [10, 5, 2, 1]
        >>> x._bases_controlled
        [5, 2]

  cls()

    Return a Python class with inheritance graph parallel to the hierarchy above `self`.

    EXAMPLES:

    Sage

        sage: # needs sage.graphs
        sage: from sage.misc.c3_controlled import HierarchyElement
        sage: P = Poset((divisors(30), lambda x, y: y.divides(x)), facade=True)
        sage: x = HierarchyElement(1, P)
        sage: x.cls
        <class '1.cls'>
        sage: x.cls.mro()
        [<class '1.cls'>, <... 'object'>]
        sage: x = HierarchyElement(30, P)
        sage: x.cls
        <class '30.cls'>
        sage: x.cls.mro()
        [<class '30.cls'>, <class '15.cls'>, <class '10.cls'>, <class '6.cls'>, <class '5.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <... 'object'>]

    Python

        >>> from sage.all import *
        >>> # needs sage.graphs
        >>> from sage.misc.c3_controlled import HierarchyElement
        >>> P = Poset((divisors(Integer(30)), lambda x, y: y.divides(x)), facade=True)
        >>> x = HierarchyElement(Integer(1), P)
        >>> x.cls
        <class '1.cls'>
        >>> x.cls.mro()
        [<class '1.cls'>, <... 'object'>]
        >>> x = HierarchyElement(Integer(30), P)
        >>> x.cls
        <class '30.cls'>
        >>> x.cls.mro()
        [<class '30.cls'>, <class '15.cls'>, <class '10.cls'>, <class '6.cls'>, <class '5.cls'>, <class '3.cls'>, <class '2.cls'>, <class '1.cls'>, <... 'object'>]

  mro()

    The MRO for this object, calculated with `C3_sorted_merge()`.

    EXAMPLES:

    Sage

        sage: # needs sage.graphs
        sage: from sage.misc.c3_controlled import HierarchyElement, C3_sorted_merge, identity
        sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
        sage: x = HierarchyElement(5, P)
        sage: x.mro
        [5, 2, 1]
        sage: x = HierarchyElement(6, P)
        sage: x.mro
        [6, 4, 3]
        sage: x = HierarchyElement(7, P)
        sage: x.mro
        [7, 6, 5, 4, 3, 2, 1]

        sage: C3_sorted_merge([[6, 4, 3], [5, 2, 1], [6, 5]], identity)             # needs sage.graphs
        ([6, 5, 4, 3, 2, 1], [6, 5, 4])

    Python

        >>> from sage.all import *
        >>> # needs sage.graphs
        >>> from sage.misc.c3_controlled import HierarchyElement, C3_sorted_merge, identity
        >>> P = Poset({Integer(7): [Integer(5), Integer(6)], Integer(5): [Integer(1), Integer(2)], Integer(6): [Integer(3), Integer(4)]}, facade=True)
        >>> x = HierarchyElement(Integer(5), P)
        >>> x.mro
        [5, 2, 1]
        >>> x = HierarchyElement(Integer(6), P)
        >>> x.mro
        [6, 4, 3]
        >>> x = HierarchyElement(Integer(7), P)
        >>> x.mro
        [7, 6, 5, 4, 3, 2, 1]

        >>> C3_sorted_merge([[Integer(6), Integer(4), Integer(3)], [Integer(5), Integer(2), Integer(1)], [Integer(6), Integer(5)]], identity)             # needs sage.graphs
        ([6, 5, 4, 3, 2, 1], [6, 5, 4])

  mro_controlled()

    The MRO for this object, calculated with `C3_merge()`, under control of `C3_sorted_merge()`

    EXAMPLES:

    Sage

        sage: from sage.misc.c3_controlled import HierarchyElement, C3_merge

        sage: # needs sage.graphs
        sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
        sage: x = HierarchyElement(5, P)
        sage: x.mro_controlled
        [5, 2, 1]
        sage: x = HierarchyElement(6, P)
        sage: x.mro_controlled
        [6, 4, 3]
        sage: x = HierarchyElement(7, P)
        sage: x.mro_controlled
        [7, 6, 5, 4, 3, 2, 1]
        sage: x._bases
        [6, 5]
        sage: x._bases_controlled
        [6, 5, 4]

        sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5]])
        [6, 4, 3, 5, 2, 1]
        sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5, 4]])
        [6, 5, 4, 3, 2, 1]

    Python

        >>> from sage.all import *
        >>> from sage.misc.c3_controlled import HierarchyElement, C3_merge

        >>> # needs sage.graphs
        >>> P = Poset({Integer(7): [Integer(5), Integer(6)], Integer(5): [Integer(1), Integer(2)], Integer(6): [Integer(3), Integer(4)]}, facade=True)
        >>> x = HierarchyElement(Integer(5), P)
        >>> x.mro_controlled
        [5, 2, 1]
        >>> x = HierarchyElement(Integer(6), P)
        >>> x.mro_controlled
        [6, 4, 3]
        >>> x = HierarchyElement(Integer(7), P)
        >>> x.mro_controlled
        [7, 6, 5, 4, 3, 2, 1]
        >>> x._bases
        [6, 5]
        >>> x._bases_controlled
        [6, 5, 4]

        >>> C3_merge([[Integer(6), Integer(4), Integer(3)], [Integer(5), Integer(2), Integer(1)], [Integer(6), Integer(5)]])
        [6, 4, 3, 5, 2, 1]
        >>> C3_merge([[Integer(6), Integer(4), Integer(3)], [Integer(5), Integer(2), Integer(1)], [Integer(6), Integer(5), Integer(4)]])
        [6, 5, 4, 3, 2, 1]

  mro_standard()

    The MRO for this object, calculated with `C3_merge()`.

    EXAMPLES:

    Sage

        sage: from sage.misc.c3_controlled import HierarchyElement, C3_merge

        sage: # needs sage.graphs
        sage: P = Poset({7: [5, 6], 5: [1, 2], 6: [3, 4]}, facade=True)
        sage: x = HierarchyElement(5, P)
        sage: x.mro_standard
        [5, 2, 1]
        sage: x = HierarchyElement(6, P)
        sage: x.mro_standard
        [6, 4, 3]
        sage: x = HierarchyElement(7, P)
        sage: x.mro_standard
        [7, 6, 4, 3, 5, 2, 1]

        sage: C3_merge([[6, 4, 3], [5, 2, 1], [6, 5]])
        [6, 4, 3, 5, 2, 1]

    Python

        >>> from sage.all import *
        >>> from sage.misc.c3_controlled import HierarchyElement, C3_merge

        >>> # needs sage.graphs
        >>> P = Poset({Integer(7): [Integer(5), Integer(6)], Integer(5): [Integer(1), Integer(2)], Integer(6): [Integer(3), Integer(4)]}, facade=True)
        >>> x = HierarchyElement(Integer(5), P)
        >>> x.mro_standard
        [5, 2, 1]
        >>> x = HierarchyElement(Integer(6), P)
        >>> x.mro_standard
        [6, 4, 3]
        >>> x = HierarchyElement(Integer(7), P)
        >>> x.mro_standard
        [7, 6, 4, 3, 5, 2, 1]

        >>> C3_merge([[Integer(6), Integer(4), Integer(3)], [Integer(5), Integer(2), Integer(1)], [Integer(6), Integer(5)]])
        [6, 4, 3, 5, 2, 1]

sage.misc.c3_controlled.identity(x)

  EXAMPLES:

  Sage

      sage: from sage.misc.c3_controlled import identity
      sage: identity(10)
      10

  Python

      >>> from sage.all import *
      >>> from sage.misc.c3_controlled import identity
      >>> identity(Integer(10))
      10