r"""
Cartan types

Loosely speaking, Dynkin diagrams (or equivalently Cartan matrices)
are graphs which are used to classify root systems, Coxeter and Weyl
groups, Lie algebras, Lie groups, crystals, etc. up to an
isomorphism. *Cartan types* are a standard set of names for those
Dynkin diagrams (see :wikipedia:Dynkin_diagram).

Let us consider, for example, the Cartan type A_4::

sage: T = CartanType(['A', 4])
sage: T
['A', 4]

It is the name of the following Dynkin diagram::

sage: DynkinDiagram(T)
O---O---O---O
1   2   3   4
A4

.. NOTE::

For convenience, the following shortcuts are available::

sage: DynkinDiagram(['A',4])
O---O---O---O
1   2   3   4
A4
sage: DynkinDiagram('A4')
O---O---O---O
1   2   3   4
A4
sage: T.dynkin_diagram()
O---O---O---O
1   2   3   4
A4

See :class:~sage.combinat.root_system.dynkin_diagram.DynkinDiagram
for how to further manipulate Dynkin diagrams.

From this data (the *Cartan datum*), one can construct the associated
root system::

sage: RootSystem(T)
Root system of type ['A', 4]

The associated Weyl group of A_n is the symmetric group S_{n+1}::

sage: W = WeylGroup(T)
sage: W
Weyl Group of type ['A', 4] (as a matrix group acting on the ambient space)
sage: W.cardinality()
120

while the Lie algebra is sl_{n+1}, and the Lie group SL_{n+1}
(TODO: illustrate this once this is implemented).

One may also construct crystals associated to various Dynkin diagrams.
For example::

sage: C = crystals.Letters(T)
sage: C
The crystal of letters for type ['A', 4]
sage: C.list()
[1, 2, 3, 4, 5]

sage: C = crystals.Tableaux(T, shape=[2])
sage: C
The crystal of tableaux of type ['A', 4] and shape(s) [[2]]
sage: C.cardinality()
15

Here is a sample of all the finite irreducible crystallographic Cartan
types::

sage: CartanType.samples(finite = True, crystallographic = True)
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2]]

One can also get latex representations of the crystallographic Cartan
types and their corresponding Dynkin diagrams::

sage: [latex(ct) for ct in CartanType.samples(crystallographic=True)]
[A_{1}, A_{5}, B_{1}, B_{5}, C_{1}, C_{5}, D_{2}, D_{3}, D_{5},
E_6, E_7, E_8, F_4, G_2,
A_{1}^{(1)}, A_{5}^{(1)}, B_{1}^{(1)}, B_{5}^{(1)}, C_{1}^{(1)}, C_{5}^{(1)}, D_{3}^{(1)}, D_{5}^{(1)},
E_6^{(1)}, E_7^{(1)}, E_8^{(1)}, F_4^{(1)}, G_2^{(1)},
BC_{1}^{(2)}, BC_{5}^{(2)},
B_{5}^{(1)\vee}, C_{4}^{(1)\vee}, F_4^{(1)\vee}, G_2^{(1)\vee}, BC_{1}^{(2)\vee}, BC_{5}^{(2)\vee}]
sage: view([DynkinDiagram(ct) for ct in CartanType.samples(crystallographic=True)]) # not tested

Non-crystallographic Cartan types are also partially supported::

sage: CartanType.samples(finite = True, crystallographic = False)
[['I', 5], ['H', 3], ['H', 4]]

In Sage, a Cartan type is used as a database of type-specific
information and algorithms (see e.g. :mod:sage.combinat.root_system.type_A).
This database includes how to construct the Dynkin diagram, the ambient space
for the root system (see :wikipedia:Root_system), and further
mathematical properties::

sage: T.is_finite(), T.is_simply_laced(), T.is_affine(), T.is_crystallographic()
(True, True, False, True)

It will eventually include Coxeter numbers, etc.

In particular, a Sage Cartan type is endowed with a fixed choice of
labels for the nodes of the Dynkin diagram. This choice follows the
conventions of Nicolas Bourbaki, Lie Groups and Lie Algebras: Chapter 4-6,
Elements of Mathematics, Springer (2002). ISBN 978-3540426509. For example::

sage: T = CartanType(['D', 4])
sage: DynkinDiagram(T)
O 4
|
|
O---O---O
1   2   3
D4

sage: E6 = CartanType(['E',6])
sage: DynkinDiagram(E6)
O 2
|
|
O---O---O---O---O
1   3   4   5   6
E6

.. NOTE::

The direction of the arrows is the **opposite** (i.e. the transpose)
of Bourbaki's convention, but agrees with Kac's.

For example, in type C_2, we have::

sage: C2 = DynkinDiagram(['C',2]); C2
O=<=O
1   2
C2
sage: C2.cartan_matrix()
[ 2 -2]
[-1  2]

However Bourbaki would have the Cartan matrix as:

.. MATH::

\begin{bmatrix}
2 & -1 \\
-2 & 2
\end{bmatrix}.

If desired, other node labelling conventions can be achieved. For
example the Kac labelling for type E_6 can be obtained via::

sage: E6.relabel({1:1,2:6,3:2,4:3,5:4,6:5}).dynkin_diagram()
O 6
|
|
O---O---O---O---O
1   2   3   4   5
E6 relabelled by {1: 1, 2: 6, 3: 2, 4: 3, 5: 4, 6: 5}

Contributions implementing other conventions are very welcome.

Another option is to build from scratch a new Dynkin diagram.  The
architecture has been designed to make it fairly easy to add other
labelling conventions. In particular, we strived at choosing type free
algorithms whenever possible, so in principle most features should
remain available even with custom Cartan types. This has not been used
much yet, so some rough corners certainly remain.

Here, we construct the hyperbolic example of Exercise 4.9 p. 57 of
diagram, and add a couple nodes::

sage: g = DynkinDiagram()

Note that the diagonal of the Cartan matrix is already initialized::

sage: g.cartan_matrix()
[2 0 0]
[0 2 0]
[0 0 2]

Then we add a couple edges::

and we get the desired Cartan matrix::

sage: g.cartan_matrix()
[2 0 0]
[0 2 0]
[0 0 2]

Oops, the Cartan matrix did not change! This is because it is cached
for efficiency (see :class:cached_method). In general, a Dynkin
diagram should not be modified after having been used.

.. WARNING:: this is not checked currently

.. TODO:: add a method :meth:set_mutable as, say, for matrices

Here, we can work around this by clearing the cache::

sage: delattr(g, 'cartan_matrix')

Now we get the desired Cartan matrix::

sage: g.cartan_matrix()
[ 2 -1 -1]
[-2  2 -1]
[-1 -1  2]

Note that backward edges have been automatically added::

sage: g.edges()
[(1, 2, 2), (1, 3, 1), (2, 1, 1), (2, 3, 1), (3, 1, 1), (3, 2, 1)]

.. rubric:: Reducible Cartan types

Reducible Cartan types can be specified by passing a sequence
or list of irreducible Cartan types::

sage: CartanType(['A',2],['B',2])
A2xB2
sage: CartanType([['A',2],['B',2]])
A2xB2
sage: CartanType(['A',2],['B',2]).is_reducible()
True

or using the following short hand notation::

sage: CartanType("A2xB2")
A2xB2
sage: CartanType("A2","B2") == CartanType("A2xB2")
True

.. rubric:: Degenerate cases

When possible, type I_n is automatically converted to the isomorphic
crystallographic Cartan types (any reason not to do so?)::

sage: CartanType(["I",1])
A1xA1
sage: CartanType(["I",3])
['A', 2]
sage: CartanType(["I",4])
['C', 2]
sage: CartanType(["I",6])
['G', 2]

The Dynkin diagrams for types B_1, C_1, D_2, and D_3 are
isomorphic to that for A_1, A_1, A_1 \times A_1, and A_3,
respectively. However their natural ambient space realizations (stemming
from the corresponding infinite families of Lie groups) are different.
Therefore, the Cartan types are considered as distinct::

sage: CartanType(['B',1])
['B', 1]
sage: CartanType(['C',1])
['C', 1]
sage: CartanType(['D',2])
['D', 2]
sage: CartanType(['D',3])
['D', 3]

.. rubric:: Affine Cartan types

For affine types, we use the usual conventions for affine Coxeter groups: each affine type
is either untwisted (that is arise from the natural affinisation
of a finite Cartan type)::

sage: CartanType(["A", 4, 1]).dynkin_diagram()
0
O-----------+
|           |
|           |
O---O---O---O
1   2   3   4
A4~
sage: CartanType(["B", 4, 1]).dynkin_diagram()
O 0
|
|
O---O---O=>=O
1   2   3   4
B4~

or dual thereof::

sage: CartanType(["B", 4, 1]).dual().dynkin_diagram()
O 0
|
|
O---O---O=<=O
1   2   3   4
B4~*

or is of type \widetilde{BC}_n (which yields an irreducible, but
nonreduced root system)::

sage: CartanType(["BC", 4, 2]).dynkin_diagram()
O=<=O---O---O=<=O
0   1   2   3   4
BC4~

This includes the two degenerate cases::

sage: CartanType(["A", 1, 1]).dynkin_diagram()
O<=>O
0   1
A1~
sage: CartanType(["BC", 1, 2]).dynkin_diagram()
4
O=<=O
0   1
BC1~

For the user convenience, Kac's notations for twisted affine types are
automatically translated into the previous ones::

sage: CartanType(["A", 9, 2])
['B', 5, 1]^*
sage: CartanType(["A", 9, 2]).dynkin_diagram()
O 0
|
|
O---O---O---O=<=O
1   2   3   4   5
B5~*
sage: CartanType(["A", 10, 2]).dynkin_diagram()
O=<=O---O---O---O=<=O
0   1   2   3   4   5
BC5~
sage: CartanType(["D", 5, 2]).dynkin_diagram()
O=<=O---O---O=>=O
0   1   2   3   4
C4~*
sage: CartanType(["D", 4, 3]).dynkin_diagram()
3
O=>=O---O
2   1   0
G2~* relabelled by {0: 0, 1: 2, 2: 1}
sage: CartanType(["E", 6, 2]).dynkin_diagram()
O---O---O=<=O---O
0   1   2   3   4
F4~*

Additionally one can set the notation global option to use Kac's notation::

sage: CartanType.global_options['notation'] = 'Kac'
sage: CartanType(["A", 9, 2])
['A', 9, 2]
sage: CartanType(["A", 9, 2]).dynkin_diagram()
O 0
|
|
O---O---O---O=<=O
1   2   3   4   5
A9^2
sage: CartanType(["A", 10, 2]).dynkin_diagram()
O=<=O---O---O---O=<=O
0   1   2   3   4   5
A10^2
sage: CartanType(["D", 5, 2]).dynkin_diagram()
O=<=O---O---O=>=O
0   1   2   3   4
D5^2
sage: CartanType(["D", 4, 3]).dynkin_diagram()
3
O=>=O---O
2   1   0
D4^3
sage: CartanType(["E", 6, 2]).dynkin_diagram()
O---O---O=<=O---O
0   1   2   3   4
E6^2
sage: CartanType.global_options['notation'] = 'BC'
"""
#*****************************************************************************
#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
#       Copyright (C) 2008-2009 Nicolas M. Thiery <nthiery at users.sf.net>,
#
#*****************************************************************************
from types import ClassType as classobj
from sage.misc.cachefunc import cached_method
from sage.misc.abstract_method import abstract_method
from sage.misc.lazy_import import LazyImport
from sage.rings.all import ZZ
from sage.structure.sage_object import SageObject
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.global_options import GlobalOptions
from sage.sets.family import Family
from sage.misc.superseded import deprecated_function_alias
from sage.misc.decorators import rename_keyword

# TODO:
# Implement the Kac conventions by relabeling/dual/... of the above
# Implement Coxeter diagrams for non crystallographic

# Intention: we want simultaneously CartanType to be a factory for
# the various subtypes of CartanType_abstract, as in:
#     CartanType(["A",4,1])
# and to behaves as a "module" for some extra utilities:
#     CartanType.samples()
#
# Implementation: CartanType is the unique instance of this class
# CartanTypeFactory. Is there a better/more standard way to do it?

CartanTypeOptions=GlobalOptions(name='cartan_type',  doc=r"""
Sets and displays the global options for Cartan types. If no parameters
are set, then the function returns a copy of the options dictionary.

The options to partitions can be accessed as the method
:obj:CartanType.global_options of
:class:CartanType <CartanTypeFactory>.
""",
end_doc=r"""
EXAMPLES::

sage: ct = CartanType(['D',5,2]); ct
['C', 4, 1]^*
sage: ct.dynkin_diagram()
O=<=O---O---O=>=O
0   1   2   3   4
C4~*
sage: latex(ct)
C_{4}^{(1)\vee}
sage: CartanType.global_options(dual_str='#', dual_latex='\\ast',)
sage: ct
['C', 4, 1]^#
sage: ct.dynkin_diagram()
O=<=O---O---O=>=O
0   1   2   3   4
C4~#
sage: latex(ct)
C_{4}^{(1)\ast}
sage: CartanType.global_options(notation='kac', mark_special_node='both')
sage: ct
['D', 5, 2]
sage: ct.dynkin_diagram()
X=<=O---O---O=>=O
0   1   2   3   4
D5^2
sage: latex(ct)
D_{5}^{(2)}

For type A_{2n}^{(2)\dagger}, the dual string/latex options are
automatically overriden::

sage: dct = CartanType(['A',8,2]).dual(); dct
['A', 8, 2]^+
sage: latex(dct)
A_{8}^{(2)\dagger}
sage: dct.dynkin_diagram()
X=>=O---O---O=>=O
0   1   2   3   4
A8^2+
sage: CartanType.global_options.reset()
""",
notation=dict(default="Stembridge",
description='Specifies which notation Cartan types should use when printed',
values=dict(Stembridge="use Stembridge's notation",
Kac="use Kac's notation"),
case_sensitive=False,
alias=dict(BC="Stembridge", tilde="Stembridge", twisted="Kac")),
dual_str=dict(default="*",
description='The string used for dual Cartan types when printing',
checker=lambda char: isinstance(char,str)),
dual_latex=dict(default="\\vee",
description='The latex used for dual CartanTypes when latexing',
checker=lambda char: isinstance(char,str)),
mark_special_node=dict(default="none",
description="Make the special nodes",
values=dict(none="no markup", latex="only in latex",
printing="only in printing", both="both in latex and printing"),
case_sensitive=False),
special_node_str=dict(default="X",
description="The string used to indicate which node is special when printing",
checker=lambda char: isinstance(char,str)),
latex_relabel=dict(default=True,
description="Indicate in the latex output if a Cartan type has been relabelled",
checker=lambda x: isinstance(x,bool))
)

class CartanTypeFactory(SageObject):
def __call__(self, *args):
"""
Constructs a Cartan type object.

INPUT:

- [letter, rank] -- letter is one of 'A', 'B', 'C', 'D', 'E', 'F', 'G'
and rank is an integer

- [letter, rank, twist] -- letter is one of 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'BC'
and rank and twist are integers

- str -- a string

- object -- a Cartan type, or an object with a Cartan type method

EXAMPLES:

We construct the Cartan type D_4::

sage: d4 = CartanType(['D',4])
sage: d4
['D', 4]

or, for short::

sage: CartanType("D4")
['D', 4]

.. SEEALSO:: :func:~sage.combinat.root_system.cartan_type.CartanType

TESTS:

Check that this is compatible with :class:CartanTypeFolded::

sage: fct = CartanType(['C', 4, 1]).as_folding()
sage: CartanType(fct)
['C', 4, 1]

Check that :trac:13774 is fixed::

sage: CT = CartanType([['A',2]])
sage: CT.is_irreducible()
True
sage: CT.cartan_matrix()
[ 2 -1]
[-1  2]
sage: CT = CartanType(['A2'])
sage: CT.is_irreducible()
True
sage: CartanType('A2')
['A', 2]
"""
if len(args) == 1:
t = args[0]
else:
t = args
if isinstance(t, CartanType_abstract):
return t
if hasattr(t, "cartan_type"):
return t.cartan_type()

if len(t) == 1: # Fix for trac #13774
t = t[0]

if isinstance(t, str):
if "x" in t:
import type_reducible
return type_reducible.CartanType([CartanType(u) for u in t.split("x")])
elif t[-1] == "*":
return CartanType(t[:-1]).dual()
elif t[-1] == "~":
return CartanType(t[:-1]).affine()
else:
return CartanType([t[0], eval(t[1:])])

t = list(t)

if isinstance(t[0], str) and t[1] in ZZ and t[1] >= 0:
letter, n = t[0], t[1]
if len(t) == 2:
if letter == "A":
if n >= 0:
import type_A
return type_A.CartanType(n)
if letter == "B":
if n >= 1:
import type_B
return type_B.CartanType(n)
if letter == "C":
if n >= 1:
import type_C
return type_C.CartanType(n)
if letter == "D":
import type_D
if n >= 2:
return type_D.CartanType(n)
if letter == "E":
if n >= 6 and n <= 8:
import type_E
return type_E.CartanType(n)
if letter == "F":
if n == 4:
import type_F
return type_F.CartanType()
if letter == "G":
if n == 2:
import type_G
return type_G.CartanType()
if letter == "H":
if n in [3, 4]:
import type_H
return type_H.CartanType(n)
if letter == "I":
if n == 1:
return CartanType([["A", 1], ["A", 1]])
if n == 3:
return CartanType(["A", 2])
if n == 4:
return CartanType(["C", 2])
if n == 6:
return CartanType(["G", 2])
if n >= 1:
import type_I
return type_I.CartanType(n)
if len(t) == 3:
if t[2] == 1: # Untwisted affine
if letter == "A":
if n >= 1:
import type_A_affine
return type_A_affine.CartanType(n)
if letter == "B":
if n >= 1:
import type_B_affine
return type_B_affine.CartanType(n)
if letter == "C":
if n >= 1:
import type_C_affine
return type_C_affine.CartanType(n)
if letter == "D":
import type_D_affine
if n >= 3:
return type_D_affine.CartanType(n)
if letter == "E":
if n >= 6 and n <= 8:
import type_E_affine
return type_E_affine.CartanType(n)
if letter == "F":
if n == 4:
import type_F_affine
return type_F_affine.CartanType()
if letter == "G":
if n == 2:
import type_G_affine
return type_G_affine.CartanType()
if t[2] in [2,3]:
if letter == "BC" and t[2] == 2:
if n >= 1:
import type_BC_affine
return type_BC_affine.CartanType(n)
if letter == "A" and t[2] == 2:
if n%2 == 0: # Kac' A_2n^(2)
return CartanType(["BC", ZZ(n/2), 2])
else:        # Kac' A_2n-1^(2)
return CartanType(["B", ZZ((n+1)/2), 1]).dual()
if letter == "D" and t[2] == 2:
return CartanType(["C", n-1, 1]).dual()
if letter == "D" and t[2] == 3 and n == 4:
return CartanType(["G", 2, 1]).dual().relabel([0,2,1])
if letter == "E" and t[2] == 2 and n == 6:
return CartanType(["F", 4, 1]).dual()
raise ValueError("%s is not a valid Cartan type"%t)
import type_reducible
return type_reducible.CartanType([ CartanType(subtype) for subtype in t ])

global_options = CartanTypeOptions

def _repr_(self):
"""
EXAMPLES::

sage: CartanType    # indirect doctest
CartanType
"""
return "CartanType"

@rename_keyword(deprecation=14673, crystalographic='crystallographic')
def samples(self, finite=None, affine=None, crystallographic=None):
"""
Return a sample of the available Cartan types.

INPUT:

- finite -- a boolean or None (default: None)

- affine -- a boolean or None (default: None)

- crystallographic -- a boolean or None (default: None)

The sample contains all the exceptional finite and affine
Cartan types, as well as typical representatives of the
infinite families.

EXAMPLES::

sage: CartanType.samples()
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2], ['I', 5], ['H', 3], ['H', 4],
['A', 1, 1], ['A', 5, 1], ['B', 1, 1], ['B', 5, 1],
['C', 1, 1], ['C', 5, 1], ['D', 3, 1], ['D', 5, 1],
['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['BC', 1, 2], ['BC', 5, 2],
['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2]^*, ['BC', 5, 2]^*]

The finite, affine and crystallographic options allow
respectively for restricting to (non) finite, (non) affine,
and (non) crystallographic Cartan types::

sage: CartanType.samples(finite=True)
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2], ['I', 5], ['H', 3], ['H', 4]]

sage: CartanType.samples(affine=True)
[['A', 1, 1], ['A', 5, 1], ['B', 1, 1], ['B', 5, 1],
['C', 1, 1], ['C', 5, 1], ['D', 3, 1], ['D', 5, 1],
['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['BC', 1, 2], ['BC', 5, 2],
['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2]^*, ['BC', 5, 2]^*]

sage: CartanType.samples(crystallographic=True)
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2],
['A', 1, 1], ['A', 5, 1], ['B', 1, 1], ['B', 5, 1],
['C', 1, 1], ['C', 5, 1], ['D', 3, 1], ['D', 5, 1],
['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['BC', 1, 2], ['BC', 5, 2],
['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2]^*, ['BC', 5, 2]^*]

sage: CartanType.samples(crystallographic=False)
[['I', 5], ['H', 3], ['H', 4]]

.. TODO:: add some reducible Cartan types (suggestions?)

TESTS::

sage: for ct in CartanType.samples(): TestSuite(ct).run()
sage: CartanType.samples(crystalographic=False)
doctest:...: DeprecationWarning: use the option 'crystallographic' instead of 'crystalographic'
See http://trac.sagemath.org/14673 for details.
[['I', 5], ['H', 3], ['H', 4]]
"""
result = self._samples()
if crystallographic is not None:
result = [t for t in result if t.is_crystallographic() == crystallographic ]
if finite is not None:
result = [t for t in result if t.is_finite() == finite]
if affine is not None:
result = [t for t in result if t.is_affine() == affine]
return result

@cached_method
def _samples(self):
"""
Return a sample of all implemented Cartan types.

.. NOTE:: This is intended to be used through :meth:samples.

EXAMPLES::

sage: CartanType._samples()
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2], ['I', 5], ['H', 3], ['H', 4],
['A', 1, 1], ['A', 5, 1], ['B', 1, 1], ['B', 5, 1],
['C', 1, 1], ['C', 5, 1], ['D', 3, 1], ['D', 5, 1],
['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['BC', 1, 2], ['BC', 5, 2],
['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2]^*, ['BC', 5, 2]^*]
"""
finite_crystallographic = \
[CartanType (t)       for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5],
['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5],
["E", 6], ["E", 7], ["E", 8],
["F", 4],
["G", 2]]]

# Support for hand constructed Dynkin diagrams as Cartan types is not yet ready enough for including an example here.
# from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class
# g = DynkinDiagram_class.an_instance()
return finite_crystallographic + \
[CartanType(t)        for t in [["I", 5], ["H", 3], ["H", 4]]] + \
[t.affine()           for t in finite_crystallographic if t.is_irreducible()] + \
[CartanType(t)        for t in [["BC", 1, 2], ["BC", 5, 2]]] + \
[CartanType(t).dual() for t in [["B", 5, 1], ["C", 4, 1], ["F", 4, 1], ["G", 2, 1],["BC", 1, 2], ["BC", 5, 2]]] #+ \
# [ g ]

_colors = {1: 'blue',    -1: 'blue',
2: 'red',     -2: 'red',
3: 'green',   -3: 'green',
4: 'cyan',    -4: 'cyan',
5: 'magenta', -5: 'magenta',
6: 'yellow',  -6: 'yellow'}

@classmethod
def color(cls, i):
"""
Default color scheme for the vertices of a Dynkin diagram (and associated objects)

EXAMPLES::

sage: CartanType.color(1)
'blue'
sage: CartanType.color(2)
'red'
sage: CartanType.color(3)
'green'

The default color is black::

sage: CartanType.color(0)
'black'

Negative indices get the same color as their positive counterparts::

sage: CartanType.color(-1)
'blue'
sage: CartanType.color(-2)
'red'
sage: CartanType.color(-3)
'green'
"""
return cls._colors.get(i, 'black')

CartanType = CartanTypeFactory()

CartanType.__doc__ = __doc__

class CartanType_abstract(object):
r"""
Abstract class for Cartan types

Subclasses should implement:

- :meth:dynkin_diagram()

- :meth:cartan_matrix()

- :meth:is_finite()

- :meth:is_affine()

- :meth:is_irreducible()
"""

def type(self):
r"""
Return the type of self, or None if unknown.

This method should be overridden in any subclass.

EXAMPLES::

sage: from sage.combinat.root_system.cartan_type import CartanType_abstract
sage: C = CartanType_abstract()
sage: C.type() is None
True
"""
return None

"""
Add abstract super-classes to the class of self.

INPUT:

- classes -- an abstract class or tuple thereof

EXAMPLES::

sage: C = CartanType(["A",3,1])
sage: class MyCartanType:
...       def my_method(self):
...           return 'I am here!'
sage: C.__class__
<class 'sage.combinat.root_system.type_A_affine.CartanType_with_superclass_with_superclass'>
sage: C.__class__.__bases__
(<class 'sage.combinat.root_system.type_A_affine.CartanType_with_superclass'>,
<class __main__.MyCartanType at ...>)
sage: C.my_method()
'I am here!'

.. TODO:: Generalize to :class:SageObject?
"""
from sage.structure.dynamic_class import dynamic_class
assert isinstance(classes, (tuple, type, classobj))
if not isinstance(classes, tuple):
classes = (classes,)
bases = (self.__class__,) + classes
self.__class__ = dynamic_class(self.__class__.__name__+"_with_superclass", bases)

def _latex_draw_arrow_tip(self, x, y, rot=0):
r"""
Draw an arrow tip at the point (x, y) rotated by rot

INPUT:

- (x, y) -- the coordinates of a point, in cm

- rot -- an angle, in degrees

This is an internal function used to assist drawing the Dynkin
diagrams. See e.g. :meth:~sage.combinat.root_system.type_B.CartanType._latex_dynkin_diagram.

EXAMPLES::

sage: CartanType(['B',2])._latex_draw_arrow_tip(1, 0, 180)
'\\draw[shift={(1, 0)}, rotate=180] (135 : 0.45cm) -- (0,0) -- (-135 : 0.45cm);\n'
"""
return "\\draw[shift={(%s, %s)}, rotate=%s] (135 : 0.45cm) -- (0,0) -- (-135 : 0.45cm);\n"%(x, y, rot)

@abstract_method
def rank(self):
"""
Return the rank of self.

This is the number of nodes of the associated Coxeter or
Dynkin diagram.

EXAMPLES::

sage: CartanType(['A', 4]).rank()
4
sage: CartanType(['A', 7, 2]).rank()
5
sage: CartanType(['I', 8]).rank()
2
"""
#return len(self.index_set())

@abstract_method
def index_set(self):
"""
Return the index set for self.

This is the list of the nodes of the associated Coxeter or
Dynkin diagram.

EXAMPLES::

sage: CartanType(['A', 3, 1]).index_set()
(0, 1, 2, 3)
sage: CartanType(['D', 4]).index_set()
(1, 2, 3, 4)
sage: CartanType(['A', 7, 2]).index_set()
(0, 1, 2, 3, 4)
sage: CartanType(['A', 7, 2]).index_set()
(0, 1, 2, 3, 4)
sage: CartanType(['A', 6, 2]).index_set()
(0, 1, 2, 3)
sage: CartanType(['D', 6, 2]).index_set()
(0, 1, 2, 3, 4, 5)
sage: CartanType(['E', 6, 1]).index_set()
(0, 1, 2, 3, 4, 5, 6)
sage: CartanType(['E', 6, 2]).index_set()
(0, 1, 2, 3, 4)
sage: CartanType(['A', 2, 2]).index_set()
(0, 1)
sage: CartanType(['G', 2, 1]).index_set()
(0, 1, 2)
sage: CartanType(['F', 4, 1]).index_set()
(0, 1, 2, 3, 4)
"""

# This coloring scheme is used for crystal graphs and will eventually
# be used for Coxeter groups etc. (experimental feature)
_index_set_coloring = {1:"blue", 2:"red", 3:"green"}

@abstract_method(optional = True)
def coxeter_diagram(self):
"""
Return the Coxeter diagram for self.

EXAMPLES::

sage: CartanType(['B',3]).coxeter_diagram()
Graph on 3 vertices
sage: CartanType(['A',3]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 3)]
sage: CartanType(['B',3]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 4)]
sage: CartanType(['G',2]).coxeter_diagram().edges()
[(1, 2, 6)]
sage: CartanType(['F',4]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 4), (3, 4, 3)]
"""

@cached_method
def coxeter_matrix(self):
"""
Return the Coxeter matrix for self.

EXAMPLES::

sage: CartanType(['A', 4]).coxeter_matrix()
[1 3 2 2]
[3 1 3 2]
[2 3 1 3]
[2 2 3 1]
"""
from sage.matrix.constructor import matrix
from sage.rings.all import ZZ
index_set = self.index_set()
reverse = dict((index_set[i], i) for i in range(len(index_set)))
m = matrix(ZZ,len(index_set), lambda i,j: 1 if i==j else 2)
for (i,j,l) in self.coxeter_diagram().edge_iterator():
m[reverse[i], reverse[j]] = l
m[reverse[j], reverse[i]] = l
m.set_immutable()
return m

def dual(self):
"""
Return the dual Cartan type, possibly just as a formal dual.

EXAMPLES::

sage: CartanType(['A',3]).dual()
['A', 3]
sage: CartanType(["B", 3]).dual()
['C', 3]
sage: CartanType(['C',2]).dual()
['B', 2]
sage: CartanType(['D',4]).dual()
['D', 4]
sage: CartanType(['E',8]).dual()
['E', 8]
sage: CartanType(['F',4]).dual()
['F', 4] relabelled by {1: 4, 2: 3, 3: 2, 4: 1}
"""
import type_dual
return type_dual.CartanType(self)

def relabel(self, relabelling):
"""
Return a relabelled copy of this Cartan type.

INPUT:

- type -- a Cartan type

- relabelling -- a function (or a list or dictionary)

OUTPUT:

an isomorphic Cartan type obtained by relabelling the nodes of
the Dynkin diagram. Namely, the node with label i is
relabelled f(i) (or, by f[i] if f is a list or
dictionary).

EXAMPLES::

sage: CartanType(['F',4]).relabel({ 1:4, 2:3, 3:2, 4:1 }).dynkin_diagram()
O---O=>=O---O
4   3   2   1
F4 relabelled by {1: 4, 2: 3, 3: 2, 4: 1}
"""
import type_relabel
return type_relabel.CartanType(self, relabelling)

def is_reducible(self):
"""
Report whether the root system is reducible (i.e. not simple), that
is whether it can be factored as a product of root systems.

EXAMPLES::

sage: CartanType("A2xB3").is_reducible()
True
sage: CartanType(['A',2]).is_reducible()
False
"""
return not self.is_irreducible()

def is_irreducible(self):
"""
Report whether this Cartan type is irreducible (i.e. simple). This
should be overridden in any subclass.

This returns False by default. Derived class should override this
appropriately.

EXAMPLES::

sage: from sage.combinat.root_system.cartan_type import CartanType_abstract
sage: C = CartanType_abstract()
sage: C.is_irreducible()
False
"""
return False

def is_atomic(self):
r"""
This method is usually equivalent to :meth:is_reducible,
except for the Cartan type D_2.

D_2 is not a standard Cartan type. It is equivalent to type
A_1 \times A_1 which is reducible; however the isomorphism
from its ambient space (for the orthogonal group of degree 4)
to that of A_1 \times A_1 is non trivial, and it is useful to
have it.

From a programming point of view its implementation is more
similar to the irreducible types, and so the method
:meth:is_atomic() is supplied.

EXAMPLES::

sage: CartanType("D2").is_atomic()
True
sage: CartanType("D2").is_irreducible()
False

TESTS::

sage: all( T.is_irreducible() == T.is_atomic() for T in CartanType.samples() if T != CartanType("D2"))
True
"""
return self.is_irreducible()

def is_compound(self):
"""
A short hand for not :meth:is_atomic.

TESTS::

sage: all( T.is_compound() == (not T.is_atomic()) for T in CartanType.samples())
True
"""
return not self.is_atomic()

@abstract_method
def is_finite(self):
"""
Return whether this Cartan type is finite.

EXAMPLES::

sage: from sage.combinat.root_system.cartan_type import CartanType_abstract
sage: C = CartanType_abstract()
sage: C.is_finite()
Traceback (most recent call last):
...
NotImplementedError: <abstract method is_finite at ...>

::

sage: CartanType(['A',4]).is_finite()
True
sage: CartanType(['A',4, 1]).is_finite()
False
"""

@abstract_method
def is_affine(self):
"""
Return whether self is affine.

EXAMPLES::

sage: CartanType(['A', 3]).is_affine()
False
sage: CartanType(['A', 3, 1]).is_affine()
True
"""

def is_crystallographic(self):
"""
Return whether this Cartan type is crystallographic.

This returns False by default. Derived class should override this
appropriately.

EXAMPLES::

sage: [ [t, t.is_crystallographic() ] for t in CartanType.samples(finite=True) ]
[[['A', 1], True], [['A', 5], True],
[['B', 1], True], [['B', 5], True],
[['C', 1], True], [['C', 5], True],
[['D', 2], True], [['D', 3], True], [['D', 5], True],
[['E', 6], True], [['E', 7], True], [['E', 8], True],
[['F', 4], True], [['G', 2], True],
[['I', 5], False], [['H', 3], False], [['H', 4], False]]

TESTS::

sage: all(t.is_crystallographic() for t in CartanType.samples(affine=True))
True
sage: t = CartanType(['A',3]); t.is_crystalographic()
See http://trac.sagemath.org/14673 for details.
True
"""
return False

is_crystalographic = deprecated_function_alias(14673, is_crystallographic)

def is_simply_laced(self):
"""
Return whether this Cartan type is simply laced.

This returns False by default. Derived class should override this
appropriately.

EXAMPLES::

sage: [ [t, t.is_simply_laced() ] for t in CartanType.samples() ]
[[['A', 1], True], [['A', 5], True],
[['B', 1], True], [['B', 5], False],
[['C', 1], True], [['C', 5], False],
[['D', 2], True], [['D', 3], True], [['D', 5], True],
[['E', 6], True], [['E', 7], True], [['E', 8], True],
[['F', 4], False], [['G', 2], False], [['I', 5], False], [['H', 3], False], [['H', 4], False],
[['A', 1, 1], False], [['A', 5, 1], True],
[['B', 1, 1], False], [['B', 5, 1], False],
[['C', 1, 1], False], [['C', 5, 1], False],
[['D', 3, 1], True], [['D', 5, 1], True],
[['E', 6, 1], True], [['E', 7, 1], True], [['E', 8, 1], True],
[['F', 4, 1], False], [['G', 2, 1], False],
[['BC', 1, 2], False], [['BC', 5, 2], False],
[['B', 5, 1]^*, False], [['C', 4, 1]^*, False], [['F', 4, 1]^*, False], [['G', 2, 1]^*, False],
[['BC', 1, 2]^*, False], [['BC', 5, 2]^*, False]]
"""
return False

def is_implemented(self):
"""
Check whether the Cartan datum for self is actually implemented.

EXAMPLES::

sage: CartanType(["A",4,1]).is_implemented()
True
sage: CartanType(['H',3]).is_implemented()
True
"""
try:
self.coxeter_diagram()
return True
except Exception:
return False

def root_system(self):
"""
Return the root system associated to self.

EXAMPLES::

sage: CartanType(['A',4]).root_system()
Root system of type ['A', 4]
"""
from sage.combinat.root_system.root_system import RootSystem
return RootSystem(self)

def as_folding(self, folding_of=None, sigma=None):
r"""
Return self realized as a folded Cartan type.

For finite and affine types, this is realized by the Dynkin
diagram foldings:

.. MATH::

\begin{array}{ccl}
C_n^{(1)}, A_{2n}^{(2)}, A_{2n}^{(2)\dagger}, D_{n+1}^{(2)}
& \hookrightarrow & A_{2n-1}^{(1)}, \\
A_{2n-1}^{(2)}, B_n^{(1)} & \hookrightarrow & D_{n+1}^{(1)}, \\
E_6^{(2)}, F_4^{(1)} & \hookrightarrow & E_6^{(1)}, \\
D_4^{(3)}, G_2^{(1)} & \hookrightarrow & D_4^{(1)}, \\
C_n & \hookrightarrow & A_{2n-1}, \\
B_n & \hookrightarrow & D_{n+1}, \\
F_4 & \hookrightarrow & E_6, \\
G_2 & \hookrightarrow & D_4.
\end{array}

For general types, this returns self as a folded type of self
with \sigma as the identity map.

:class:sage.combinat.root_system.type_folded.CartanTypeFolded.

If the optional inputs folding_of and sigma are specified, then
this returns the folded Cartan type of self in folding_of given
by the automorphism sigma.

EXAMPLES::

sage: CartanType(['B', 3, 1]).as_folding()
['B', 3, 1] as a folding of  ['D', 4, 1]
sage: CartanType(['F', 4]).as_folding()
['F', 4] as a folding of  ['E', 6]
sage: CartanType(['BC', 3, 2]).as_folding()
['BC', 3, 2] as a folding of  ['A', 5, 1]
sage: CartanType(['D', 4, 3]).as_folding()
['G', 2, 1]^* relabelled by {0: 0, 1: 2, 2: 1} as a folding of ['D', 4, 1]
"""
from sage.combinat.root_system.type_folded import CartanTypeFolded
if folding_of is None and sigma is None:
return self._default_folded_cartan_type()
if folding_of is None or sigma is None:
raise ValueError("Both folding_of and sigma must be given")
return CartanTypeFolded(self, folding_of, sigma)

def _default_folded_cartan_type(self):
"""
Return the default folded Cartan type.

In general, this just returns self in self with \sigma as
the identity map.

EXAMPLES::

sage: D = CartanMatrix([[2, -3], [-2, 2]]).dynkin_diagram()
sage: D._default_folded_cartan_type()
Dynkin diagram of rank 2 as a folding of  Dynkin diagram of rank 2
"""
from sage.combinat.root_system.type_folded import CartanTypeFolded
return CartanTypeFolded(self, self, [[i] for i in self.index_set()])

global_options = CartanTypeOptions

class CartanType_crystallographic(CartanType_abstract):
"""
An abstract class for crystallographic Cartan types.
"""
# The default value should really be lambda x:x, but sphinx does
# not like it currently (see #14553); since this is an abstract method
# the value won't actually be used, so we put a fake instead.
@abstract_method(optional=True)
def ascii_art(self, label='lambda x: x'):
r"""
Return an ascii art representation of the Dynkin diagram.

INPUT:

- label -- a relabeling function for the nodes (default: the identity)

EXAMPLES::

sage: cartan_type = CartanType(['B',5,1])
sage: print cartan_type.ascii_art()
O 0
|
|
O---O---O---O=>=O
1   2   3   4   5

The label option is useful to visualize various statistics on
the nodes of the Dynkin diagram::

sage: a = cartan_type.col_annihilator(); a
Finite family {0: 1, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2}
sage: print CartanType(['B',5,1]).ascii_art(label=a.__getitem__)
O 1
|
|
O---O---O---O=>=O
1   2   2   2   2
"""

@abstract_method(optional=True)
def _latex_dynkin_diagram(self, label=lambda x: x, node_dist=2):
r"""
Return a latex representation of the Dynkin diagram.

INPUT:

- label -- a relabeling function for the nodes (default: the identity)

- node_dist -- the distance between nodes, in cm

EXAMPLES::

sage: latex(CartanType(['A',4]).dynkin_diagram()) # indirect doctest
\begin{tikzpicture}[scale=0.5]
\draw (-1,0) node[anchor=east] {$A_{4}$};
\draw (0 cm,0) -- (6 cm,0);
\draw[fill=white] (0 cm, 0) circle (.25cm) node[below=4pt]{$1$};
\draw[fill=white] (2 cm, 0) circle (.25cm) node[below=4pt]{$2$};
\draw[fill=white] (4 cm, 0) circle (.25cm) node[below=4pt]{$3$};
\draw[fill=white] (6 cm, 0) circle (.25cm) node[below=4pt]{$4$};
\end{tikzpicture}
"""

@abstract_method
def dynkin_diagram(self):
"""
Return the Dynkin diagram associated with self.

EXAMPLES::

sage: CartanType(['A',4]).dynkin_diagram()
O---O---O---O
1   2   3   4
A4

.. NOTE::

Derived subclasses should typically implement this as a cached
method.
"""

@cached_method
def cartan_matrix(self):
"""
Return the Cartan matrix associated with self.

EXAMPLES::

sage: CartanType(['A',4]).cartan_matrix()
[ 2 -1  0  0]
[-1  2 -1  0]
[ 0 -1  2 -1]
[ 0  0 -1  2]
"""
from sage.combinat.root_system.cartan_matrix import CartanMatrix
return CartanMatrix(self.dynkin_diagram())

@cached_method
def coxeter_diagram(self):
"""
Return the Coxeter diagram for self.

This implementation constructs it from the Dynkin diagram.

.. SEEALSO:: :meth:CartanType_abstract.coxeter_diagram

EXAMPLES::

sage: CartanType(['A',3]).coxeter_diagram()
Graph on 3 vertices
sage: CartanType(['A',3]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 3)]
sage: CartanType(['B',3]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 4)]
sage: CartanType(['G',2]).coxeter_diagram().edges()
[(1, 2, 6)]
sage: CartanType(['F',4]).coxeter_diagram().edges()
[(1, 2, 3), (2, 3, 4), (3, 4, 3)]
sage: CartanType(['A',2,2]).coxeter_diagram().edges()
[(0, 1, +Infinity)]
"""
from sage.rings.infinity import infinity
scalarproducts_to_order = { 0: 2,  1: 3,  2: 4,  3: 6, 4: infinity }
from sage.graphs.graph import Graph
coxeter_diagram = Graph(multiedges=False)
a = self.dynkin_diagram()
I = self.index_set()
for i in I:
for j in a.neighbors_out(i):
# avoid adding the edge twice
if not coxeter_diagram.has_edge(i,j):
return coxeter_diagram

def is_crystallographic(self):
"""
Implements :meth:CartanType_abstract.is_crystallographic
by returning True.

EXAMPLES::

sage: CartanType(['A', 3, 1]).is_crystallographic()
True

TESTS::

sage: t = CartanType(['A',3]); t.is_crystalographic()
See http://trac.sagemath.org/14673 for details.
True
"""
return True

is_crystalographic = deprecated_function_alias(14673, is_crystallographic)

@cached_method
def symmetrizer(self):
"""
Return the symmetrizer of the Cartan matrix of self.

A Cartan matrix M is symmetrizable if there exists a non
trivial diagonal matrix D such that DM is a symmetric
matrix, that is DM = M^tD. In that case, D is unique, up
to a scalar factor for each connected component of the Dynkin
diagram.

This method computes the unique minimal such D with positive
integral coefficients. If D exists, it is returned as a
family. Otherwise None is returned.

The coefficients are coerced to base_ring.

EXAMPLES::

sage: CartanType(["B",5]).symmetrizer()
Finite family {1: 2, 2: 2, 3: 2, 4: 2, 5: 1}

Here is a neat trick to visualize it better::

sage: T = CartanType(["B",5])
sage: print T.ascii_art(T.symmetrizer().__getitem__)
O---O---O---O=>=O
2   2   2   2   1

sage: T = CartanType(["BC",5, 2])
sage: print T.ascii_art(T.symmetrizer().__getitem__)
O=<=O---O---O---O=<=O
1   2   2   2   2   4

Here is the symmetrizer of some reducible Cartan types::

sage: T = CartanType(["D", 2])
sage: print T.ascii_art(T.symmetrizer().__getitem__)
O   O
1   1

sage: T = CartanType(["B",5],["BC",5, 2])
sage: print T.ascii_art(T.symmetrizer().__getitem__)
O---O---O---O=>=O
2   2   2   2   1
O=<=O---O---O---O=<=O
1   2   2   2   2   4

Property: up to an overall scalar factor, this gives the norm
of the simple roots in the ambient space::

sage: T = CartanType(["C",5])
sage: print T.ascii_art(T.symmetrizer().__getitem__)
O---O---O---O=<=O
1   1   1   1   2

sage: alpha = RootSystem(T).ambient_space().simple_roots()
sage: print T.ascii_art(lambda i: alpha[i].scalar(alpha[i]))
O---O---O---O=<=O
2   2   2   2   4
"""
from sage.matrix.constructor import matrix, diagonal_matrix
m = self.cartan_matrix()
n = m.nrows()
M = matrix(ZZ, n, n*n, sparse = True)
for (i,j) in m.nonzero_positions():
M[i, n * i + j]  = m[i,j]
M[j, n * i + j] -= m[j,i]
kern = M.integer_kernel()
c = len(self.dynkin_diagram().connected_components())
if kern.dimension() < c:
# the Cartan matrix is not symmetrizable
return None
assert kern.dimension() == c
# Now the basis contains one vector v per connected component
# C of the Dynkin diagram, or equivalently diagonal block of
# the Cartan matrix. The support of v is exactly that
# connected component, and it symmetrizes the corresponding
# diagonal block of the Cartan matrix. We sum all those vectors.
D = sum(kern.basis())
assert diagonal_matrix(D) * m == m.transpose() * diagonal_matrix(D)
I = self.index_set()
return Family( dict( (I[i], D[i]) for i in range(n) ) )

def index_set_bipartition(self):
r"""
Return a bipartition \{L,R\} of the vertices of the Dynkin diagram.

For i and j both in L (or both in R), the simple
reflections s_i and s_j commute.

Of course, the Dynkin diagram should be bipartite. This is
always the case for all finite types.

EXAMPLES::

sage: CartanType(['A',5]).index_set_bipartition()
(set([1, 3, 5]), set([2, 4]))

sage: CartanType(['A',2,1]).index_set_bipartition()
Traceback (most recent call last):
...
AssertionError: The Dynkin diagram should be bipartite

"""
from sage.graphs.graph import Graph
G = Graph(self.dynkin_diagram())
assert G.is_bipartite(), "The Dynkin diagram should be bipartite"
return G.bipartite_sets()

class CartanType_simply_laced(CartanType_crystallographic):
"""
An abstract class for simply laced Cartan types.
"""
def is_simply_laced(self):
"""
Return whether self is simply laced, which is True.

EXAMPLES::

sage: CartanType(['A',3,1]).is_simply_laced()
True
sage: CartanType(['A',2]).is_simply_laced()
True
"""
return True

def dual(self):
"""
Simply laced Cartan types are self-dual, so return self.

EXAMPLES::

sage: CartanType(["A", 3]).dual()
['A', 3]
sage: CartanType(["A", 3, 1]).dual()
['A', 3, 1]
sage: CartanType(["D", 3]).dual()
['D', 3]
sage: CartanType(["D", 4, 1]).dual()
['D', 4, 1]
sage: CartanType(["E", 6]).dual()
['E', 6]
sage: CartanType(["E", 6, 1]).dual()
['E', 6, 1]
"""
return self

class CartanType_simple(CartanType_abstract):
"""
An abstract class for simple Cartan types.
"""
def is_irreducible(self):
"""
Return whether self is irreducible, which is True.

EXAMPLES::

sage: CartanType(['A', 3]).is_irreducible()
True
"""
return True

class CartanType_finite(CartanType_abstract):
"""
An abstract class for simple affine Cartan types.
"""
def is_finite(self):
"""
EXAMPLES::

sage: CartanType(["A", 3]).is_finite()
True
"""
return True

def is_affine(self):
"""
EXAMPLES::

sage: CartanType(["A", 3]).is_affine()
False
"""
return False

class CartanType_affine(CartanType_simple, CartanType_crystallographic):
"""
An abstract class for simple affine Cartan types
"""

AmbientSpace = LazyImport('sage.combinat.root_system.type_affine', 'AmbientSpace')

def is_finite(self):
"""
EXAMPLES::

sage: CartanType(['A', 3, 1]).is_finite()
False
"""
return False

def is_affine(self):
"""
EXAMPLES::

sage: CartanType(['A', 3, 1]).is_affine()
True
"""
return True

def is_untwisted_affine(self):
"""
Return whether self is untwisted affine

A Cartan type is untwisted affine if it is the canonical
affine extension of some finite type. Every affine type is
either untwisted affine, dual thereof, or of type BC.

EXAMPLES::

sage: CartanType(['A', 3, 1]).is_untwisted_affine()
True
sage: CartanType(['A', 3, 1]).dual().is_untwisted_affine() # this one is self dual!
True
sage: CartanType(['B', 3, 1]).dual().is_untwisted_affine()
False
sage: CartanType(['BC', 3, 2]).is_untwisted_affine()
False
"""
return False

@abstract_method
def special_node(self):
r"""
Return a special node of the Dynkin diagram.

A *special* node is a node of the Dynkin diagram such that
pruning it yields a Dynkin diagram for the associated
classical type (see :meth:classical).

This method returns the label of some special node. This is
usually 0 in the standard conventions.

EXAMPLES::

sage: CartanType(['A', 3, 1]).special_node()
0

The choice is guaranteed to be consistent with the indexing of
the nodes of the classical Dynkin diagram::

sage: CartanType(['A', 3, 1]).index_set()
(0, 1, 2, 3)
sage: CartanType(['A', 3, 1]).classical().index_set()
(1, 2, 3)
"""

@abstract_method
def classical(self):
r"""
Return the classical Cartan type associated with this affine Cartan type.

EXAMPLES::

sage: CartanType(['A', 1, 1]).classical()
['A', 1]
sage: CartanType(['A', 3, 1]).classical()
['A', 3]
sage: CartanType(['B', 3, 1]).classical()
['B', 3]

sage: CartanType(['A', 2, 2]).classical()
['C', 1]
sage: CartanType(['BC', 1, 2]).classical()
['C', 1]
sage: CartanType(['A', 4, 2]).classical()
['C', 2]
sage: CartanType(['BC', 2, 2]).classical()
['C', 2]
sage: CartanType(['A', 10, 2]).classical()
['C', 5]
sage: CartanType(['BC', 5, 2]).classical()
['C', 5]

sage: CartanType(['D', 5, 2]).classical()
['B', 4]
sage: CartanType(['E', 6, 1]).classical()
['E', 6]
sage: CartanType(['G', 2, 1]).classical()
['G', 2]
sage: CartanType(['E', 6, 2]).classical()
['F', 4] relabelled by {1: 4, 2: 3, 3: 2, 4: 1}
sage: CartanType(['D', 4, 3]).classical()
['G', 2]

We check that :meth:classical,
:meth:sage.combinat.root_system.cartan_type.CartanType_crystallographic.dynkin_diagram,
and :meth:.special_node are consistent::

sage: for ct in CartanType.samples(affine = True):
...       g1 = ct.classical().dynkin_diagram()
...       g2 = ct.dynkin_diagram()
...       g2.delete_vertex(ct.special_node())
...       assert sorted(g1.vertices()) == sorted(g2.vertices())
...       assert sorted(g1.edges()) == sorted(g2.edges())

"""

def row_annihilator(self, m = None):
r"""
Return the unique minimal non trivial annihilating linear
combination of \alpha_0, \alpha_1, \ldots, \alpha_n with
nonnegative coefficients (or alternatively, the unique minimal
non trivial annihilating linear combination of the rows of the
Cartan matrix with non-negative coefficients).

Throw an error if the existence of uniqueness does not hold

The optional argument m is for internal use only.

EXAMPLES::

sage: RootSystem(['C',2,1]).cartan_type().acheck()
Finite family {0: 1, 1: 1, 2: 1}
sage: RootSystem(['D',4,1]).cartan_type().acheck()
Finite family {0: 1, 1: 1, 2: 2, 3: 1, 4: 1}
sage: RootSystem(['F',4,1]).cartan_type().acheck()
Finite family {0: 1, 1: 2, 2: 3, 3: 2, 4: 1}
sage: RootSystem(['BC',4,2]).cartan_type().acheck()
Finite family {0: 1, 1: 2, 2: 2, 3: 2, 4: 2}

acheck is a shortcut for row_annihilator::

sage: RootSystem(['BC',4,2]).cartan_type().row_annihilator()
Finite family {0: 1, 1: 2, 2: 2, 3: 2, 4: 2}

FIXME:

- The current implementation assumes that the Cartan matrix
is indexed by [0,1,...], in the same order as the index set.
- This really should be a method of :class:CartanMatrix.
"""
if m is None:
m = self.cartan_matrix()
assert self.index_set() == tuple(range(m.ncols()))
annihilator_basis = m.integer_kernel().gens()
assert(len(annihilator_basis) == 1)
assert(all(coef > 0 for coef in annihilator_basis[0]))

return Family(dict((i,annihilator_basis[0][i])for i in self.index_set()))

acheck = row_annihilator

def col_annihilator(self):
r"""
Return the unique minimal non trivial annihilating linear
combination of \alpha^\vee_0, \alpha^\vee, \ldots, \alpha^\vee with
nonnegative coefficients (or alternatively, the unique minimal
non trivial annihilating linear combination of the columns of the
Cartan matrix with non-negative coefficients).

Throw an error if the existence or uniqueness does not hold

FIXME: the current implementation assumes that the Cartan
matrix is indexed by [0,1,...], in the same order as the
index set.

EXAMPLES::

sage: RootSystem(['C',2,1]).cartan_type().a()
Finite family {0: 1, 1: 2, 2: 1}
sage: RootSystem(['D',4,1]).cartan_type().a()
Finite family {0: 1, 1: 1, 2: 2, 3: 1, 4: 1}
sage: RootSystem(['F',4,1]).cartan_type().a()
Finite family {0: 1, 1: 2, 2: 3, 3: 4, 4: 2}
sage: RootSystem(['BC',4,2]).cartan_type().a()
Finite family {0: 2, 1: 2, 2: 2, 3: 2, 4: 1}

a is a shortcut for col_annihilator::

sage: RootSystem(['BC',4,2]).cartan_type().col_annihilator()
Finite family {0: 2, 1: 2, 2: 2, 3: 2, 4: 1}
"""
return self.row_annihilator(self.cartan_matrix().transpose())

a = col_annihilator

def c(self):
r"""
Returns the family (c_i)_i of integer coefficients defined by
c_i=max(1, a_i/a^vee_i) (see e.g. [FSS07]_ p. 3)

FIXME: the current implementation assumes that the Cartan
matrix is indexed by [0,1,...], in the same order as the
index set.

EXAMPLES::

sage: RootSystem(['C',2,1]).cartan_type().c()
Finite family {0: 1, 1: 2, 2: 1}
sage: RootSystem(['D',4,1]).cartan_type().c()
Finite family {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
sage: RootSystem(['F',4,1]).cartan_type().c()
Finite family {0: 1, 1: 1, 2: 1, 3: 2, 4: 2}
sage: RootSystem(['BC',4,2]).cartan_type().c()
Finite family {0: 2, 1: 1, 2: 1, 3: 1, 4: 1}

TESTS::

sage: CartanType(["B", 3, 1]).c().map(parent)
Finite family {0: Integer Ring, 1: Integer Ring, 2: Integer Ring, 3: Integer Ring}

REFERENCES:

.. [FSS07] G. Fourier, A. Schilling, and M. Shimozono,
Demazure structure inside Kirillov-Reshetikhin crystals,
J. Algebra, Vol. 309, (2007), p. 386-404
http://arxiv.org/abs/math/0605451
"""
a = self.a()
acheck = self.acheck()
return Family(dict((i, max(ZZ(1), a[i] // acheck[i]))
for i in self.index_set()))

def translation_factors(self):
r"""
Returns the translation factors for self. Those are the
smallest factors t_i such that the translation by t_i
\alpha_i maps the fundamental polygon to another polygon in
the alcove picture.

OUTPUT: a dictionary from self.index_set() to \ZZ
(or \QQ for affine type BC)

Those coefficients are all 1 for dual untwisted, and in
particular for simply laced. They coincide with the usual
c_i coefficients (see :meth:c) for untwisted and dual
thereof. See the discussion below for affine type BC.

Note: one usually realizes the alcove picture in the coweight
lattice, with translations by coroots; in that case, one will
use the translation factors for the dual Cartan type.

FIXME: the current implementation assumes that the Cartan
matrix is indexed by [0,1,...], in the same order as the
index set.

EXAMPLES::

sage: CartanType(['C',2,1]).translation_factors()
Finite family {0: 1, 1: 2, 2: 1}
sage: CartanType(['C',2,1]).dual().translation_factors()
Finite family {0: 1, 1: 1, 2: 1}
sage: CartanType(['D',4,1]).translation_factors()
Finite family {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
sage: CartanType(['F',4,1]).translation_factors()
Finite family {0: 1, 1: 1, 2: 1, 3: 2, 4: 2}
sage: CartanType(['BC',4,2]).translation_factors()
Finite family {0: 1, 1: 1, 2: 1, 3: 1, 4: 1/2}

We proceed with systematic tests taken from MuPAD-Combinat's
testsuite::

sage: list(CartanType(["A", 1, 1]).translation_factors())
[1, 1]
sage: list(CartanType(["A", 5, 1]).translation_factors())
[1, 1, 1, 1, 1, 1]
sage: list(CartanType(["B", 5, 1]).translation_factors())
[1, 1, 1, 1, 1, 2]
sage: list(CartanType(["C", 5, 1]).translation_factors())
[1, 2, 2, 2, 2, 1]
sage: list(CartanType(["D", 5, 1]).translation_factors())
[1, 1, 1, 1, 1, 1]
sage: list(CartanType(["E", 6, 1]).translation_factors())
[1, 1, 1, 1, 1, 1, 1]
sage: list(CartanType(["E", 7, 1]).translation_factors())
[1, 1, 1, 1, 1, 1, 1, 1]
sage: list(CartanType(["E", 8, 1]).translation_factors())
[1, 1, 1, 1, 1, 1, 1, 1, 1]
sage: list(CartanType(["F", 4, 1]).translation_factors())
[1, 1, 1, 2, 2]
sage: list(CartanType(["G", 2, 1]).translation_factors())
[1, 3, 1]
sage: list(CartanType(["A", 2, 2]).translation_factors())
[1, 1/2]
sage: list(CartanType(["A", 2, 2]).dual().translation_factors())
[1/2, 1]
sage: list(CartanType(["A", 10, 2]).translation_factors())
[1, 1, 1, 1, 1, 1/2]
sage: list(CartanType(["A", 10, 2]).dual().translation_factors())
[1/2, 1, 1, 1, 1, 1]
sage: list(CartanType(["A", 9, 2]).translation_factors())
[1, 1, 1, 1, 1, 1]
sage: list(CartanType(["D", 5, 2]).translation_factors())
[1, 1, 1, 1, 1]
sage: list(CartanType(["D", 4, 3]).translation_factors())
[1, 1, 1]
sage: list(CartanType(["E", 6, 2]).translation_factors())
[1, 1, 1, 1, 1]

We conclude with a discussion of the appropriate value for
affine type BC. Let us consider the alcove picture realized
in the weight lattice. It is obtained by taking the level-1
affine hyperplane in the weight lattice, and projecting it
along \Lambda_0::

sage: R = RootSystem(["BC",2,2])
sage: alpha = R.weight_space().simple_roots()
sage: alphacheck = R.coroot_space().simple_roots()
sage: Lambda = R.weight_space().fundamental_weights()

Here are the levels of the fundamental weights::

sage: Lambda[0].level(), Lambda[1].level(), Lambda[2].level()
(1, 2, 2)

So the "center" of the fundamental polygon at level 1 is::

sage: O = Lambda[0]
sage: O.level()
1

We take the projection \omega_1 at level 0 of \Lambda_1
as unit vector on the x-axis, and the projection \omega_2
at level 0 of \Lambda_2 as unit vector of the y-axis::

sage: omega1 = Lambda[1]-2*Lambda[0]
sage: omega2 = Lambda[2]-2*Lambda[0]
sage: omega1.level(), omega2.level()
(0, 0)

The projections of the simple roots can be read off::

sage: alpha[0]
2*Lambda[0] - Lambda[1]
sage: alpha[1]
-2*Lambda[0] + 2*Lambda[1] - Lambda[2]
sage: alpha[2]
-2*Lambda[1] + 2*Lambda[2]

Namely \alpha_0 = -\omega_1, \alpha_1 = 2\omega_1 -
\omega_2 and \alpha_2 = -2 \omega_1 + 2 \omega_2.

The reflection hyperplane defined by \alpha_0^\vee goes
through the points O+1/2 \omega_1 and O+1/2 \omega_2::

sage: (O+(1/2)*omega1).scalar(alphacheck[0])
0
sage: (O+(1/2)*omega2).scalar(alphacheck[0])
0

Hence, the fundamental alcove is the triangle (O, O+1/2
\omega_1, O+1/2 \omega_2). By successive reflections, one can
tile the full plane. This induces a tiling of the full plane
by translates of the fundamental polygon.

.. TODO::

Add the picture here, once root system plots in the
weight lattice will be implemented. In the mean time, the
reader may look up the dual picture on Figure 2 of [HST09]_

From this picture, one can read that translations by
\alpha_0, \alpha_1, and 1/2\alpha_2 map the fundamental
polygon to translates of it in the alcove picture, and are
smallest with this property. Hence, the translation factors
for affine type BC are t_0=1, t_1=1, t_2=1/2::

sage: CartanType(['BC',2,2]).translation_factors()
Finite family {0: 1, 1: 1, 2: 1/2}

TESTS::

sage: CartanType(["B", 3, 1]).translation_factors().map(parent)
Finite family {0: Integer Ring, 1: Integer Ring, 2: Integer Ring, 3: Integer Ring}
sage: CartanType(["BC", 3, 2]).translation_factors().map(parent)
Finite family {0: Integer Ring, 1: Integer Ring, 2: Integer Ring, 3: Rational Field}

REFERENCES:

.. [HST09] F. Hivert, A. Schilling, and N. M. Thiery,
Hecke group algebras as quotients of affine Hecke
algebras at level 0, JCT A, Vol. 116, (2009) p. 844-863
http://arxiv.org/abs/0804.3781
"""
a = self.a()
acheck = self.acheck()
if set([1/ZZ(2), 2]).issubset( set(a[i]/acheck[i] for i in self.index_set()) ):
# The test above and the formula below are rather meaningless
# But they detect properly type BC or dual and return the correct value
return Family(dict((i, min(ZZ(1), a[i] / acheck[i]))
for i in self.index_set()))

else:
return self.c()

def _test_dual_classical(self, **options):
r"""
Tests whether the special node of the dual is still the same and whether
the methods dual and classical commute.

TESTS::

sage: C = CartanType(['A',2,2])
sage: C._test_dual_classical()
"""
tester = self._tester(**options)
tester.assertTrue( self.classical().dual() == self.dual().classical() )
tester.assertTrue( self.special_node() == self.dual().special_node() )

##############################################################################
# Concrete base classes

class CartanType_standard_finite(UniqueRepresentation, SageObject, CartanType_finite):
"""
A concrete base class for the finite standard Cartan types.

This includes for example A_3, D_4, or E_8.
"""
def __init__(self, letter, n):
"""
EXAMPLES::

sage: ct = CartanType(['A',4])

TESTS::

sage: TestSuite(ct).run(verbose = True)
running ._test_category() . . . pass
running ._test_not_implemented_methods() . . . pass
running ._test_pickling() . . . pass
"""
#         assert(t[0] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'])
#         assert(t[1] in ZZ and t[1] >= 0)
#         if t[0] in ['B', 'C']:
#             assert(t[1] >= 2)
#         if t[0] == 'D':
#             assert(t[1] >= 3)
#         if t[0] == 'E':
#             assert(t[1] <= 8)
#         if t[0] == 'F':
#             assert(t[1] <= 4)
#         if t[0] == 'G':
#             assert(t[1] <= 2)
#         if t[0] == 'H':
#             assert(t[1] <= 4)
self.letter = letter
self.n = n

# Technical methods
def _repr_(self, compact = False):
"""
TESTS::

sage: ct = CartanType(['A',3])
sage: repr(ct)
"['A', 3]"
sage: ct._repr_(compact=True)
'A3'
"""
format = '%s%s' if compact else "['%s', %s]"
return format%(self.letter, self.n)

def __reduce__(self):
"""
TESTS::

sage: T = CartanType(['D', 4])
sage: T.__reduce__()
(CartanType, ('D', 4))
True

"""
from cartan_type import CartanType
return (CartanType, (self.letter, self.n))

def __cmp__(self, other):
"""
TESTS::

sage: ct1 = CartanType(['A',4])
sage: ct2 = CartanType(['A',4])
sage: ct3 = CartanType(['A',5])
sage: ct1 == ct2
True
sage: ct1 != ct3
True
"""
if other.__class__ != self.__class__:
return cmp(self.__class__, other.__class__)
if other.letter != self.letter:
return cmp(self.letter, other.letter)
return cmp(self.n, other.n)

def __hash__(self):
"""
EXAMPLES::

sage: ct = CartanType(['A',2])
sage: hash(ct) #random
-5684143898951441983
"""
return hash((self.n, self.letter))

def __getitem__(self, i):
"""
EXAMPLES::

sage: t = CartanType(['A', 3, 1])
sage: t[0]
'A'
sage: t[1]
3
sage: t[2]
1
sage: t[3]
Traceback (most recent call last):
...
IndexError: index out of range
"""
if i == 0:
return self.letter
elif i==1:
return self.n
else:
raise IndexError("index out of range")

def __len__(self):
"""
EXAMPLES::

sage: len(CartanType(['A',4]))
2
"""
return 2

# mathematical methods

def index_set(self):
"""
Implements :meth:CartanType_abstract.index_set.

The index set for all standard finite Cartan types is of the form
\{1, \ldots, n\}. (See :mod:~sage.combinat.root_system.type_I
for a slight abuse of this).

EXAMPLES::

sage: CartanType(['A', 5]).index_set()
(1, 2, 3, 4, 5)
"""
return tuple(range(1,self.n+1))

def rank(self):
"""
Return the rank of self which for type X_n is n.

EXAMPLES::

sage: CartanType(['A', 3]).rank()
3
sage: CartanType(['B', 3]).rank()
3
sage: CartanType(['C', 3]).rank()
3
sage: CartanType(['D', 4]).rank()
4
sage: CartanType(['E', 6]).rank()
6
"""
return self.n

def affine(self):
"""
Return the corresponding untwisted affine Cartan type.

EXAMPLES::

sage: CartanType(['A',3]).affine()
['A', 3, 1]
"""
return CartanType([self.letter, self.n, 1])

def type(self):
"""
Returns the type of self.

EXAMPLES::

sage: CartanType(['A', 4]).type()
'A'
sage: CartanType(['A', 4, 1]).type()
'A'
"""
return self.letter

##########################################################################
class CartanType_standard_affine(UniqueRepresentation, SageObject, CartanType_affine):
r"""
A concrete class for affine simple Cartan types.
"""

def __init__(self, letter, n, affine = 1):
"""
EXAMPLES::

sage: ct = CartanType(['A',4,1])
sage: TestSuite(ct).run()

TESTS::

sage: ct1 = CartanType(['A',3, 1])
sage: ct2 = CartanType(['B',3, 1])
sage: ct3 = CartanType(['A',3])
sage: ct1 == ct1
True
sage: ct1 == ct2
False
sage: ct1 == ct3
False

"""
assert(letter in ['A', 'B', 'C', 'BC', 'D', 'E', 'F', 'G'])
self.letter = letter
self.n = n
self.affine = affine

def _repr_(self, compact = False):
"""
TESTS::

sage: ct = CartanType(['A',3, 1])
sage: repr(ct)
"['A', 3, 1]"
sage: ct._repr_(compact=True)
'A3~'
"""
letter = self.letter
n = self.n
aff = self.affine
if self.global_options('notation') == "Kac":
if letter == 'BC':
letter = 'A'
n *= 2
if compact:
return '%s%s^%s'%(letter, n, aff)
if compact:
return '%s%s~'%(letter, n)
else:
return "['%s', %s, %s]"%(letter, n, aff)

def __reduce__(self):
"""
TESTS::

sage: T = CartanType(['D', 4, 1])
sage: T.__reduce__()
(CartanType, ('D', 4, 1))
True

"""
from sage.combinat.root_system.cartan_type import CartanType
return (CartanType, (self.letter, self.n, self.affine))

def __len__(self):
"""
EXAMPLES::

sage: len(CartanType(['A',4,1]))
3
"""
return 3

def __getitem__(self, i):
"""
EXAMPLES::

sage: t = CartanType(['A', 3, 1])
sage: t[0]
'A'
sage: t[1]
3
sage: t[2]
1
sage: t[3]
Traceback (most recent call last):
...
IndexError: index out of range
"""
if i == 0:
return self.letter
elif i==1:
return self.n
elif i == 2:
return self.affine
else:
raise IndexError("index out of range")

def rank(self):
"""
Return the rank of self which for type X_n^{(1)} is n + 1.

EXAMPLES::

sage: CartanType(['A', 4, 1]).rank()
5
sage: CartanType(['B', 4, 1]).rank()
5
sage: CartanType(['C', 3, 1]).rank()
4
sage: CartanType(['D', 4, 1]).rank()
5
sage: CartanType(['E', 6, 1]).rank()
7
sage: CartanType(['E', 7, 1]).rank()
8
sage: CartanType(['F', 4, 1]).rank()
5
sage: CartanType(['G', 2, 1]).rank()
3
sage: CartanType(['A', 2, 2]).rank()
2
sage: CartanType(['A', 6, 2]).rank()
4
sage: CartanType(['A', 7, 2]).rank()
5
sage: CartanType(['D', 5, 2]).rank()
5
sage: CartanType(['E', 6, 2]).rank()
5
sage: CartanType(['D', 4, 3]).rank()
3
"""
return self.n+1

def index_set(self):
r"""
Implements :meth:CartanType_abstract.index_set.

The index set for all standard affine Cartan types is of the form
\{0, \ldots, n\}.

EXAMPLES::

sage: CartanType(['A', 5, 1]).index_set()
(0, 1, 2, 3, 4, 5)
"""
return tuple(range(self.n+1))

def special_node(self):
r"""
Implement :meth:CartanType_abstract.special_node.

With the standard labelling conventions, 0 is always a
special node.

EXAMPLES::

sage: CartanType(['A', 3, 1]).special_node()
0
"""
return 0

def type(self):
"""
Return the type of self.

EXAMPLES::

sage: CartanType(['A', 4, 1]).type()
'A'
"""
return self.letter

##########################################################################
class CartanType_standard_untwisted_affine(CartanType_standard_affine):
r"""
A concrete class for the standard untwisted affine Cartan types.
"""
def classical(self):
r"""
Return the classical Cartan type associated with self.

EXAMPLES::

sage: CartanType(['A', 3, 1]).classical()
['A', 3]
sage: CartanType(['B', 3, 1]).classical()
['B', 3]
sage: CartanType(['C', 3, 1]).classical()
['C', 3]
sage: CartanType(['D', 4, 1]).classical()
['D', 4]
sage: CartanType(['E', 6, 1]).classical()
['E', 6]
sage: CartanType(['F', 4, 1]).classical()
['F', 4]
sage: CartanType(['G', 2, 1]).classical()
['G', 2]
"""
return CartanType([self.letter,self.n])

def is_untwisted_affine(self):
"""
Implement :meth:CartanType_affine.is_untwisted_affine by
returning True.

EXAMPLES::

sage: CartanType(['B', 3, 1]).is_untwisted_affine()
True

"""
return True

def _latex_(self):
r"""
Return a latex representation of self.

EXAMPLES::

sage: latex(CartanType(['B',4,1]))
B_{4}^{(1)}
sage: latex(CartanType(['C',4,1]))
C_{4}^{(1)}
sage: latex(CartanType(['D',4,1]))
D_{4}^{(1)}
sage: latex(CartanType(['F',4,1]))
F_4^{(1)}
sage: latex(CartanType(['G',2,1]))
G_2^{(1)}
"""
return self.classical()._latex_()+"^{(1)}"

##############################################################################
# For backward compatibility
class CartanType_simple_finite(object):
def __setstate__(self, dict):
"""
Implements the unpickling of Cartan types pickled by Sage <= 4.0.

EXAMPLES:

This is the pickle for CartanType(["A", 4])::

sage: pg_CartanType_simple_finite = unpickle_global('sage.combinat.root_system.cartan_type', 'CartanType_simple_finite')
sage: si1 = unpickle_newobj(pg_CartanType_simple_finite, ())
sage: pg_unpickleModule = unpickle_global('twisted.persisted.styles', 'unpickleModule')
sage: pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer')
sage: si2 = pg_make_integer('4')
sage: unpickle_build(si1, {'tools':pg_unpickleModule('sage.combinat.root_system.type_A'), 't':['A', si2], 'letter':'A', 'n':si2})

sage: si1
['A', 4]
sage: si1.dynkin_diagram()
O---O---O---O
1   2   3   4
A4

This is quite hacky; in particular unique representation is not preserved::

sage: si1 == CartanType(["A", 4]) # todo: not implemented
True
"""
T = CartanType([dict['letter'], dict['n']])
self.__class__ = T.__class__
self.__dict__ = T.__dict__