From 12a7c62e6139bb2dfd166413b205df83efc382af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 17 Aug 2016 16:14:56 +0200 Subject: [PATCH 1/2] py3: getting rid of iterkeys() --- .../letterplace/free_algebra_letterplace.pyx | 2 +- src/sage/categories/category_cy_helper.pyx | 8 +- src/sage/coding/codecan/codecan.pyx | 2 +- src/sage/combinat/finite_state_machine.py | 141 +----------------- .../finite_state_machine_generators.py | 2 +- src/sage/combinat/integer_lists/invlex.pyx | 4 +- .../dynamics/interval_exchanges/template.py | 4 +- src/sage/graphs/base/c_graph.pyx | 2 +- src/sage/graphs/generic_graph.py | 6 +- src/sage/graphs/graph.py | 6 +- .../perm_gps/partn_ref/refinement_graphs.pyx | 6 +- src/sage/libs/gap/util.pyx | 2 +- .../matroids/circuit_closures_matroid.pyx | 2 +- src/sage/misc/mrange.py | 2 +- src/sage/modular/pollack_stevens/manin_map.py | 4 +- src/sage/plot/line.py | 6 +- .../elliptic_curves/gal_reps_number_field.py | 4 +- src/sage/structure/parent.pyx | 2 +- src/sage/structure/unique_representation.py | 2 +- src/sage/symbolic/units.py | 4 +- 20 files changed, 38 insertions(+), 173 deletions(-) diff --git a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx index e78f85ce779..d0f74bd8125 100644 --- a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx @@ -833,7 +833,7 @@ cdef class FreeAlgebra_letterplace(Algebra): if not D: return self.zero() cdef int l - for e in D.iterkeys(): + for e in D: l = len(e) break cdef dict out = {} diff --git a/src/sage/categories/category_cy_helper.pyx b/src/sage/categories/category_cy_helper.pyx index ffc22dd9edc..30b3b903376 100644 --- a/src/sage/categories/category_cy_helper.pyx +++ b/src/sage/categories/category_cy_helper.pyx @@ -110,8 +110,8 @@ cpdef tuple _flatten_categories(categories, ClasscallMetaclass JoinCategory): cdef bint is_supercategory_of_done(new_cat, dict done): # This is a helper function. It replaces the closure - # any(cat.is_subcategory(new_cat) for cat in done.iterkeys()) - for cat in done.iterkeys(): + # any(cat.is_subcategory(new_cat) for cat in done) + for cat in done: if cat.is_subcategory(new_cat): return True return False @@ -193,7 +193,7 @@ cpdef tuple join_as_tuple(tuple categories, tuple axioms, tuple ignore_axioms): new_axioms.add(axiom) # Mark old categories with new axioms as todo - for category in done.iterkeys(): + for category in done: for axiom in new_axioms: todo.add( (category, axiom) ) for new_cat in new_cats: @@ -205,7 +205,7 @@ cpdef tuple join_as_tuple(tuple categories, tuple axioms, tuple ignore_axioms): for axiom in axiomsS.difference(axs): todo.add( (new_cat, axiom) ) - return _sort_uniq(done.iterkeys()) + return _sort_uniq(done) ############################################# diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index 977ce77b947..06ea08c5663 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -297,7 +297,7 @@ cdef class InnerGroup: factor = d.get(self.get_rep(i)) if factor and not factor.is_zero(): m.rescale_row(i, factor) - for i in d.iterkeys(): + for i in d: first_nz_rep = self.join_rows(first_nz_rep, i) # rescale the already fixed part by column multiplications for col in fixed_minimized_cols: diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index dd229935e44..dbbec5ce6ac 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -4695,7 +4695,7 @@ def latex_options(self, self.format_transition_label = format_transition_label if loop_where is not None: - permissible = list(tikz_automata_where.iterkeys()) + permissible = list(tikz_automata_where) for state in self.states(): if hasattr(loop_where, '__call__'): where = loop_where(state.label()) @@ -4714,7 +4714,7 @@ def latex_options(self, (state.label(), permissible)) if initial_where is not None: - permissible = list(tikz_automata_where.iterkeys()) + permissible = list(tikz_automata_where) for state in self.iter_initial_states(): if hasattr(initial_where, '__call__'): where = initial_where(state.label()) @@ -4745,7 +4745,7 @@ def latex_options(self, self.accepting_distance = accepting_distance if accepting_where is not None: - permissible = list(tikz_automata_where.iterkeys()) + permissible = list(tikz_automata_where) for state in self.iter_final_states(): if hasattr(accepting_where, '__call__'): where = accepting_where(state.label()) @@ -14941,7 +14941,6 @@ def result(self, format_output=None): return self._finished_ return [r[:2] + (format_output(r[2]),) for r in self._finished_] - def preview_word(self, track_number=None, length=1, return_word=False): """ Reads a word from the input tape. @@ -14996,140 +14995,6 @@ def preview_word(self, track_number=None, length=1, return_word=False): track_number, length, return_word) - @property - def current_state(self): - """ - The current/reached state in the process. - - .. WARNING:: - - This attribute is deprecated and should not be used any - longer (it may return a wrong result for non-deterministic - finite state machines). - - TESTS:: - - sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, - ....: initial_states=['A'], final_states=['A']) - sage: it = inverter.iter_process(input_tape=[0, 1, 1]) - sage: for current in it: - ....: s = it.current_state - ....: print(current) - ....: print('current state: {}'.format(s)) - doctest:...: DeprecationWarning: This attribute will be - removed in future releases. Use result() at the end of our - iteration or the output of next(). - See http://trac.sagemath.org/16538 for details. - process (1 branch) - + at state 'A' - +-- tape at 1, [[1]] - current state: 'A' - process (1 branch) - + at state 'A' - +-- tape at 2, [[1, 0]] - current state: 'A' - process (1 branch) - + at state 'A' - +-- tape at 3, [[1, 0, 0]] - current state: 'A' - process (0 branches) - current state: None - """ - from sage.misc.superseded import deprecation - deprecation(16538, 'This attribute will be removed in future ' - 'releases. Use result() at the end of our ' - 'iteration or the output of next().') - if not self._current_: - return None - return next(next(self._current_.itervalues()).iterkeys()) - - - @property - def output_tape(self): - """ - The written output. - - .. WARNING:: - - This attribute is deprecated and should not be used any - longer (it may return a wrong result for non-deterministic - finite state machines). - - TESTS:: - - sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, - ....: initial_states=['A'], final_states=['A']) - sage: it = inverter.iter_process(input_tape=[0, 1, 1]) - sage: for current in it: - ....: t = it.output_tape - ....: print(current) - ....: print('output: {}'.format(t)) - doctest:...: DeprecationWarning: This attribute will be removed - in future releases. Use result() at the end of our iteration - or the output of next(). - See http://trac.sagemath.org/16538 for details. - process (1 branch) - + at state 'A' - +-- tape at 1, [[1]] - output: [1] - process (1 branch) - + at state 'A' - +-- tape at 2, [[1, 0]] - output: [1, 0] - process (1 branch) - + at state 'A' - +-- tape at 3, [[1, 0, 0]] - output: [1, 0, 0] - process (0 branches) - output: None - """ - from sage.misc.superseded import deprecation - deprecation(16538, 'This attribute will be removed in future ' - 'releases. Use result() at the end of our iteration ' - 'or the output of next().') - if not self._current_: - return None - return next(next(self._current_.itervalues()).itervalues()).outputs[0] - - - @property - def accept_input(self): - """ - Is ``True`` if the reached state is accepted. This is only available - at the end of the iteration process. - - .. WARNING:: - - This attribute is deprecated and should not be used any - longer (it may return a wrong result for non-deterministic - finite state machines). - - TESTS:: - - sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, - ....: initial_states=['A'], final_states=['A']) - sage: it = inverter.iter_process(input_tape=[0, 1, 1]) - sage: for _ in it: - ....: pass - sage: it.result() - [Branch(accept=True, state='A', output=[1, 0, 0])] - sage: it.accept_input - doctest:...: DeprecationWarning: This attribute will be removed - in future releases. Use result() at the end of our iteration - or the output of next(). - See http://trac.sagemath.org/16538 for details. - True - """ - from sage.misc.superseded import deprecation - deprecation(16538, 'This attribute will be removed in future ' - 'releases. Use result() at the end of our iteration ' - 'or the output of next().') - try: - return self._finished_[0].accept - except KeyError: - raise AttributeError - - #***************************************************************************** diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index 1398a3ae207..4ff2615e59e 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -1977,7 +1977,7 @@ def f(n): if carry >= 0}, multiedges=False) - initial_values_set = set(initial_values.iterkeys()) + initial_values_set = set(initial_values) missing_initial_values = required_initial_values.difference( initial_values_set) diff --git a/src/sage/combinat/integer_lists/invlex.pyx b/src/sage/combinat/integer_lists/invlex.pyx index 28b68771a0d..2dee7535d6b 100644 --- a/src/sage/combinat/integer_lists/invlex.pyx +++ b/src/sage/combinat/integer_lists/invlex.pyx @@ -29,6 +29,8 @@ limitations and lack of robustness w.r.t. input. #***************************************************************************** from __future__ import print_function +from builtins import object + from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall from sage.misc.cachefunc import cached_method from sage.combinat.integer_lists.base cimport IntegerListsBackend @@ -1285,7 +1287,7 @@ class IntegerListsLexIter(object): self._current_sum -= self._current_list[-1] self._current_list.pop() - def next(self): + def __next__(self): r""" Return the next element in the iteration. diff --git a/src/sage/dynamics/interval_exchanges/template.py b/src/sage/dynamics/interval_exchanges/template.py index efdc0f64211..97894ca547a 100644 --- a/src/sage/dynamics/interval_exchanges/template.py +++ b/src/sage/dynamics/interval_exchanges/template.py @@ -3313,7 +3313,7 @@ def __iter__(self): H(0, 0) H(0, 0) """ - for data in self._succ.iterkeys(): + for data in self._succ: yield self._vertex_to_permutation(data) def __contains__(self, element): @@ -3335,7 +3335,7 @@ def __contains__(self, element): sage: q in s True """ - for p in self._succ.iterkeys(): + for p in self._succ: if self._vertex_to_permutation(p) == element: return True diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index 3d11c4ea00f..e0277fb1e82 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -1846,7 +1846,7 @@ cdef class CGraphBackend(GenericGraphBackend): """ cdef int i if verts is None: - S = set(self.vertex_ints.iterkeys()) + S = set(self.vertex_ints) for i in range((self._cg).active_vertices.size): if (i not in self.vertex_labels and bitset_in((self._cg).active_vertices, i)): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 38a860b2fc0..560f3d002cd 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -9777,7 +9777,7 @@ def vertex_iterator(self, vertices=None): 100000 loops, best of 3: 8.85 [micro]s per loop sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop - sage: timeit V = list(P._nxg.adj.iterkeys()) # not tested + sage: timeit V = list(P._nxg.adj) # not tested 100000 loops, best of 3: 3.45 [micro]s per loop In other words, if you want a fast vertex iterator, call the @@ -9856,7 +9856,7 @@ def vertices(self, key=None): 100000 loops, best of 3: 8.85 [micro]s per loop sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop - sage: timeit V = list(P._nxg.adj.iterkeys()) # not tested + sage: timeit V = list(P._nxg.adj) # not tested 100000 loops, best of 3: 3.45 [micro]s per loop We illustrate various ways to use a ``key`` to sort the list:: @@ -19832,7 +19832,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input = True, if len(set(perm.values())) < len(perm): raise NotImplementedError("Non injective relabeling") - for v in perm.iterkeys(): + for v in perm: if v in self: try: hash(perm[v]) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 899d1b7ca1c..1997259c832 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -5499,7 +5499,7 @@ def cliques_number_of(self, vertices=None, cliques=None): {0: 1, 1: 1, 2: 1, 3: 1, 4: 2} sage: F = graphs.Grid2dGraph(2,3) sage: X = F.cliques_number_of() - sage: for v in sorted(X.iterkeys()): + sage: for v in sorted(X): ....: print("{} {}".format(v, X[v])) (0, 0) 2 (0, 1) 3 @@ -5992,7 +5992,7 @@ def cliques_vertex_clique_number(self, algorithm="cliquer", vertices=None, {0: 2, 1: 4, 2: 4, 3: 4, 4: 4} sage: F = graphs.Grid2dGraph(2,3) sage: X = F.cliques_vertex_clique_number(algorithm="networkx") - sage: for v in sorted(X.iterkeys()): + sage: for v in sorted(X): ....: print("{} {}".format(v, X[v])) (0, 0) 2 (0, 1) 2 @@ -6056,7 +6056,7 @@ def cliques_containing_vertex(self, vertices=None, cliques=None): {0: [[0, 4]], 1: [[1, 2, 3, 4]], 2: [[1, 2, 3, 4]], 3: [[1, 2, 3, 4]], 4: [[0, 4], [1, 2, 3, 4]]} sage: F = graphs.Grid2dGraph(2,3) sage: X = F.cliques_containing_vertex() - sage: for v in sorted(X.iterkeys()): + sage: for v in sorted(X): ....: print("{} {}".format(v, X[v])) (0, 0) [[(0, 1), (0, 0)], [(1, 0), (0, 0)]] (0, 1) [[(0, 1), (0, 0)], [(0, 1), (0, 2)], [(0, 1), (1, 1)]] diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index adc8f900343..c5a29def5ae 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -91,7 +91,7 @@ def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=Fal G_in = copy(G_in) to = G_in.relabel(return_map=True) frm = {} - for v in to.iterkeys(): + for v in to: frm[to[v]] = v if first: partition = [[to[v] for v in cell] for cell in partn] @@ -382,7 +382,7 @@ def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certificat G_in = copy(G_in) to = G_in.relabel(return_map=True) frm = {} - for v in to.iterkeys(): + for v in to: frm[to[v]] = v partition = [[to[v] for v in cell] for cell in partition] else: @@ -459,7 +459,7 @@ def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certificat return_tuple = [list_of_gens] if dict_rep: ddd = {} - for v in frm.iterkeys(): + for v in frm: ddd[frm[v]] = v if v != 0 else n return_tuple.append(ddd) if lab: diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index d0df7441e45..25eed9afb8e 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -134,7 +134,7 @@ cdef void gasman_callback(): Callback before each GAP garbage collection """ global owned_objects_refcount - for obj in owned_objects_refcount.iterkeys(): + for obj in owned_objects_refcount: libGAP_MARK_BAG((obj).value) diff --git a/src/sage/matroids/circuit_closures_matroid.pyx b/src/sage/matroids/circuit_closures_matroid.pyx index 20ece775885..3ff711e3a5e 100644 --- a/src/sage/matroids/circuit_closures_matroid.pyx +++ b/src/sage/matroids/circuit_closures_matroid.pyx @@ -156,7 +156,7 @@ cdef class CircuitClosuresMatroid(Matroid): else: self._groundset = frozenset(groundset) self._circuit_closures = {} - for k in circuit_closures.iterkeys(): + for k in circuit_closures: self._circuit_closures[k] = frozenset([frozenset(X) for X in circuit_closures[k]]) self._matroid_rank = self.rank(self._groundset) diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index c073a27c372..c6b92018096 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -694,7 +694,7 @@ def cantor_product(*args, **kwds): elif repeat < 0: raise ValueError("repeat argument cannot be negative") if kwds: - raise TypeError("'{}' is an invalid keyword argument for this function".format(next(kwds.iterkeys()))) + raise TypeError("'{}' is an invalid keyword argument for this function".format(list(kwds)[0])) mm = m * repeat for n in count(0): diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 136dcb77eb7..34f75bdf787 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -688,10 +688,8 @@ def _right_action(self, gamma): (17, -34, 69) """ D = {} - sd = self._dict # we should eventually replace the for loop with a call to apply_many - keys = [ky for ky in sd.iterkeys()] - for ky in keys: + for ky in self._dict: D[ky] = self(gamma * ky) * gamma return self.__class__(self._codomain, self._manin, D, check=False) diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py index e164e0cd869..66beb52e588 100644 --- a/src/sage/plot/line.py +++ b/src/sage/plot/line.py @@ -42,10 +42,10 @@ def __init__(self, xdata, ydata, options): sage: Line([-1,2], [17,4], {'thickness':2}) Line defined by 2 points """ - valid_options = self._allowed_options().keys() - for opt in options.iterkeys(): + valid_options = self._allowed_options() + for opt in options: if opt not in valid_options: - raise RuntimeError("Error in line(): option '%s' not valid."%opt) + raise RuntimeError("Error in line(): option '%s' not valid." % opt) self.xdata = xdata self.ydata = ydata GraphicPrimitive_xydata.__init__(self, options) diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py index 6ec92e8e661..c50b575ab3c 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -658,7 +658,7 @@ def _exceptionals(E, L, patience=1000): unexc = [] # Primes we discover are unexceptional go here. - for l in D.iterkeys(): + for l in D: tr = GF(l)(trace) det = GF(l)(determinant) disc = GF(l)(discriminant) @@ -699,7 +699,7 @@ def _exceptionals(E, L, patience=1000): if (D == {}) or (patience == 0): break - for l in D.iterkeys(): + for l in D: output.append(l) output.sort() diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 33b552cfb80..ea7c6389b15 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -3295,7 +3295,7 @@ cdef bint _may_cache_none(x, y, tag) except -1: # with the only exception of the path from y to x. # See #12969. cdef EltPair P - for P in _coerce_test_dict.iterkeys(): + for P in _coerce_test_dict: if (P.y is y) and (P.x is not x) and (P.tag is tag): return 0 return 1 diff --git a/src/sage/structure/unique_representation.py b/src/sage/structure/unique_representation.py index 73d8908c257..a60ee9f5662 100644 --- a/src/sage/structure/unique_representation.py +++ b/src/sage/structure/unique_representation.py @@ -1109,7 +1109,7 @@ def _clear_cache_(cls): cache = C.__classcall__.cache except AttributeError: pass - for k in cache.iterkeys(): + for k in cache: if issubclass(k[0][0],cls): del_list.append(k) for k in del_list: diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index c3c4dfef2d2..5b7f62fd6d3 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -512,8 +512,8 @@ def evalunitdict(): for k, v in unitdict.iteritems(): for a in v: unit_to_type[a] = k - for w in unitdict.iterkeys(): - for j in unitdict[w].iterkeys(): + for w in unitdict: + for j in unitdict[w]: if isinstance(unitdict[w][j], tuple): unitdict[w][j] = unitdict[w][j][0] value_to_unit[w] = dict(zip(unitdict[w].itervalues(), unitdict[w].iterkeys())) From 4a3b20f54da5c7aceb6a4310d940d48fac213b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 18 Aug 2016 15:47:33 +0200 Subject: [PATCH 2/2] trac 21266 trying to fix doctests in generic_graph color_by_label --- src/sage/graphs/generic_graph.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 560f3d002cd..c74e215a16b 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -17342,14 +17342,14 @@ def _color_by_label(self, format='hex', as_function=False, default_color = "blac The default output is a dictionary assigning edges to colors:: sage: G._color_by_label() - {'#0000ff': [((1,3,2,4), (1,4)(2,3), 3), ..., ((1,3), (1,4,3), 3)], - '#00ff00': [((1,3,2,4), (1,2,4), 2), ..., ((1,3), (1,2,3), 2)], - '#ff0000': [((1,3,2,4), (1,3)(2,4), 1), ..., ((1,3), (1,3,2), 1)]} + {'#0000ff': [((1,3,2,4), (1,4)(2,3), 3), ...], + '#00ff00': [((1,3,2,4), (1,2,4), 2), ...], + '#ff0000': [((1,3,2,4), (1,3)(2,4), 1), ...]} sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) - {'blue': [((1,3,2,4), (1,3)(2,4), 1), ..., ((1,3), (1,3,2), 1)], - 'green': [((1,3,2,4), (1,4)(2,3), 3), ..., ((1,3), (1,4,3), 3)], - 'red': [((1,3,2,4), (1,2,4), 2), ..., ((1,3), (1,2,3), 2)]} + {'blue': [((1,3,2,4), (1,3)(2,4), 1), ...], + 'green': [((1,3,2,4), (1,4)(2,3), 3), ...], + 'red': [((1,3,2,4), (1,2,4), 2), ...]} TESTS: