# -*- coding: utf-8 -*-
r"""
Library of commonly used, famous, or interesting polytopes

This module gathers several constructors of polytopes that can be reached
through ``polytopes.<tab>``. For example, here is the hypercube in dimension 5::

    sage: polytopes.hypercube(5)
    A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 32 vertices

The following constructions are available

.. csv-table::
    :class: contentstable
    :widths: 30
    :delim: |

    :meth:`~sage.geometry.polyhedron.library.Polytopes.Birkhoff_polytope`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.associahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.bitruncated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.buckyball`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cross_polytope`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cube`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cuboctahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.cyclic_polytope`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.dodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.flow_polytope`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.Gosset_3_21`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.grand_antiprism`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.great_rhombicuboctahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.hypercube`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.hypersimplex`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.icosahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.icosidodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.Kirkman_icosahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.octahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.parallelotope`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.pentakis_dodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.permutahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.generalized_permutahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.regular_polygon`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.rhombic_dodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.rhombicosidodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.runcinated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.simplex`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.small_rhombicuboctahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.snub_cube`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.snub_dodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.tetrahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_cube`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_dodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_icosidodecahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_tetrahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_octahedron`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_one_hundred_twenty_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_six_hundred_cell`
    :meth:`~sage.geometry.polyhedron.library.Polytopes.twenty_four_cell`
"""
########################################################################
#       Copyright (C) 2008 Marshall Hampton <hamptonio@gmail.com>
#                     2011 Volker Braun <vbraun.name@gmail.com>
#                     2015 Vincent Delecroix <20100.delecroix@gmail.com>
#                     2019 Jean-Philippe Labbé <labbe@math.fu-berlin.de>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#
#                  https://www.gnu.org/licenses/
########################################################################
from __future__ import absolute_import, division

import itertools

from sage.rings.all import ZZ, QQ, RDF, RR, AA, QQbar
from sage.combinat.permutation import Permutations
from sage.groups.perm_gps.permgroup_named import AlternatingGroup
from .constructor import Polyhedron
from sage.graphs.digraph import DiGraph
from sage.combinat.root_system.associahedron import Associahedron


def zero_sum_projection(d, base_ring=RDF):
    r"""
    Return a matrix corresponding to the projection on the orthogonal of
    `(1,1,\ldots,1)` in dimension `d`.

    The projection maps the orthonormal basis `(1,-1,0,\ldots,0) / \sqrt(2)`,
    `(1,1,-1,0,\ldots,0) / \sqrt(3)`, \ldots, `(1,1,\ldots,1,-1) / \sqrt(d)` to
    the canonical basis in `\RR^{d-1}`.

    OUTPUT:

    A matrix of dimensions `(d-1)\times d` defined over ``base_ring`` (default:
    :class:`RDF <sage.rings.real_double.RealDoubleField_class>`).

    EXAMPLES::

        sage: from sage.geometry.polyhedron.library import zero_sum_projection
        sage: zero_sum_projection(2)
        [ 0.7071067811865475 -0.7071067811865475]
        sage: zero_sum_projection(3)
        [ 0.7071067811865475 -0.7071067811865475                 0.0]
        [ 0.4082482904638631  0.4082482904638631 -0.8164965809277261]

    Exact computation in :class:`AA <sage.rings.qqbar.AlgebraicRealField>`::

        sage: zero_sum_projection(3, base_ring=AA)
        [ 0.7071067811865475? -0.7071067811865475?                    0]
        [ 0.4082482904638630?  0.4082482904638630? -0.8164965809277260?]

    """
    from sage.matrix.constructor import matrix
    from sage.modules.free_module_element import vector
    basis = [vector(base_ring, [1]*i + [-i] + [0]*(d-i-1)) for i in range(1, d)]
    return matrix(base_ring, [v / v.norm() for v in basis])


def project_points(*points, **kwds):
    """
    Projects a set of points into a vector space of dimension one less.

    INPUT:

    - ``points``... -- the points to project.

    - ``base_ring`` -- (defaults to ``RDF`` if keyword is ``None`` or not
      provided in ``kwds``) the base ring to use.

    The projection is isometric to the orthogonal projection on the hyperplane
    made of zero sum vector. Hence, if the set of points have all equal sums,
    then their projection is isometric (as a set of points).

    The projection used is the matrix given by :func:`zero_sum_projection`.

    EXAMPLES::

        sage: from sage.geometry.polyhedron.library import project_points
        sage: project_points([2,-1,3,2])          # abs tol 1e-15
        [(2.1213203435596424, -2.041241452319315, -0.577350269189626)]
        sage: project_points([1,2,3],[3,3,5])     # abs tol 1e-15
        [(-0.7071067811865475, -1.2247448713915892), (0.0, -1.6329931618554523)]

    These projections are compatible with the restriction. More precisely,
    given a vector `v`, the projection of `v` restricted to the first `i`
    coordinates will be equal to the projection of the first `i+1` coordinates
    of `v`::

        sage: project_points([1,2])    # abs tol 1e-15
        [(-0.7071067811865475)]
        sage: project_points([1,2,3])  # abs tol 1e-15
        [(-0.7071067811865475, -1.2247448713915892)]
        sage: project_points([1,2,3,4])     # abs tol 1e-15
        [(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776)]
        sage: project_points([1,2,3,4,0])   # abs tol 1e-15
        [(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776, 2.23606797749979)]

    Check that it is (almost) an isometry::

        sage: V = list(map(vector, IntegerVectors(n=5,length=3)))
        sage: P = project_points(*V)
        sage: for i in range(21):
        ....:     for j in range(21):
        ....:         assert abs((V[i]-V[j]).norm() - (P[i]-P[j]).norm()) < 0.00001

    Example with exact computation::

        sage: V = [ vector(v) for v in IntegerVectors(n=4,length=2) ]
        sage: P = project_points(*V, base_ring=AA)
        sage: for i in range(len(V)):
        ....:     for j in range(len(V)):
        ....:         assert (V[i]-V[j]).norm() == (P[i]-P[j]).norm()

    """
    if not points:
        return []
    base_ring = kwds.pop('base_ring', None)
    if base_ring is None:
        base_ring = RDF
    from sage.modules.free_module_element import vector
    vecs = [vector(base_ring, p) for p in points]
    m = zero_sum_projection(len(vecs[0]), base_ring=base_ring)
    return [m * v for v in vecs]


class Polytopes():
    """
    A class of constructors for commonly used, famous, or interesting
    polytopes.
    """

    def regular_polygon(self, n, exact=True, base_ring=None, backend=None):
        """
        Return a regular polygon with `n` vertices.

        INPUT:

        - ``n`` -- a positive integer, the number of vertices.

        - ``exact`` -- (boolean, default ``True``) if ``False`` floating point
          numbers are used for coordinates.

        - ``base_ring`` -- a ring in which the coordinates will lie. It is
          ``None`` by default. If it is not provided and ``exact`` is ``True``
          then it will be the field of real algebraic number, if ``exact`` is
          ``False`` it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: octagon = polytopes.regular_polygon(8)
            sage: octagon
            A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
            sage: octagon.n_vertices()
            8
            sage: v = octagon.volume()
            sage: v
            2.828427124746190?
            sage: v == 2*QQbar(2).sqrt()
            True

        Its non exact version::

            sage: polytopes.regular_polygon(3, exact=False).vertices()
            (A vertex at (0.0, 1.0),
             A vertex at (0.8660254038, -0.5),
             A vertex at (-0.8660254038, -0.5))
            sage: polytopes.regular_polygon(25, exact=False).n_vertices()
            25

        TESTS::

            sage: octagon = polytopes.regular_polygon(8, backend='normaliz')  # optional - pynormaliz
            sage: octagon                                                     # optional - pynormaliz
            A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
            sage: octagon.n_vertices()                                        # optional - pynormaliz
            8
            sage: octagon.volume()                                            # optional - pynormaliz
            2.828427124746190?
        """
        n = ZZ(n)
        if n <= 2:
            raise ValueError("n (={}) must be an integer greater than 2".format(n))

        if base_ring is None:
            if exact:
                base_ring = AA
            else:
                base_ring = RDF

        try:
            omega = 2*base_ring.pi() / n
            verts = [((i*omega).sin(), (i*omega).cos()) for i in range(n)]
        except AttributeError:
            z = QQbar.zeta(n)
            verts = [(base_ring((z**k).imag()), base_ring((z**k).real())) for k in range(n)]

        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def Birkhoff_polytope(self, n, backend=None):
        """
        Return the Birkhoff polytope with `n!` vertices.

        The vertices of this polyhedron are the (flattened) `n` by `n`
        permutation matrices. So the ambient vector space has dimension `n^2`
        but the dimension of the polyhedron is `(n-1)^2`.

        INPUT:

        - ``n`` -- a positive integer giving the size of the permutation matrices.

        - ``backend`` -- the backend to use to create the polytope.

        .. SEEALSO::

            :meth:`sage.matrix.matrix2.Matrix.as_sum_of_permutations` -- return
            the current matrix as a sum of permutation matrices

        EXAMPLES::

            sage: b3 = polytopes.Birkhoff_polytope(3)
            sage: b3.f_vector()
            (1, 6, 15, 18, 9, 1)
            sage: b3.ambient_dim(), b3.dim()
            (9, 4)
            sage: b3.is_lattice_polytope()
            True
            sage: p3 = b3.ehrhart_polynomial()     # optional - latte_int
            sage: p3                               # optional - latte_int
            1/8*t^4 + 3/4*t^3 + 15/8*t^2 + 9/4*t + 1
            sage: [p3(i) for i in [1,2,3,4]]       # optional - latte_int
            [6, 21, 55, 120]
            sage: [len((i*b3).integral_points()) for i in [1,2,3,4]]
            [6, 21, 55, 120]

            sage: b4 = polytopes.Birkhoff_polytope(4)
            sage: b4.n_vertices(), b4.ambient_dim(), b4.dim()
            (24, 16, 9)

        TESTS::

            sage: b4norm = polytopes.Birkhoff_polytope(4,backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(b4norm).run(skip='_test_pickling')                # optional - pynormaliz
        """
        from itertools import permutations
        verts = []
        for p in permutations(range(n)):
            verts.append([ZZ.one() if p[i] == j else ZZ.zero()
                          for j in range(n) for i in range(n)])
        return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)

    def simplex(self, dim=3, project=False, base_ring=None, backend=None):
        r"""
        Return the ``dim`` dimensional simplex.

        The `d`-simplex is the convex hull in `\RR^{d+1}` of the standard basis
        `(1,0,\ldots,0)`, `(0,1,\ldots,0)`, \ldots, `(0,0,\ldots,1)`. For more
        information, see the :wikipedia:`Simplex`.

        INPUT:

        - ``dim`` -- The dimension of the simplex, a positive
          integer.

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension
          ``dim-1``.  This corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.  By default, this operation turns
          the coordinates into floating point approximations (see
          ``base_ring``).

        - ``base_ring`` -- the base ring to use to create the polytope.
          If ``project`` is ``False``, this defaults to `\ZZ`.
          Otherwise, it defaults to ``RDF``.

        - ``backend`` -- the backend to use to create the polytope.

        .. SEEALSO::

            :meth:`tetrahedron`

        EXAMPLES::

            sage: s5 = polytopes.simplex(5)
            sage: s5
            A 5-dimensional polyhedron in ZZ^6 defined as the convex hull of 6 vertices
            sage: s5.f_vector()
            (1, 6, 15, 20, 15, 6, 1)

            sage: s5 = polytopes.simplex(5, project=True)
            sage: s5
            A 5-dimensional polyhedron in RDF^5 defined as the convex hull of 6 vertices

        Its volume is `\sqrt{d+1} / d!`::

            sage: s5 = polytopes.simplex(5, project=True)
            sage: s5.volume()      # abs tol 1e-10
            0.0204124145231931
            sage: sqrt(6.) / factorial(5)
            0.0204124145231931

            sage: s6 = polytopes.simplex(6, project=True)
            sage: s6.volume()      # abs tol 1e-10
            0.00367465459870082
            sage: sqrt(7.) / factorial(6)
            0.00367465459870082

        Computation in algebraic reals::

            sage: s3 = polytopes.simplex(3, project=True, base_ring=AA)
            sage: s3.volume() == sqrt(3+1) / factorial(3)
            True

        TESTS::

            sage: s6norm = polytopes.simplex(6,backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(s6norm).run(skip='_test_pickling')      # optional - pynormaliz
        """
        verts = list((ZZ**(dim + 1)).basis())
        if project:
            # Handling of default in base_ring is delegated to project_points
            verts = project_points(*verts, base_ring=base_ring)
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def icosahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return an icosahedron with edge length 1.

        The icosahedron is one of the Platonic solids. It has 20 faces
        and is dual to the :meth:`dodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- (optional) the ring in which the coordinates will
          belong to.  Note that this ring must contain `\sqrt(5)`. If it is not
          provided and ``exact=True`` it will be the number field
          `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
          field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: ico = polytopes.icosahedron()
            sage: ico.f_vector()
            (1, 12, 30, 20, 1)
            sage: ico.volume()
            5/12*sqrt5 + 5/4

        Its non exact version::

            sage: ico = polytopes.icosahedron(exact=False)
            sage: ico.base_ring()
            Real Double Field
            sage: ico.volume() # known bug (trac 18214)
            2.181694990...

        A version using `AA <sage.rings.qqbar.AlgebraicRealField>`::

            sage: ico = polytopes.icosahedron(base_ring=AA)   # long time
            sage: ico.base_ring()                             # long time
            Algebraic Real Field
            sage: ico.volume()                                # long time
            2.181694990624913?

        Note that if base ring is provided it must contain the square root of
        `5`. Otherwise you will get an error::

            sage: polytopes.icosahedron(base_ring=QQ)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational

        TESTS::

            sage: ico = polytopes.icosahedron(backend='normaliz')  # optional - pynormaliz
            sage: ico.f_vector()                                   # optional - pynormaliz
            (1, 12, 30, 20, 1)
            sage: ico.volume()                                     # optional - pynormaliz
            5/12*sqrt5 + 5/4

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        r12 = base_ring.one() / 2
        z = base_ring.zero()
        pts = [[z, s1 * r12, s2 * g / 2]
               for s1, s2 in itertools.product([1, -1], repeat=2)]
        verts = [p(v) for p in AlternatingGroup(3) for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def dodecahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return a dodecahedron.

        The dodecahedron is the Platonic solid dual to the :meth:`icosahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- (optional) the ring in which the coordinates will
          belong to.  Note that this ring must contain `\sqrt(5)`. If it is not
          provided and ``exact=True`` it will be the number field
          `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
          field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: d12 = polytopes.dodecahedron()
            sage: d12.f_vector()
            (1, 20, 30, 12, 1)
            sage: d12.volume()
            -176*sqrt5 + 400
            sage: numerical_approx(_)
            6.45203596003699

            sage: d12 = polytopes.dodecahedron(exact=False)
            sage: d12.base_ring()
            Real Double Field

        Here is an error with a field that does not contain `\sqrt(5)`::

            sage: polytopes.dodecahedron(base_ring=QQ)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational

        TESTS::

            sage: d12 = polytopes.dodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: d12.f_vector()                                    # optional - pynormaliz
            (1, 20, 30, 12, 1)

        """
        return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).polar()

    def small_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the (small) rhombicuboctahedron.

        The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26
        faces. See the :wikipedia:`Rhombicuboctahedron` for more information.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: sr = polytopes.small_rhombicuboctahedron()
            sage: sr.f_vector()
            (1, 24, 48, 26, 1)
            sage: sr.volume()
            80/3*sqrt2 + 32

        The faces are `8` equilateral triangles and `18` squares::

            sage: sum(1 for f in sr.facets() if len(f.vertices()) == 3)
            8
            sage: sum(1 for f in sr.facets() if len(f.vertices()) == 4)
            18

        Its non exact version::

            sage: sr = polytopes.small_rhombicuboctahedron(False)
            sage: sr
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of
            24 vertices
            sage: sr.f_vector()
            (1, 24, 48, 26, 1)

        TESTS::

            sage: sr = polytopes.small_rhombicuboctahedron(backend='normaliz')  # optional - pynormaliz
            sage: sr.f_vector()                                                 # optional - pynormaliz
            (1, 24, 48, 26, 1)
            sage: sr.volume()                                                   # optional - pynormaliz
            80/3*sqrt2 + 32
        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(2, 'sqrt2')
            sqrt2 = K.gen()
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            sqrt2 = base_ring(2).sqrt()

        one = base_ring.one()
        a = sqrt2 + one
        verts = []
        verts.extend([s1*one, s2*one, s3*a] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        verts.extend([s1*one, s3*a, s2*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        verts.extend([s1*a, s2*one, s3*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        return Polyhedron(vertices=verts, backend=backend)

    def great_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the great rhombicuboctahedron.

        The great rhombicuboctahedron (or truncated cuboctahedron) is an
        Archimedean solid with 48 vertices and 26 faces. For more information
        see the :wikipedia:`Truncated_cuboctahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: gr = polytopes.great_rhombicuboctahedron()  # long time ~ 3sec
            sage: gr.f_vector()                               # long time
            (1, 48, 72, 26, 1)

        A faster implementation is obtained by setting ``exact=False``::

            sage: gr = polytopes.great_rhombicuboctahedron(exact=False)
            sage: gr.f_vector()
            (1, 48, 72, 26, 1)

        Its facets are 4 squares, 8 regular hexagons and 6 regular octagons::

            sage: sum(1 for f in gr.facets() if len(f.vertices()) == 4)
            12
            sage: sum(1 for f in gr.facets() if len(f.vertices()) == 6)
            8
            sage: sum(1 for f in gr.facets() if len(f.vertices()) == 8)
            6
        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            base_ring = QuadraticField(2, 'sqrt2')
            sqrt2 = base_ring.gen()
        else:
            if base_ring is None:
                base_ring = RDF
            sqrt2 = base_ring(2).sqrt()

        one = base_ring.one()
        v1 = sqrt2 + 1
        v2 = 2 * sqrt2 + 1
        verts = [[s1 * z1, s2 * z2, s3 * z3]
                 for z1, z2, z3 in itertools.permutations([one, v1, v2])
                 for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def rhombic_dodecahedron(self, backend=None):
        """
        Return the rhombic dodecahedron.

        The rhombic dodecahedron is a a polytope  dual to the cuboctahedron. It
        has 14 vertices and 12 faces. For more information see
        the :wikipedia:`Rhombic_dodecahedron`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        .. SEEALSO::

            :meth:`cuboctahedron`

        EXAMPLES::

            sage: rd = polytopes.rhombic_dodecahedron()
            sage: rd.f_vector()
            (1, 14, 24, 12, 1)

        Its facets are 12 quadrilaterals (not all identical)::

            sage: sum(1 for f in rd.facets() if len(f.vertices()) == 4)
            12

        Some more computations::

            sage: p = rd.ehrhart_polynomial()    # optional - latte_int
            sage: p                              # optional - latte_int
            16*t^3 + 12*t^2 + 4*t + 1
            sage: [p(i) for i in [1,2,3,4]]      # optional - latte_int
            [33, 185, 553, 1233]
            sage: [len((i*rd).integral_points()) for i in [1,2,3,4]]
            [33, 185, 553, 1233]

        TESTS::

            sage: rd_norm = polytopes.rhombic_dodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(rd_norm).run(skip='_test_pickling')                 # optional - pynormaliz
        """
        v = [[2,0,0],[-2,0,0],[0,2,0],[0,-2,0],[0,0,2],[0,0,-2]]
        v.extend((itertools.product([1, -1], repeat=3)))
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def cuboctahedron(self, backend=None):
        r"""
        Return the cuboctahedron.

        The cuboctahedron is an Archimedean solid with 12 vertices and 14 faces
        dual to the rhombic dodecahedron. It can be defined as the convex hull
        of the twelve vertices `(0, \pm 1, \pm 1)`, `(\pm 1, 0, \pm 1)` and
        `(\pm 1, \pm 1, 0)`. For more information, see the
        :wikipedia:`Cuboctahedron`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        .. SEEALSO::

            :meth:`rhombic_dodecahedron`

        EXAMPLES::

            sage: co = polytopes.cuboctahedron()
            sage: co.f_vector()
            (1, 12, 24, 14, 1)

        Its facets are 8 triangles and 6 squares::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
            8
            sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
            6

        Some more computation::

            sage: co.volume()
            20/3
            sage: co.ehrhart_polynomial()      # optional - latte_int
            20/3*t^3 + 8*t^2 + 10/3*t + 1

        TESTS::

            sage: co_norm = polytopes.cuboctahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(co_norm).run(skip='_test_pickling')          # optional - pynormaliz
        """
        v = [[0, -1, -1], [0, 1, -1], [0, -1, 1], [0, 1, 1],
             [-1, -1, 0], [1, -1, 0], [-1, 1, 0], [1, 1, 0],
             [-1, 0, -1], [1, 0, -1], [-1, 0, 1], [1, 0, 1]]
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def truncated_cube(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the truncated cube.

        The truncated cube is an Archimedean solid with 24 vertices
        and 14 faces. It can be defined as the convex hull of the 24 vertices
        `(\pm x, \pm 1, \pm 1), (\pm 1, \pm x, \pm 1), (\pm 1, \pm 1, \pm x)`
        where `x = \sqrt(2) - 1`. For more information, see the
        :wikipedia:`Truncated_cube`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\sqrt{2}]` and if ``exact=False`` it
          will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: co = polytopes.truncated_cube()
            sage: co.f_vector()
            (1, 24, 36, 14, 1)

        Its facets are 8 triangles and 6 octogons::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
            8
            sage: sum(1 for f in co.facets() if len(f.vertices()) == 8)
            6

        Some more computation::

            sage: co.volume()
            56/3*sqrt2 - 56/3

        TESTS::

            sage: co = polytopes.truncated_cube(backend='normaliz')  # optional - pynormaliz
            sage: co.f_vector()                                      # optional - pynormaliz
            (1, 24, 36, 14, 1)

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(2, 'sqrt2')
            sqrt2 = K.gen()
            g = sqrt2 - 1
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = base_ring(2).sqrt() - 1

        v = [[a * g, b, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
        v += [[a, b * g, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
        v += [[a, b, c * g] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
        return Polyhedron(vertices=v, base_ring=base_ring, backend=backend)

    def tetrahedron(self, backend=None):
        """
        Return the tetrahedron.

        The tetrahedron is a Platonic solid with 4 vertices and 4 faces
        dual to itself. It can be defined as the convex hull
        of the 4 vertices `(0, 0, 0)`, `(1, 1, 0)`, `(1, 0, 1)` and
        `(0, 1, 1)`. For more information, see the
        :wikipedia:`Tetrahedron`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        .. SEEALSO::

            :meth:`simplex`

        EXAMPLES::

            sage: co = polytopes.tetrahedron()
            sage: co.f_vector()
            (1, 4, 6, 4, 1)

        Its facets are 4 triangles::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
            4

        Some more computation::

            sage: co.volume()
            1/3
            sage: co.ehrhart_polynomial()      # optional - latte_int
            1/3*t^3 + t^2 + 5/3*t + 1

        TESTS::

            sage: t_norm = polytopes.tetrahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(t_norm).run(skip='_test_pickling')        # optional - pynormaliz
        """
        v = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def truncated_tetrahedron(self, backend=None):
        r"""
        Return the truncated tetrahedron.

        The truncated tetrahedron is an Archimedean solid with 12
        vertices and 8 faces. It can be defined as the convex hull off
        all the permutations of `(\pm 1, \pm 1, \pm 3)` with an even
        number of minus signs. For more information, see the
        :wikipedia:`Truncated_tetrahedron`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: co = polytopes.truncated_tetrahedron()
            sage: co.f_vector()
            (1, 12, 18, 8, 1)

        Its facets are 4 triangles and 4 hexagons::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
            4
            sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
            4

        Some more computation::

            sage: co.volume()
            184/3
            sage: co.ehrhart_polynomial()      # optional - latte_int
            184/3*t^3 + 28*t^2 + 26/3*t + 1

        TESTS::

            sage: tt_norm = polytopes.truncated_tetrahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(tt_norm).run(skip='_test_pickling')                  # optional - pynormaliz
        """
        v = [(3,1,1), (1,3,1), (1,1,3),
             (-3,-1,1), (-1,-3,1), (-1,-1,3),
             (-3,1,-1), (-1,3,-1), (-1,1,-3),
             (3,-1,-1), (1,-3,-1), (1,-1,-3)]
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def truncated_octahedron(self, backend=None):
        r"""
        Return the truncated octahedron.

        The truncated octahedron is an Archimedean solid with 24
        vertices and 14 faces. It can be defined as the convex hull
        off all the permutations of `(0, \pm 1, \pm 2)`. For more
        information, see the :wikipedia:`Truncated_octahedron`.

        This is also known as the permutohedron of dimension 3.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: co = polytopes.truncated_octahedron()
            sage: co.f_vector()
            (1, 24, 36, 14, 1)

        Its facets are 6 squares and 8 hexagons::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
            6
            sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
            8

        Some more computation::

            sage: co.volume()
            32
            sage: co.ehrhart_polynomial()      # optional - latte_int
            32*t^3 + 18*t^2 + 6*t + 1

        TESTS::

            sage: to_norm = polytopes.truncated_octahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(to_norm).run(skip='_test_pickling')                 # optional - pynormaliz
        """
        v = [(0, e, f) for e in [-1, 1] for f in [-2, 2]]
        v = [(xyz[sigma(1) - 1], xyz[sigma(2) - 1], xyz[sigma(3) - 1])
             for sigma in Permutations(3) for xyz in v]
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def octahedron(self, backend=None):
        r"""
        Return the octahedron.

        The octahedron is a Platonic solid with 6 vertices and 8 faces
        dual to the cube. It can be defined as the convex hull
        of the six vertices `(0, 0, \pm 1)`, `(\pm 1, 0, 0)` and
        `(0, \pm 1, 0)`. For more information, see the
        :wikipedia:`Octahedron`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: co = polytopes.octahedron()
            sage: co.f_vector()
            (1, 6, 12, 8, 1)

        Its facets are 8 triangles::

            sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
            8

        Some more computation::

            sage: co.volume()
            4/3
            sage: co.ehrhart_polynomial()      # optional - latte_int
            4/3*t^3 + 2*t^2 + 8/3*t + 1

        TESTS::

            sage: o_norm = polytopes.octahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(o_norm).run(skip='_test_pickling')       # optional - pynormaliz
        """
        v = [[0, 0, -1], [0, 0, 1], [1, 0, 0],
             [-1, 0, 0], [0, 1, 0], [0, -1, 0]]
        return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)

    def snub_cube(self, exact=False, base_ring=None, backend=None, verbose=False):
        """
        Return a snub cube.

        The snub cube is an Archimedean solid. It has 24 vertices and 38 faces.
        For more information see the :wikipedia:`Snub_cube`.

        The constant `z` used in constructing this polytope is the reciprocal
        of the tribonacci constant, that is, the solution of the equation
        `x^3 + x^2 + x - 1 = 0`.
        See :wikipedia:`Generalizations_of_Fibonacci_numbers#Tribonacci_numbers`.

        INPUT:

        - ``exact`` -- (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations

        - ``base_ring`` -- the field to use. If ``None`` (the default),
          construct the exact number field needed (if ``exact`` is ``True``) or
          default to ``RDF`` (if ``exact`` is ``True``).

        - ``backend`` -- the backend to use to create the polytope.  If
          ``None`` (the default), the backend will be selected automatically.

        EXAMPLES::

            sage: sc_inexact = polytopes.snub_cube(exact=False)
            sage: sc_inexact
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
            sage: sc_inexact.f_vector()
            (1, 24, 60, 38, 1)
            sage: sc_exact = polytopes.snub_cube(exact=True)  # long time - 30secs
            sage: sc_exact.f_vector()               # long time
            (1, 24, 60, 38, 1)
            sage: sorted(sc_exact.vertices())       # long time
            [A vertex at (-1, -z, -z^2),
             A vertex at (-1, -z^2, z),
             A vertex at (-1, z^2, -z),
             A vertex at (-1, z, z^2),
             A vertex at (-z, -1, z^2),
             A vertex at (-z, -z^2, -1),
             A vertex at (-z, z^2, 1),
             A vertex at (-z, 1, -z^2),
             A vertex at (-z^2, -1, -z),
             A vertex at (-z^2, -z, 1),
             A vertex at (-z^2, z, -1),
             A vertex at (-z^2, 1, z),
             A vertex at (z^2, -1, z),
             A vertex at (z^2, -z, -1),
             A vertex at (z^2, z, 1),
             A vertex at (z^2, 1, -z),
             A vertex at (z, -1, -z^2),
             A vertex at (z, -z^2, 1),
             A vertex at (z, z^2, -1),
             A vertex at (z, 1, z^2),
             A vertex at (1, -z, z^2),
             A vertex at (1, -z^2, -z),
             A vertex at (1, z^2, z),
             A vertex at (1, z, -z^2)]
            sage: sc_exact.is_combinatorially_isomorphic(sc_inexact) #long time
            True

        TESTS::

            sage: sc = polytopes.snub_cube(exact=True, backend='normaliz')  # optional - pynormaliz
            sage: sc.f_vector()                                             # optional - pynormaliz
            (1, 24, 60, 38, 1)

        """
        def construct_z(field):
            # z here is the reciprocal of the tribonacci constant, that is, the
            # solution of the equation x^3 + x^2 + x - 1 = 0.
            tsqr33 = 3 * field(33).sqrt()
            return ((17 + tsqr33)**QQ((1, 3)) - (-17 + tsqr33)**QQ((1, 3)) - 1) / 3

        if exact and base_ring is None:
            # construct the exact number field
            from sage.rings.number_field.number_field import NumberField
            R = QQ['x']
            f = R([-1, 1, 1, 1])
            embedding = construct_z(AA)
            base_ring = NumberField(f, name='z', embedding=embedding)
            z = base_ring.gen()
        else:
            if base_ring is None:
                base_ring = RDF
            z = construct_z(base_ring)

        verts = []
        z2 = z ** 2
        A3 = AlternatingGroup(3)
        for e in [-1, 1]:
            for f in [-1, 1]:
                for g in [-1, 1]:
                    if e * f * g == -1:
                        v = [e, f * z, g * z2]
                        for p in A3:
                            verts += [p(v)]
                    else:
                        v = [f * z, e, g * z2]
                        for p in A3:
                            verts += [p(v)]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def buckyball(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the bucky ball.

        The bucky ball, also known as the truncated icosahedron is an
        Archimedean solid.  It has 32 faces and 60 vertices.

        .. SEEALSO::

            :meth:`icosahedron`

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: bb = polytopes.buckyball()   # long time - 6secs
            sage: bb.f_vector()                # long time
            (1, 60, 90, 32, 1)
            sage: bb.base_ring()               # long time
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        A much faster implementation using floating point approximations::

            sage: bb = polytopes.buckyball(exact=False)
            sage: bb.f_vector()
            (1, 60, 90, 32, 1)
            sage: bb.base_ring()
            Real Double Field

        Its facets are 5 regular pentagons and 6 regular hexagons::

            sage: sum(1 for f in bb.facets() if len(f.vertices()) == 5)
            12
            sage: sum(1 for f in bb.facets() if len(f.vertices()) == 6)
            20

        TESTS::

            sage: bb = polytopes.buckyball(backend='normaliz')  # optional - pynormaliz
            sage: bb.f_vector()                                 # optional - pynormaliz
            (1, 60, 90, 32, 1)
            sage: bb.base_ring()                                # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        """
        return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).truncation()

    def icosidodecahedron(self, exact=True, backend=None):
        """
        Return the icosidodecahedron.

        The Icosidodecahedron is a polyhedron with twenty triangular faces and
        twelve pentagonal faces. For more information see the
        :wikipedia:`Icosidodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: id = polytopes.icosidodecahedron()
            sage: id.f_vector()
            (1, 30, 60, 32, 1)

        TESTS::

            sage: polytopes.icosidodecahedron(exact=False)
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices

            sage: id = polytopes.icosidodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: id.f_vector()                                         # optional - pynormaliz
            (1, 30, 60, 32, 1)
            sage: id.base_ring()                                        # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
        """
        from sage.rings.number_field.number_field import QuadraticField
        from itertools import product

        K = QuadraticField(5, 'sqrt5')
        one = K.one()
        phi = (one+K.gen())/2

        gens = [((-1)**a*one/2, (-1)**b*phi/2, (-1)**c*(one+phi)/2)
                for a, b, c in product([0, 1], repeat=3)]
        gens.extend([(0, 0, phi), (0, 0, -phi)])

        verts = []
        for p in AlternatingGroup(3):
            verts.extend(p(x) for x in gens)

        if exact:
            return Polyhedron(vertices=verts, base_ring=K, backend=backend)
        else:
            verts = [(RR(x), RR(y), RR(z)) for x, y, z in verts]
            return Polyhedron(vertices=verts, backend=backend)

    def icosidodecahedron_V2(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the icosidodecahedron.

        The icosidodecahedron is an Archimedean solid.
        It has 32 faces and 30 vertices. For more information, see the
        :wikipedia:`Icosidodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to.
          If it is not provided and ``exact=True`` it will be a the number
          field `\QQ[\phi]` where `\phi` is the golden ratio and if
          ``exact=False`` it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: id = polytopes.icosidodecahedron_V2()   # long time - 6secs
            sage: id.f_vector()                # long time
            (1, 30, 60, 32, 1)
            sage: id.base_ring()               # long time
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        A much faster implementation using floating point approximations::

            sage: id = polytopes.icosidodecahedron_V2(exact=False)
            sage: id.f_vector()
            (1, 30, 60, 32, 1)
            sage: id.base_ring()
            Real Double Field

        Its facets are 20 triangles and 12 regular pentagons::

            sage: sum(1 for f in id.facets() if len(f.vertices()) == 3)
            20
            sage: sum(1 for f in id.facets() if len(f.vertices()) == 5)
            12

        TESTS::

            sage: id = polytopes.icosidodecahedron_V2(backend='normaliz')  # optional - pynormaliz
            sage: id.f_vector()                                            # optional - pynormaliz
            (1, 30, 60, 32, 1)
            sage: id.base_ring()                                           # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        pts = [[g, 0, 0], [-g, 0, 0]]
        pts += [[s1 * base_ring.one() / 2, s2 * g / 2, s3 * (1 + g)/2]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        verts = pts
        verts += [[v[1], v[2], v[0]] for v in pts]
        verts += [[v[2], v[0], v[1]] for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def truncated_dodecahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the truncated dodecahedron.

        The truncated dodecahedron is an Archimedean solid.
        It has 32 faces and 60 vertices. For more information, see the
        :wikipedia:`Truncated dodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: td = polytopes.truncated_dodecahedron()
            sage: td.f_vector()
            (1, 60, 90, 32, 1)
            sage: td.base_ring()
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        Its facets are 20 triangles and 12 regular decagons::

            sage: sum(1 for f in td.facets() if len(f.vertices()) == 3)
            20
            sage: sum(1 for f in td.facets() if len(f.vertices()) == 10)
            12

        The faster implementation using floating point approximations does not
        fully work unfortunately, see https://github.com/cddlib/cddlib/pull/7
        for a detailed discussion of this case::

            sage: td = polytopes.truncated_dodecahedron(exact=False) # random
            doctest:warning
            ...
            UserWarning: This polyhedron data is numerically complicated; cdd
            could not convert between the inexact V and H representation
            without loss of data. The resulting object might show
            inconsistencies.
            sage: td.f_vector()
            Traceback (most recent call last):
            ...
            ValueError: not all vertices are intersections of facets
            sage: td.base_ring()
            Real Double Field

        TESTS::

            sage: td = polytopes.truncated_dodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: td.f_vector()                                              # optional - pynormaliz
            (1, 60, 90, 32, 1)
            sage: td.base_ring()                                             # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        z = base_ring.zero()
        pts = [[z, s1 * base_ring.one() / g, s2 * (2 + g)]
                for s1, s2 in itertools.product([1, -1], repeat=2)]
        pts += [[s1 * base_ring.one() / g, s2 * g, s3 * (2 * g)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * g, s2 * base_ring(2), s3 * (g ** 2)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        verts = pts
        verts += [[v[1], v[2], v[0]] for v in pts]
        verts += [[v[2], v[0], v[1]] for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def pentakis_dodecahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the pentakis dodecahedron.

        The pentakis dodecahedron (orkisdodecahedron) is a face-regular,
        vertex-uniform polytope dual to the truncated icosahedron.  It has 60
        facets and 32 vertices. See the :wikipedia:`Pentakis_dodecahedron` for more
        information.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: pd = polytopes.pentakis_dodecahedron()    # long time - ~10 sec
            sage: pd.n_vertices()                           # long time
            32
            sage: pd.n_inequalities()                       # long time
            60

        A much faster implementation is obtained when setting ``exact=False``::

            sage: pd = polytopes.pentakis_dodecahedron(exact=False)
            sage: pd.n_vertices()
            32
            sage: pd.n_inequalities()
            60

        The 60 are triangles::

            sage: all(len(f.vertices()) == 3 for f in pd.facets())
            True
        """
        return self.buckyball(exact=exact, base_ring=base_ring, backend=backend).polar()

    def Kirkman_icosahedron(self, backend=None):
        r"""
        Return the Kirkman icosahedron.

        The Kirkman icosahedron is a 3-polytope with integer coordinates: `(\pm
        9, \pm 6, \pm 6)`, `(\pm 12, \pm 4, 0)`, `(0, \pm 12, \pm 8)`, `(\pm 6,
        0, \pm 12)`. See [Fe2012]_ for more information.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: ki = polytopes.Kirkman_icosahedron()
            sage: ki.f_vector()
            (1, 20, 38, 20, 1)

            sage: ki.volume()
            6528

            sage: vertices = ki.vertices()
            sage: edges = [[vector(edge[0]),vector(edge[1])] for edge in ki.bounded_edges()]
            sage: edge_lengths = [norm(edge[0]-edge[1]) for edge in edges]
            sage: union(edge_lengths)
            [7, 8, 9, 11, 12, 14, 16]

        TESTS::

            sage: ki_norm = polytopes.Kirkman_icosahedron(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(ki_norm).run(skip='_test_pickling')                # optional - pynormaliz
        """
        vertices = [[9, 6, 6], [-9, 6, 6], [9, -6, 6], [9, 6, -6],
                    [-9, -6, 6], [-9, 6, -6], [9, -6, -6], [-9, -6, -6],
                    [12, 4, 0], [-12, 4, 0], [12, -4, 0], [-12, -4, 0],
                    [0, 12, 8], [0, -12, 8], [0, 12, -8], [0, -12, -8],
                    [6, 0, 12], [-6, 0, 12], [6, 0, -12], [-6, 0, -12]]
        return Polyhedron(vertices=vertices, base_ring=ZZ, backend=backend)

    def rhombicosidodecahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the rhombicosidodecahedron.

        The rhombicosidodecahedron is an Archimedean solid.
        It has 62 faces and 60 vertices. For more information, see the
        :wikipedia:`Rhombicosidodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: rid = polytopes.rhombicosidodecahedron()   # long time - 6secs
            sage: rid.f_vector()                # long time
            (1, 60, 120, 62, 1)
            sage: rid.base_ring()               # long time
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        A much faster implementation using floating point approximations::

            sage: rid = polytopes.rhombicosidodecahedron(exact=False)
            sage: rid.f_vector()
            (1, 60, 120, 62, 1)
            sage: rid.base_ring()
            Real Double Field

        Its facets are 20 triangles, 30 squares and 12 pentagons::

            sage: sum(1 for f in rid.facets() if len(f.vertices()) == 3)
            20
            sage: sum(1 for f in rid.facets() if len(f.vertices()) == 4)
            30
            sage: sum(1 for f in rid.facets() if len(f.vertices()) == 5)
            12

        TESTS::

            sage: rid = polytopes.rhombicosidodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: rid.f_vector()                                              # optional - pynormaliz
            (1, 60, 120, 62, 1)
            sage: rid.base_ring()                                             # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        pts = [[s1 * base_ring.one(), s2 * base_ring.one(), s3 * (g**3)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * (g**2), s2 * g, s3 * 2 * g]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * (2 + g), 0, s2 * (g**2)]
                for s1, s2 in itertools.product([1, -1], repeat=2)]
        # the vertices are all even permutations of the lists in pts
        verts = pts
        verts += [[v[1], v[2], v[0]] for v in pts]
        verts += [[v[2], v[0], v[1]] for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def truncated_icosidodecahedron(self, exact=True, base_ring=None, backend=None):
        r"""
        Return the truncated icosidodecahedron.

        The truncated icosidodecahedron is an Archimedean solid.
        It has 62 faces and 120 vertices. For more information, see the
        :wikipedia:`Truncated_icosidodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
          it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: ti = polytopes.truncated_icosidodecahedron()   # long time
            sage: ti.f_vector()                # long time
            (1, 120, 180, 62, 1)
            sage: ti.base_ring()               # long time
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        The implementation using floating point approximations is much faster::

            sage: ti = polytopes.truncated_icosidodecahedron(exact=False) # random
            sage: ti.f_vector()
            (1, 120, 180, 62, 1)
            sage: ti.base_ring()
            Real Double Field

        Its facets are 30 squares, 20 hexagons and 12 decagons::

            sage: sum(1 for f in ti.facets() if len(f.vertices()) == 4)
            30
            sage: sum(1 for f in ti.facets() if len(f.vertices()) == 6)
            20
            sage: sum(1 for f in ti.facets() if len(f.vertices()) == 10)
            12

        TESTS::

            sage: ti = polytopes.truncated_icosidodecahedron(backend='normaliz')  # optional - pynormaliz
            sage: ti.f_vector()                                                   # optional - pynormaliz
            (1, 120, 180, 62, 1)
            sage: ti.base_ring()                                                  # optional - pynormaliz
            Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?

        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        pts = [[s1 * 1 / g, s2 * 1 / g, s3 * (3 + g)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * 2 / g, s2 * g, s3 * (1 + 2 * g)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * 1 / g, s2 * (g**2), s3 * (-1 + 3 * g)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * (-1 + 2 * g), s2 * 2 * base_ring.one(), s3 * (2 + g)]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        pts += [[s1 * g, s2 * 3 * base_ring.one(), s3 * 2 * g]
                for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        # the vertices are all ever permutations of the lists in pts
        verts = pts
        verts += [[v[1], v[2], v[0]] for v in pts]
        verts += [[v[2], v[0], v[1]] for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def snub_dodecahedron(self, base_ring=None, backend=None, verbose=False):
        """
        Return the snub dodecahedron.

        The snub dodecahedron is an Archimedean solid.
        It has 92 faces and 60 vertices. For more information, see the
        :wikipedia:`Snub_dodecahedron`.

        INPUT:

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided it will be the real double field.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES:

        Only the backend using the optional normaliz package can construct
        the snub dodecahedron in reasonable time::

            sage: sd = polytopes.snub_dodecahedron(base_ring=AA, backend='normaliz') # optional - pynormaliz, long time
            sage: sd.f_vector()                                                      # optional - pynormaliz, long time
            (1, 60, 150, 92, 1)
            sage: sd.base_ring()                                                     # optional - pynormaliz, long time
            Algebraic Real Field

        Its facets are 80 triangles and 12 pentagons::

            sage: sum(1 for f in sd.facets() if len(f.vertices()) == 3)              # optional - pynormaliz, long time
            80
            sage: sum(1 for f in sd.facets() if len(f.vertices()) == 5)              # optional - pynormaliz, long time
            12

        TESTS:

        The cdd backend with floating point arithmetic fails for this
        polytope::

            sage: sd = polytopes.snub_dodecahedron()        # not tested
            sage: sd.f_vector()                             # not tested
            (1, 60, 150, 92, 1)
            sage: sd.base_ring()                            # not tested
            Real Double Field

        """
        if base_ring is None:
            base_ring = RDF
        phi = (1 + base_ring(5).sqrt()) / 2
        xi = ((phi/2 + (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)) +
              (phi/2 - (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)))

        alpha = xi - 1 / xi
        beta = xi * phi + phi**2 + phi / xi
        signs = [[-1,-1,-1], [-1,1,1], [1,-1,1], [1,1,-1]]

        pts = [[s1 * 2 * alpha, s2 * 2 * base_ring.one(), s3 * 2 * beta]
                for s1, s2, s3 in signs]
        pts += [[s1 * (alpha + beta/phi + phi), s2 * (-alpha * phi + beta + 1/phi), s3 * (alpha/phi + beta * phi - 1)]
                for s1, s2, s3 in signs]
        pts += [[s1 * (alpha + beta/phi - phi), s2 * (alpha * phi - beta + 1/phi), s3 * (alpha/phi + beta * phi + 1)]
                for s1, s2, s3 in signs]
        pts += [[s1 * (-alpha/phi + beta * phi + 1), s2 * (-alpha + beta/phi - phi), s3 * (alpha * phi + beta - 1/phi)]
                for s1, s2, s3 in signs]
        pts += [[s1 * (-alpha/phi + beta * phi - 1), s2 * (alpha - beta/phi - phi), s3 * (alpha * phi + beta + 1/phi)]
                for s1, s2, s3 in signs]

        # the vertices are all even permutations of the lists in pts
        verts = pts
        verts += [[v[1], v[2], v[0]] for v in pts]
        verts += [[v[2], v[0], v[1]] for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)

    def twenty_four_cell(self, backend=None):
        """
        Return the standard 24-cell polytope.

        The 24-cell polyhedron (also called icositetrachoron or octaplex) is a
        regular polyhedron in 4-dimension. For more information see
        the :wikipedia:`24-cell`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: p24 = polytopes.twenty_four_cell()
            sage: p24.f_vector()
            (1, 24, 96, 96, 24, 1)
            sage: v = next(p24.vertex_generator())
            sage: for adj in v.neighbors(): print(adj)
            A vertex at (-1/2, -1/2, -1/2, 1/2)
            A vertex at (-1/2, -1/2, 1/2, -1/2)
            A vertex at (-1, 0, 0, 0)
            A vertex at (-1/2, 1/2, -1/2, -1/2)
            A vertex at (0, -1, 0, 0)
            A vertex at (0, 0, -1, 0)
            A vertex at (0, 0, 0, -1)
            A vertex at (1/2, -1/2, -1/2, -1/2)

            sage: p24.volume()
            2

        TESTS::

            sage: tfcell = polytopes.twenty_four_cell(backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(tfcell).run(skip='_test_pickling')             # optional - pynormaliz
        """
        q12 = QQ((1, 2))
        verts = list(itertools.product([q12, -q12], repeat=4))
        B4 = (ZZ**4).basis()
        verts.extend(v for v in B4)
        verts.extend(-v for v in B4)
        return Polyhedron(vertices=verts, backend=backend)

    def runcitruncated_six_hundred_cell(self, exact=True, backend=None):
        """
        Return the runcitruncated 600-cell.

        The runcitruncated 600-cell is a 4-dimensional 4-uniform polytope in
        the `H_4` family. It has 7200 vertices. For more information see
        :wikipedia:`Runcitruncated 600-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.runcitruncated_six_hundred_cell(backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of
            7200 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 1], exact=exact, backend=backend, regular=True)

    def cantitruncated_six_hundred_cell(self, exact=True, backend=None):
        """
        Return the cantitruncated 600-cell.

        The cantitruncated 600-cell is a 4-dimensional 4-uniform polytope in
        the `H_4` family. It has 7200 vertices. For more information see
        :wikipedia:`Cantitruncated 600-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.cantitruncated_six_hundred_cell(exact=True,backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 1, 1, 0], exact=exact, backend=backend, regular=True)

    def bitruncated_six_hundred_cell(self, exact=True, backend=None):
        """
        Return the bitruncated 600-cell.

        The bitruncated 600-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 3600 vertices. For more information see
        :wikipedia:`Bitruncated 600-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.runcinated_six_hundred_cell(exact=True,backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 0], exact=exact, backend=backend, regular=True)

    def cantellated_six_hundred_cell(self, exact=False, backend=None):
        """
        Return the cantellated 600-cell.

        The cantellated 600-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 3600 vertices. For more information see
        :wikipedia:`Cantellated 600-cell`.

        .. WARNING::

            The coordinates are inexact by default. The computation with
            inexact coordinates (using the backend ``'cdd'``) issues a
            UserWarning on inconsistencies.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.cantellated_six_hundred_cell() # not tested - very long time
            doctest:warning
            ...
            UserWarning: This polyhedron data is numerically complicated; cdd
            could not convert between the inexact V and H representation
            without loss of data. The resulting object might show
            inconsistencies.
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 3600 vertices

        It is possible to use the backend ``'normaliz'`` to get an exact
        representation::

            sage: polytopes.cantellated_six_hundred_cell(exact=True,backend='normaliz') # not tested - long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 0], exact=exact, backend=backend, regular=True)

    def truncated_six_hundred_cell(self, exact=False, backend=None):
        """
        Return the truncated 600-cell.

        The truncated 600-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 1440 vertices. For more information see
        :wikipedia:`Truncated 600-cell`.

        .. WARNING::

            The coordinates are not exact by default. The computation with
            exact coordinates takes a huge amount of time.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.truncated_six_hundred_cell() # not tested - long time
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 1440 vertices

        It is possible to use the backend ``'normaliz'`` to get an exact
        representation::

            sage: polytopes.truncated_six_hundred_cell(exact=True,backend='normaliz') # not tested - long time ~16sec
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1440 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 0], exact=exact, backend=backend, regular=True)

    def rectified_six_hundred_cell(self, exact=True, backend=None):
        """
        Return the rectified 600-cell.

        The rectified 600-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 720 vertices. For more information see
        :wikipedia:`Rectified 600-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.rectified_six_hundred_cell(backend='normaliz') # not tested - long time ~14sec
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 720 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 0], exact=exact, backend=backend, regular=True)

    def six_hundred_cell(self, exact=False, backend=None):
        """
        Return the standard 600-cell polytope.

        The 600-cell is a 4-dimensional regular polytope. In many ways this is
        an analogue of the icosahedron.

        .. WARNING::

            The coordinates are not exact by default. The computation with
            exact coordinates takes a huge amount of time.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: p600 = polytopes.six_hundred_cell()
            sage: p600
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 120 vertices
            sage: p600.f_vector()  # long time ~2sec
            (1, 120, 720, 1200, 600, 1)

        Computation with exact coordinates is currently too long to be useful::

            sage: p600 = polytopes.six_hundred_cell(exact=True) # not tested - very long time
            sage: len(list(p600.bounded_edges()))               # not tested - very long time
            720

        TESTS::

            sage: p600 = polytopes.six_hundred_cell(exact=True, backend='normaliz') # optional - pynormaliz
            sage: len(list(p600.bounded_edges()))                                   # optional - pynormaliz
            720
        """
        if exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            g = (1 + RDF(5).sqrt()) / 2
            base_ring = RDF

        q12 = base_ring(1) / base_ring(2)
        z = base_ring.zero()
        verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in itertools.product([1,-1], repeat=4)]
        V = (base_ring)**4
        verts.extend(V.basis())
        verts.extend(-v for v in V.basis())
        pts = [[s1 * q12, s2*g/2, s3/(2*g), z] for (s1,s2,s3) in itertools.product([1,-1], repeat=3)]
        for p in AlternatingGroup(4):
            verts.extend(p(x) for x in pts)
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def grand_antiprism(self, exact=True, backend=None, verbose=False):
        """
        Return the grand antiprism.

        The grand antiprism is a 4-dimensional non-Wythoffian uniform polytope.
        The coordinates were taken from http://eusebeia.dyndns.org/4d/gap. For
        more information, see the :wikipedia:`Grand_antiprism`.

        .. WARNING::

            The coordinates are exact by default. The computation with exact
            coordinates is not as fast as with floating point approximations.
            If you find this method to be too slow, consider using floating
            point approximations

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``False`` use floating
          point approximations instead of exact coordinates

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: gap = polytopes.grand_antiprism()  # not tested - very long time
            sage: gap                                # not tested - very long time
            A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
            polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
            the convex hull of 100 vertices

        Computation with the backend ``'normaliz'`` is instantaneous::

            sage: gap_norm = polytopes.grand_antiprism(backend='normaliz')  # optional - pynormaliz
            sage: gap_norm                                                  # optional - pynormaliz
            A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
            polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
            the convex hull of 100 vertices

        Computation with approximated coordinates is also faster, but inexact::

            sage: gap = polytopes.grand_antiprism(exact=False) # random
            sage: gap
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 100 vertices
            sage: gap.f_vector()
            (1, 100, 500, 720, 320, 1)
            sage: len(list(gap.bounded_edges()))
            500
        """
        from itertools import product

        if exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            g = (1 + RDF(5).sqrt()) / 2
            base_ring = RDF

        q12 = base_ring(1) / base_ring(2)
        z = base_ring.zero()
        verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in product([1,-1], repeat=4)]
        V = (base_ring)**4
        verts.extend(V.basis()[2:])
        verts.extend(-v for v in V.basis()[2:])

        verts.extend([s1 * q12, s2/(2*g), s3*g/2, z] for (s1,s2,s3) in product([1,-1], repeat=3))
        verts.extend([s3*g/2, s1 * q12, s2/(2*g), z] for (s1,s2,s3) in product([1,-1], repeat=3))
        verts.extend([s2/(2*g), s3*g/2, s1 * q12, z] for (s1,s2,s3) in product([1,-1], repeat=3))

        verts.extend([s1 * q12, s2*g/2, z, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))
        verts.extend([s3/(2*g), s1 * q12, z, s2*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))
        verts.extend([s2*g/2, s3/(2*g), z, s1 * q12] for (s1,s2,s3) in product([1,-1], repeat=3))

        verts.extend([s1 * q12, z, s2/(2*g), s3*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))

        verts.extend([z, s1 * q12, s2*g/2, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))

        verts.extend([z, s1/(2*g), q12, g/2] for s1 in [1, -1])
        verts.extend([z, s1/(2*g), -q12, -g/2] for s1 in [1, -1])

        verts.extend([z, s1*g/2, 1/(2*g), q12] for s1 in [1, -1])
        verts.extend([z, s1*g/2, -1/(2*g), -q12] for s1 in [1, -1])

        verts.extend([s1*g/2, z, q12, -1/(2*g)] for s1 in [1, -1])
        verts.extend([s1*g/2, z, -q12, 1/(2*g)] for s1 in [1, -1])

        verts.extend([s1/(2*g), z, g/2, -q12] for s1 in [1, -1])
        verts.extend([s1/(2*g), z, -g/2, q12] for s1 in [1, -1])

        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)

    def Gosset_3_21(self, backend=None):
        r"""
        Return the Gosset `3_{21}` polytope.

        The Gosset `3_{21}` polytope is a uniform 7-polytope. It has 56
        vertices, and 702 facets: `126` `3_{11}` and `576` `6`-simplex. For
        more information, see the :wikipedia:`3_21_polytope`.

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: g = polytopes.Gosset_3_21(); g
            A 7-dimensional polyhedron in ZZ^8 defined as the convex hull of 56 vertices
            sage: g.f_vector() # not tested (~16s)
            (1, 56, 756, 4032, 10080, 12096, 6048, 702, 1)

        TESTS::

            sage: G321 = polytopes.Gosset_3_21(backend='normaliz')   # optional - pynormaliz
            sage: TestSuite(G321).run(skip='_test_pickling')         # optional - pynormaliz
        """
        from itertools import combinations
        verts = []
        for i, j in combinations(range(8), 2):
            x = [1]*8
            x[i] = x[j] = -3
            verts.append(x)
            verts.append([-xx for xx in x])

        return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)

    def cyclic_polytope(self, dim, n, base_ring=QQ, backend=None):
        r"""
        Return a cyclic polytope.

        A cyclic polytope of dimension ``dim`` with ``n`` vertices is the
        convex hull of the points  ``(t,t^2,...,t^dim)`` with `t \in
        \{0,1,...,n-1\}` .  For more information, see the
        :wikipedia:`Cyclic_polytope`.

        INPUT:

        - ``dim`` -- positive integer. the dimension of the polytope.

        - ``n`` -- positive integer. the number of vertices.

        - ``base_ring`` -- either ``QQ`` (default) or ``RDF``.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: c = polytopes.cyclic_polytope(4,10)
            sage: c.f_vector()
            (1, 10, 45, 70, 35, 1)

        TESTS::

            sage: cp = polytopes.cyclic_polytope(4,10,backend='normaliz')  # optional - pynormaliz
            sage: TestSuite(cp).run(skip='_test_pickling')                 # optional - pynormaliz
        """
        verts = [[t**i for i in range(1, dim+1)] for t in range(n)]
        return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

    def hypersimplex(self, dim, k, project=False, backend=None):
        r"""
        Return the hypersimplex in dimension ``dim`` and parameter ``k``.

        The hypersimplex `\Delta_{d,k}` is the convex hull of the vertices made
        of `k` ones and `d-k` zeros. It lies in the `d-1` hyperplane of vectors
        of sum `k`. If you want a projected version to `\RR^{d-1}` (with
        floating point coordinates) then set ``project=True`` in the options.

        .. SEEALSO::

            :meth:`simplex`

        INPUT:

        - ``dim`` -- the dimension

        - ``n`` -- the numbers ``(1,...,n)`` are permuted

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension
          ``dim-1``.  This operation turns the coordinates into floating point
          approximations and corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: h_4_2 = polytopes.hypersimplex(4, 2)
            sage: h_4_2
            A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices
            sage: h_4_2.f_vector()
            (1, 6, 12, 8, 1)
            sage: h_4_2.ehrhart_polynomial()    # optional - latte_int
            2/3*t^3 + 2*t^2 + 7/3*t + 1

            sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True)
            sage: h_7_3
            A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices
            sage: h_7_3.f_vector()
            (1, 35, 210, 350, 245, 84, 14, 1)
        """
        verts = Permutations([0] * (dim - k) + [1] * k).list()
        if project:
            verts = project_points(*verts)
        return Polyhedron(vertices=verts, backend=backend)

    def permutahedron(self, n, project=False, backend=None):
        r"""
        Return the standard permutahedron of (1,...,n).

        The permutahedron (or permutohedron) is the convex hull of the
        permutations of `\{1,\ldots,n\}` seen as vectors. The edges
        between the permutations correspond to multiplication on the
        right by an elementary transposition in the
        :class:`~sage.groups.perm_gps.permgroup_named.SymmetricGroup`.

        If we take the graph in which the vertices correspond to
        vertices of the polyhedron, and edges to edges, we get the
        :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`.

        INPUT:

        - ``n`` -- integer

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension
          ``dim-1``.  This operation turns the coordinates into floating point
          approximations and corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: perm4 = polytopes.permutahedron(4)
            sage: perm4
            A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 24 vertices
            sage: perm4.is_lattice_polytope()
            True
            sage: perm4.ehrhart_polynomial()   # optional - latte_int
            16*t^3 + 15*t^2 + 6*t + 1

            sage: perm4 = polytopes.permutahedron(4, project=True)
            sage: perm4
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
            sage: perm4.plot()
            Graphics3d Object
            sage: perm4.graph().is_isomorphic(graphs.BubbleSortGraph(4))
            True

        .. SEEALSO::

            * :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`

        TESTS::

            sage: p4 = polytopes.permutahedron(4,backend='normaliz')   # optional - pynormaliz
            sage: TestSuite(p4).run(skip='_test_pickling')             # optional - pynormaliz
        """
        verts = list(itertools.permutations(range(1, n + 1)))
        if project:
            verts = project_points(*verts)
        return Polyhedron(vertices=verts, backend=backend)

    def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regular=False, backend=None):
        r"""
        Return the generalized permutahedron of type ``coxeter_type`` as the
        convex hull of the orbit of ``point`` in the fundamental cone.

        This generalized permutahedron lies in the vector space used in the
        geometric representation, that is, in the default case, the dimension
        of generalized permutahedron equals the dimension of the space.

        INPUT:

        - ``coxeter_type`` -- a Coxeter type; given as a pair [type,rank],
          where type is a letter and rank is the number of generators.

        - ``point`` -- a list (default: ``None``); a point given by its
          coordinates in the weight basis. If ``None`` is given, the point
          `(1, 1, 1, \ldots)` is used.

        - ``exact`` - (boolean, default ``True``) if ``False`` use floating
          point approximations instead of exact coordinates

        - ``regular`` -- boolean (default: ``False``); whether to apply a
          linear transformation making the vertex figures isometric.

        - ``backend`` -- backend to use to create the polytope; (default:
          ``None``)

        EXAMPLES::

            sage: perm_a3 = polytopes.generalized_permutahedron(['A',3]); perm_a3
            A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices

        You can put the starting point along the hyperplane of the first
        generator::

            sage: perm_a3_011 = polytopes.generalized_permutahedron(['A',3],[0,1,1]); perm_a3_011
            A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
            sage: perm_a3_110 = polytopes.generalized_permutahedron(['A',3],[1,1,0]); perm_a3_110
            A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
            sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_011)
            True
            sage: perm_a3_101 = polytopes.generalized_permutahedron(['A',3],[1,0,1]); perm_a3_101
            A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
            sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_101)
            False
            sage: perm_a3_011.f_vector()
            (1, 12, 18, 8, 1)
            sage: perm_a3_101.f_vector()
            (1, 12, 24, 14, 1)

        The usual output does not necessarily give a polyhedron with isometric
        vertex figures::

            sage: perm_a2 = polytopes.generalized_permutahedron(['A',2])
            sage: perm_a2.vertices()
            (A vertex at (-1, -1),
             A vertex at (-1, 0),
             A vertex at (0, -1),
             A vertex at (0, 1),
             A vertex at (1, 0),
             A vertex at (1, 1))

        Setting ``regular=True`` applies a linear transformation to get
        isometric vertex figures and the result is inscribed. Even though there
        are traces of small numbers, the internal computations are done using
        an exact embedded NumberField::

            sage: perm_a2_reg = polytopes.generalized_permutahedron(['A',2],regular=True)
            sage: perm_a2_reg.vertices()
            (A vertex at (-1/2, -0.866025403784439?),
             A vertex at (-1, 0),
             A vertex at (1/2, -0.866025403784439?),
             A vertex at (-1/2, 0.866025403784439?),
             A vertex at (1.000000000000000?, 0.?e-18),
             A vertex at (0.500000000000000?, 0.866025403784439?))
            sage: perm_a2_reg.is_inscribed()
            True
            sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True)
            sage: perm_a3_reg.is_inscribed()
            True

        The same is possible with vertices in ``RDF``::

            sage: perm_a2_inexact = polytopes.generalized_permutahedron(['A',2],exact=False)
            sage: perm_a2_inexact.vertices()
            (A vertex at (0.0, 1.0),
             A vertex at (-1.0, 0.0),
             A vertex at (-1.0, -1.0),
             A vertex at (0.0, -1.0),
             A vertex at (1.0, 0.0),
             A vertex at (1.0, 1.0))

            sage: perm_a2_inexact_reg = polytopes.generalized_permutahedron(['A',2],exact=False,regular=True)
            sage: perm_a2_inexact_reg.vertices()
            (A vertex at (-0.5, 0.8660254038),
             A vertex at (-1.0, 0.0),
             A vertex at (-0.5, -0.8660254038),
             A vertex at (0.5, -0.8660254038),
             A vertex at (1.0, 0.0),
             A vertex at (0.5, 0.8660254038))

        It works also with types with non-rational coordinates::

            sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3
            A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3 defined as the convex hull of 48 vertices

            sage: perm_b3_reg = polytopes.generalized_permutahedron(['B',3],regular=True); perm_b3_reg # not tested - long time (12sec on 64 bits).
            A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices

        It is faster with the backend ``'normaliz'``::

            sage: perm_b3_reg_norm = polytopes.generalized_permutahedron(['B',3],regular=True,backend='normaliz') # optional - pynormaliz
            sage: perm_b3_reg_norm # optional - pynormaliz
            A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices

        The backend ``'normaliz'`` allows further faster computation in the
        non-rational case::

            sage: perm_h3 = polytopes.generalized_permutahedron(['H',3],backend='normaliz')  # optional - pynormaliz
            sage: perm_h3                                                                    # optional - pynormaliz
            A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 defined as the convex hull of 120 vertices
            sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz')  # optional - pynormaliz
            sage: perm_f4                                                                    # optional - pynormaliz
            A 4-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4 defined as the convex hull of 1152 vertices

        .. SEEALSO::

            * :meth:`~sage.combinat.root_system.reflection_group_real.permutahedron`
            * :meth:`~sage.categories.finite_coxeter_groups.permutahedron`

        TESTS::

            sage: TestSuite(perm_h3).run(skip='_test_pickling')    # optional - pynormaliz
        """
        from sage.combinat.root_system.coxeter_group import CoxeterGroup
        try:
            W = CoxeterGroup(coxeter_type)
        except:
            raise ValueError("can not build a Coxeter group from {}".format(coxeter_type))
        n = W.one().canonical_matrix().rank()
        weights = W.fundamental_weights()
        if point is None:
            point = [ZZ.one()] * n
        apex = sum(point[i-1] * weights[i] for i in weights.keys())
        # Try to rationalize the starting point
        non_zero_index = list(apex).index([x for x in apex if x != 0][0])
        apex = (QQ(1)/apex[non_zero_index]) * apex
        apex.set_immutable()
        vertices = set()
        # This does not work well with UCF, so we set it to None:
        # br = apex.base_ring()
        br = None
        for w in W:
            # The apex is considered in the space on which it acts and not in
            # the weight space.
            new_point = w * apex
            new_point.set_immutable()
            vertices.add(new_point)
        if regular:
            from sage.matrix.constructor import matrix
            from sage.modules.free_module_element import vector
            # This transformation fixes the first root and adjust the other
            # roots to have the correct angles
            bf = W.bilinear_form()
            transf_col = [[1] + [0]*(n-1)]
            for i in range(1, n):
                new_col = [0]*i + [1] + [0]*(n-i-1)
                transf_col += [new_col]
                m = matrix(AA, transf_col)
                col = bf.column(i)
                rhs = vector(list(col[:i+1]))
                adjusted_col = m.solve_right(rhs)
                # Then scales the images so that the polytope is inscribed
                c = 1 - sum(adjusted_col[j]**2 for j in range(n) if j != i)
                c = c.sqrt()
                adjusted_col[i] = c
                transf_col[-1] = adjusted_col
            # TODO: Make this matrix into the cyclotomics, the value of c is an
            # algebraic number not anymore in the cyclotomic field.
            transf = matrix(transf_col).transpose()
            vertices = [transf * v.change_ring(AA) for v in vertices]
            br = AA
        if not exact:
            vertices = [v.change_ring(RDF) for v in vertices]
            br = RDF
        return Polyhedron(vertices=vertices, backend=backend, base_ring=br)

    def omnitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
        """
        Return the omnitruncated 120-cell.

        The omnitruncated 120-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 14400 vertices. For more information see
        :wikipedia:`Omnitruncated 120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.omnitruncated_one_hundred_twenty_cell(backend='normaliz') # not tested - very long time ~10min
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 14400 vertices
        """
        if not exact:
            # cdd finds a numerical inconsistency.
            raise NotImplementedError("can not compute the convex hull using floating points")
        return self.generalized_permutahedron(['H', 4], exact=exact, backend=backend, regular=True)

    omnitruncated_six_hundred_cell = omnitruncated_one_hundred_twenty_cell

    def runcitruncated_one_hundred_twenty_cell(self, exact=False, backend=None):
        """
        Return the runcitruncated 120-cell.

        The runcitruncated 120-cell is a 4-dimensional 4-uniform polytope in
        the `H_4` family. It has 7200 vertices. For more information see
        :wikipedia:`Runcitruncated 120-cell`.

        .. WARNING::

            The coordinates are inexact by default. The computation with
            inexact coordinates (using the backend ``'cdd'``) issues a
            UserWarning on inconsistencies.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=False) # not tested - very long time
            doctest:warning
            ...
            UserWarning: This polyhedron data is numerically complicated; cdd
            could not convert between the inexact V and H representation
            without loss of data. The resulting object might show
            inconsistencies.

        It is possible to use the backend ``'normaliz'`` to get an exact
        representation::

            sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=True,backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 1], exact=exact, backend=backend, regular=True)

    def cantitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
        """
        Return the cantitruncated 120-cell.

        The cantitruncated 120-cell is a 4-dimensional 4-uniform polytope in
        the `H_4` family. It has 7200 vertices. For more information see
        :wikipedia:`Cantitruncated 120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.cantitruncated_one_hundred_twenty_cell(exact=True,backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 1], exact=exact, backend=backend, regular=True)

    def runcinated_one_hundred_twenty_cell(self, exact=False, backend=None):
        """
        Return the runcinated 120-cell.

        The runcinated 120-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 2400 vertices. For more information see
        :wikipedia:`Runcinated 120-cell`.

        .. WARNING::

            The coordinates are inexact by default. The computation with
            inexact coordinates (using the backend ``'cdd'``) issues a
            UserWarning on inconsistencies.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.runcinated_one_hundred_twenty_cell(exact=False) # not tested - very long time
            doctest:warning ...  UserWarning: This polyhedron data is
            numerically complicated; cdd could not convert between the inexact
            V and H representation without loss of data. The resulting object
            might show inconsistencies.
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 2400 vertices

        It is possible to use the backend ``'normaliz'`` to get an exact
        representation::

            sage: polytopes.runcinated_one_hundred_twenty_cell(exact=True,backend='normaliz') # not tested - very long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[1, 0, 0, 1], exact=exact, backend=backend, regular=True)

    def cantellated_one_hundred_twenty_cell(self, exact=True, backend=None):
        """
        Return the cantellated 120-cell.

        The cantellated 120-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 3600 vertices. For more information see
        :wikipedia:`Cantellated 120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.cantellated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 1], exact=exact, backend=backend, regular=True)

    def truncated_one_hundred_twenty_cell(self, exact=True, backend=None):
        """
        Return the truncated 120-cell.

        The truncated 120-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 2400 vertices. For more information see
        :wikipedia:`Truncated 120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.truncated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 1], exact=exact, backend=backend, regular=True)

    def rectified_one_hundred_twenty_cell(self, exact=True, backend=None):
        """
        Return the rectified 120-cell.

        The rectified 120-cell is a 4-dimensional 4-uniform polytope in the
        `H_4` family. It has 1200 vertices. For more information see
        :wikipedia:`Rectified 120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` - (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.rectified_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1200 vertices
        """
        return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 0], exact=exact, backend=backend, regular=True)

    def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxeter'):
        """
        Return the 120-cell.

        The 120-cell is a 4-dimensional 4-uniform polytope in the `H_4` family.
        It has 600 vertices and 120 facets. For more information see
        :wikipedia:`120-cell`.

        .. WARNING::

            The coordinates are exact by default. The computation with inexact
            coordinates (using the backend ``'cdd'``) returns a numerical
            inconsistency error, and thus can not be computed.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) if ``True`` use exact
          coordinates instead of floating point approximations.

        - ``backend`` -- the backend to use to create the polytope.

        - ``construction`` -- the construction to use (string, default 'coxeter');
          the other possibility is 'as_permutahedron'.

`       EXAMPLES:

        The classical construction given by Coxeter in [Cox1969]_ is given by::

            sage: polytopes.one_hundred_twenty_cell()                    # not tested - long time ~15 sec.
            A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
            polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
            the convex hull of 600 vertices

        The ``'normaliz'`` is faster::

            sage: polytopes.one_hundred_twenty_cell(backend='normaliz')  # optional - pynormaliz
            A 4-dimensional polyhedron in (Number Field in sqrt5 with defining 
            polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as the convex hull of 600 vertices

        It is also possible to realize it using the generalized permutahedron
        of type `H_4`::

            sage: polytopes.one_hundred_twenty_cell(backend='normaliz',construction='as_permutahedron') # not tested - long time
            A 4-dimensional polyhedron in AA^4 defined as the convex hull of 600 vertices
        """
        if construction == 'coxeter':
            if not exact:
                raise ValueError("The 'cdd' backend produces numerical inconsistencies, use 'exact=True'.")
            from sage.rings.number_field.number_field import QuadraticField
            base_ring = QuadraticField(5, 'sqrt5')
            sqrt5 = base_ring.gen()
            phi = (1 + sqrt5) / 2
            phi_inv = base_ring.one() / phi

            # The 24 permutations of [0,0,±2,±2] (the ± are independant)
            verts = Permutations([0,0,2,2]).list() + Permutations([0,0,-2,-2]).list() + Permutations([0,0,2,-2]).list()

            # The 64 permutations of the following vectors:
            # [±1,±1,±1,±sqrt(5)]
            # [±1/phi^2,±phi,±phi,±phi]
            # [±1/phi,±1/phi,±1/phi,±phi^2]
            from sage.categories.cartesian_product import cartesian_product
            full_perm_vectors = [[[1,-1],[1,-1],[1,-1],[-sqrt5,sqrt5]],
                                 [[phi_inv**2,-phi_inv**2],[phi,-phi],[phi,-phi],[-phi,phi]],
                                 [[phi_inv,-phi_inv],[phi_inv,-phi_inv],[phi_inv,-phi_inv],[-(phi**2),phi**2]]]
            for vect in full_perm_vectors:
                cp = cartesian_product(vect)
                # The group action creates duplicates, so we reduce it:
                verts += list(set([tuple(p) for c in cp for p in Permutations(list(c))]))

            # The 96 even permutations of [0,±1/phi^2,±1,±phi^2]
            # The 96 even permutations of [0,±1/phi,±phi,±sqrt(5)]
            # The 192 even permutations of [±1/phi,±1,±phi,±2]
            even_perm_vectors = [[[0],[phi_inv**2,-phi_inv**2],[1,-1],[-(phi**2),phi**2]],
                                 [[0],[phi_inv,-phi_inv],[phi,-phi],[-sqrt5,sqrt5]],
                                 [[phi_inv,-phi_inv],[1,-1],[phi,-phi],[-2,2]]]
            even_perm = AlternatingGroup(4)
            for vect in even_perm_vectors:
                cp = cartesian_product(vect)
                verts += [p(tuple(c)) for p in even_perm for c in cp]

            return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)

        elif construction == 'as_permutahedron':
            return self.generalized_permutahedron(['H', 4], point=[0, 0, 0, 1], exact=exact, backend=backend, regular=True)
        else:
            raise ValueError("construction (={}) must be either 'coxeter' or 'as_permutahedron' ".format(construction))

    def hypercube(self, dim, backend=None):
        r"""
        Return a hypercube in the given dimension.

        The `d` dimensional hypercube is the convex hull of the points `(\pm 1,
        \pm 1, \ldots, \pm 1)` in `\RR^d`. For more information see
        the :wikipedia:`Hypercube`.

        INPUT:

        - ``dim`` -- integer. The dimension of the cube.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: four_cube = polytopes.hypercube(4)
            sage: four_cube.is_simple()
            True
            sage: four_cube.base_ring()
            Integer Ring
            sage: four_cube.volume()
            16
            sage: four_cube.ehrhart_polynomial()    # optional - latte_int
            16*t^4 + 32*t^3 + 24*t^2 + 8*t + 1

        TESTS::

            sage: fc = polytopes.hypercube(4,backend='normaliz')   # optional - pynormaliz
            sage: TestSuite(fc).run(skip='_test_pickling')         # optional - pynormaliz
        """
        return Polyhedron(vertices=list(itertools.product([1, -1], repeat=dim)), base_ring=ZZ, backend=backend)

    def cube(self, backend=None):
        r"""
        Return the cube.

        The cube is the Platonic solid that is obtained as the convex hull of
        the points `(\pm 1, \pm 1, \pm 1)`. It generalizes into several
        dimension into hypercubes.

        .. SEEALSO::

            :meth:`hypercube`

        INPUT:

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: c = polytopes.cube()
            sage: c
            A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
            sage: c.f_vector()
            (1, 8, 12, 6, 1)
            sage: c.volume()
            8
            sage: c.plot()
            Graphics3d Object
        """
        return self.hypercube(3, backend=backend)

    def cross_polytope(self, dim, backend=None):
        r"""
        Return a cross-polytope in dimension ``dim``.

        A cross-polytope is a higher dimensional generalization of the
        octahedron. It is the convex hull of the `2d` points `(\pm 1, 0,
        \ldots, 0)`, `(0, \pm 1, \ldots, 0)`, \ldots, `(0, 0, \ldots, \pm 1)`.
        See the :wikipedia:`Cross-polytope` for more information.

        INPUT:

        - ``dim`` -- integer. The dimension of the cross-polytope.

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: four_cross = polytopes.cross_polytope(4)
            sage: four_cross.f_vector()
            (1, 8, 24, 32, 16, 1)
            sage: four_cross.is_simple()
            False

        TESTS::

            sage: cp = polytopes.cross_polytope(4,backend='normaliz')   # optional - pynormaliz
            sage: TestSuite(cp).run(skip='_test_pickling')              # optional - pynormaliz
        """
        verts = list((ZZ**dim).basis())
        verts.extend([-v for v in verts])
        return Polyhedron(vertices=verts, backend=backend)

    def parallelotope(self, generators, backend=None):
        r"""
        Return the zonotope, or parallelotope, spanned by the generators.

        The parallelotope is the multi-dimensional generalization of a
        parallelogram (2 generators) and a parallelepiped (3 generators).

        INPUT:

        - ``generators`` -- a list of vectors of same dimension

        - ``backend`` -- the backend to use to create the polytope.

        EXAMPLES::

            sage: polytopes.parallelotope([ (1,0), (0,1) ])
            A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
            sage: polytopes.parallelotope([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]])
            A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices

            sage: K = QuadraticField(2, 'sqrt2')
            sage: sqrt2 = K.gen()
            sage: polytopes.parallelotope([ (1,sqrt2), (1,-1) ])
            A 2-dimensional polyhedron in (Number Field in sqrt2 with defining
            polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as
            the convex hull of 4 vertices
        """
        from sage.modules.free_module_element import vector
        from sage.structure.sequence import Sequence
        generators = [vector(v) for v in generators]
        V = Sequence(generators).universe()
        R = V.base_ring()

        from itertools import combinations
        par = [V.zero()]
        par.extend(sum(c) for k in range(1, len(generators) + 1)
                   for c in combinations(generators, k))
        return Polyhedron(vertices=par, base_ring=R, backend=backend)

    zonotope = parallelotope

    # --------------------------------------------------------
    # imports from other files
    # --------------------------------------------------------
    associahedron = staticmethod(Associahedron)

    flow_polytope = staticmethod(DiGraph.flow_polytope)


polytopes = Polytopes()
