---
title: "12 Pentagons, Part 3"
description: |
  Exploring even more symmetries based on the 12 pentagons condition.
format:
  html:
    html-math-method: katex
jupyter: python3
date: "2021-06-09"
date-modified: "2025-03-16"
categories:
  - geometry
  - combinatorics
  - symmetry
---

<style>
.image-wide {
  width: 192px;
  object-fit: contain;
}

.column.slim-column {
  width: 45%;
  margin-right: 4%
}
</style>

```{python}
from dataclasses import dataclass, asdict
import itertools
from typing import Callable, TypeAlias

from IPython.display import Markdown
import sympy
from tabulate import tabulate

from goldberg.display import (
    group_dict,
    hexagons_to_polyhedron,
    link_polyhedronisme,
)
from goldberg.operators import (
    GoldbergOperator,
    goldberg_matrix_operators,
    goldberg_operators_to_parameters,
    loeschian_numbers,
    loeschian_norm,
    t_param,
)

n_param, aux_t_param, temp_param = sympy.symbols("n T' x")
from_base_case = lambda x: (t_param * (x + 10) - 10)

@dataclass
class MiniConwayTable:
    conway: str
    hexagon_count: str
    vertex_count: str
    edge_count: str

as_maybe_tex = lambda x: x if isinstance(x, int) or not x.has_free(t_param) else f"${sympy.latex(x)}$"
as_maybe_recipe_link = lambda x, recipe: x if recipe is None else link_polyhedronisme(x, recipe)

def display_mini_conway_table(
    base_case_hexagons: int,
    base_case_name: str,
    recipes: dict[str, str] = {},
) -> Markdown:
    headers = MiniConwayTable(
        "Conway",
        "$F_6$",
        "V",
        "E",
    )

    rows = [
        MiniConwayTable(
            conway=recipe,
            hexagon_count=str(as_maybe_tex(polyhedron.face_count - 12)),
            vertex_count=str(as_maybe_tex(polyhedron.vertex_count)),
            edge_count=str(as_maybe_tex(polyhedron.edge_count)),
        )
        # base row list
        for operator in ["", "dk", "c", "w", "tk", "g_T"]
        # try lookup (a, b) tuple in parameter dict
        for maybe_parameter in (goldberg_operators_to_parameters.get(operator),)
        # combinatorially construct polyhedron from the base case and (maybe) the parameter
        for polyhedron in (
            (
                hexagons_to_polyhedron(base_case_hexagons)
                if operator == ""
                else hexagons_to_polyhedron(
                    from_base_case(base_case_hexagons).expand().subs(
                        # if the lookup failed, leave the T parameter free
                        t_param,
                        loeschian_norm(*maybe_parameter)
                        if maybe_parameter is not None
                        else t_param
                    )
                )
            ),
        )
        # pull the recipe from the `recipes` dict
        for recipe in (
            as_maybe_recipe_link(
                f"${operator + base_case_name.split(' ')[-1]}$"
                if operator == ""
                else f"${base_case_name}$",
                recipes.get(operator),
            ),
        )
    ]

    return Markdown(tabulate(
        {
            field: [name] + [getattr(item, field) for item in rows]
            for field, name in asdict(headers).items()
        },
        headers="firstrow",
        numalign="left",
    ))
```


This is the third part in an investigation into answering the following question:

> A soccer ball is a (roughly spherical) figure made of pentagons and hexagons, each meeting 3 at a point.
  [T]here are 12 pentagons...how many hexagons can there be?

The [first post](../1) in the series proved the 12 pentagons portion and investigated
  (dodecahedral) Goldberg polyhedra.
The [second post](../2) investigated tetrahedral Goldberg polyhedra as well as other tetrahedral solutions.
Yet more unconventional solutions are presented below.


Combinatorics of Goldberg-Coxeter Operators
-------------------------------------------

In the first post, we established that a solution figure *S* has feature counts
  which are parametrized entirely by $F_6$:

$$
S = \begin{pmatrix} v \\ e \\ f \end{pmatrix}
  = \begin{pmatrix}
    2F_6 + 20 \\
    3F_6 + 30 \\
    F_6 + 12
  \end{pmatrix}
  = \begin{pmatrix}  2 \\  3 \\  1 \end{pmatrix} F_6
  + \begin{pmatrix} 20 \\ 30 \\ 12 \end{pmatrix}
$$

In the last post, it was discovered that certain solutions cannot be created easily from seed polyhedra
  in a polyhedron viewer.
This prompts the question of how to calculate the vertex, edge, and face counts
  without acquiring them from the output of such a program.
Fortunatelly, each of the simple GC operators (*dk*, *c*, and *w*) has a combinatoric matrix form[^1].

$$
\underset{
  \text{Class I}
}{
  c = \begin{pmatrix}
    1 & 2 & 0 \\
    0 & 4 & 0 \\
    0 & 1 & 1
  \end{pmatrix}
} \quad
\underset{
  \text{Class II}
}{
  dk = \begin{pmatrix}
    0 & 2 & 0 \\
    0 & 3 & 0 \\
    1 & 0 & 1
  \end{pmatrix}
} \quad
\underset{
  \text{Class III}
}{
  w = \begin{pmatrix}
    1 & 4 & 0 \\
    0 & 7 & 0 \\
    0 & 2 & 1
  \end{pmatrix}
}
$$

[^1]:
    These matrices can be derived by closely observing the vertex, edge, and face counts of a figure before
      and after applying the operator.
    Gather the feature counts of the tetrahedron, dodecahedron, and icosahedron into a matrix.

    $$
    \begin{gather*}
      f \begin{pmatrix} | & | & | \\ T & D & I \\ | & | & | \end{pmatrix}
        = f \begin{pmatrix} 4 & 20 & 12 \\ 6 & 30 & 30 \\ 4 & 12 & 20 \end{pmatrix}
        = \begin{pmatrix} | & | & | \\ fT & fD & fI \\ | & | & | \end{pmatrix} \\
      f = \begin{pmatrix} | & | & | \\ fT & fD & fI \\ | & | & | \end{pmatrix}
        \begin{pmatrix} 4 & 20 & 12 \\ 6 & 30 & 30 \\ 4 & 12 & 20 \end{pmatrix}^{-1}
    \end{gather*}
    $$

    This matrix is invertible, and the feature counts for *fT*, *fD*, and *fI* can be acquired from a viewer
      implementing the operator *f*.

These operators are labelled with the classes of Goldberg polyhedra they construct
  when applied to the dodecahedron.

- *dkD* = *tI* = GC(1, 1)
- *cD* = GC(2, 0)
- *wD* = GC(2, 1)

It's worth noting again that *dk* alternates between producing Class I and Class II solutions.


### Diagonalization

*tk* is the square of the *dk* operator.
Unlike it, it preserves solution class.
Powers of a matrix are more readily expressed when the matrix is diagonalized.
Diagonalizing each of these operators shows that they have something in common.

```{python}
# diagonalizing dk gives an eigenspace which shows the 3V = 2E condition
# and Euler characteristic more clearly
eigenspace, _ = goldberg_matrix_operators["dk"].diagonalize()

def show_diagonalized(operator: GoldbergOperator):
    assert operator != "g_T"
    matrix = goldberg_matrix_operators[operator]
    diag = eigenspace.inv() @ matrix @ eigenspace

    return f"""{
      sympy.latex(matrix)
    } = \\textcolor{{red}}{{{
      sympy.latex(eigenspace)
    }}}{
      sympy.latex(diag)
    }\\textcolor{{blue}}{{{
      sympy.latex(eigenspace.inv(), fold_short_frac=True)
    }}}"""

Markdown(f"""$$
    \\begin{{align*}}
      dk &= {show_diagonalized("dk")} \\\\
      c &= {show_diagonalized("c")} \\\\
      w &= {show_diagonalized("w")}
    \\end{{align*}}
    $$
    """.replace(
        "\\left[\\begin{matrix}", "\\begin{pmatrix}"
    ).replace(
        "\\end{matrix}\\right]", "\\end{pmatrix}"
    )
)
```

All of these operators share the same eigenvectors, which can be seen
  in the columns of the left outer matrix and rows of the right outer one.
This means that composition of these operators only modifies the diagonal matrix,
  specifically the upper-left and lower-right eigenvalues.

Some of these eigenvectors have special interpretations:

- The left eigenvector (1, -1, 1) (right matrix, middle row) always has eigenvalue 1.
    - This means that the operation does not change the Euler characteristic.
- The left eigenvector (3, -2, 0) (right matrix, top row), when applied to general polyhedra,
  corresponds to the edges and vertices added by the operation:
    - In *dk*, this vector has eigenvalue 0, forcing $3V = 2E$, i.e., all vertices to have degree 3.
    - In *c* and *w*, this vector has eigenvalue 1. These operators only add degree-3 vertices.

The (right) eigenvector (2, 3, 1), deserves special consideration.
It happens to be the same as the $F_6$-dependent component of *S*.
Its eigenvalue is different for the three operators --
  4 in the case of *c*, 3 in the case of *dk*, and 7 in the case of *w*.
In all three cases, these coincide with the norm of the GC parameters:

$$
\begin{gather*}
  dkD = GC(1, 1) \longrightarrow \|1 + 1u\| = 1^2 + 1 \cdot 1 + 1^2 = 3 \\
  cD  = GC(2, 0) \longrightarrow \|2 + 0u\| = 2^2 + 2 \cdot 0 + 0^2 = 4 \\
  wD  = GC(2, 1) \longrightarrow \|2 + 1u\| = 2^2 + 2 \cdot 1 + 1^2 = 7
\end{gather*}
$$

Call this number $T = \|a + bu\| = a^2 + ab + b^2$.
We know that integers of this form are never congruent to 2 (mod 3).
Conveniently, this matches with the upper-left eigenvalue.
Assuming for the sake of argument that this is true, it implies something interesting:
  **the GC operators produced from *T* are (combinatorially) closed under composition**.
This captures all powers of a given *T*, as well as products between it and other possible *T*s.

However, this combinatorial view misses some of the picture, since some feature counts
  can be shared between two different classes at once.
This comes down to the existence of chiral pairs.
For example, [*ww*](https://levskaya.github.io/polyhedronisme/?recipe=K30wwD)
  = $(2, 1) \circ (2, 1) = (5, 3)$,
  but
  [*ww'*](https://levskaya.github.io/polyhedronisme/?recipe=K30wrwD)
  = $(2, 1) \circ (1, 2) = (7, 0)$.

This doesn't affect hexagon counts, so it means we can characterize the possible numbers given a certain *T*:

$$
\begin{align*}
  g_T S &= g_T \left(
    \begin{pmatrix}  2 \\  3 \\  1 \end{pmatrix} F_6
    + \begin{pmatrix} 20 \\ 30 \\ 10 \end{pmatrix}
    + \begin{pmatrix} 0 \\ 0 \\ 2 \end{pmatrix}
  \right) \\
  &= T \begin{pmatrix}  2 \\  3 \\  1 \end{pmatrix} F_6
    + T \begin{pmatrix} 20 \\ 30 \\ 10 \end{pmatrix}
    + \begin{pmatrix} 0 \\ 0 \\ 2 \end{pmatrix} \\
  &= \begin{pmatrix} T(2F_6 + 20) \\ T(3F_6 + 30) \\ T(F_6 + 10) + 2 \end{pmatrix} \\[8pt]
  F'_6 &= T(F_6 + 10) - 10
\end{align*}
$$

This demonstrates two things: the initial $F_6$ count from the choice of seed polygon *S* matters,
  and we can use valid values of *T* to produce a sequence of solutions.


Searching for Other Seeds
-------------------------

The dodecahedron, and by extension dodecahedral solutions, have icosahedral symmetry.
Tetrahedral symmetry, a sub-symmetry of this, was exploited in the previous post.

However, there are further sub-symmetries which have not been encountered.
For example, some lesser symmetries of the dodecahedron are the vertices under rotation
  and inversion ([dihedral](https://en.wikipedia.org/wiki/Dihedral_group), degree 3)
  and the faces under rotation and inversion (dihedral, degree 5).

Rather than the typical construction based on paths between pentagons,
  this section will focus on certain base cases followed by a rudimentary application
  of the Conway operators above.
The particular symmetry group of each figure will be mentioned in each section.


### Degenerate Deltahedra

*Deltahedra* are polyhedra formed by equilateral triangles,
  a [table of which](https://en.wikipedia.org/wiki/Deltahedron) is available on Wikipedia.
Equilateral triangles are important since three to five of them can be joined at a convex vertex,
  and if coplanar arrangements are allowed, up to six.
Degree-5 and degree-6 vertices can be dualized or truncated to pentagons and hexagons respectively.
Additionally, when dualizing, all of the triangles become degree-3 vertices, producing a solution.

All convex deltahedra besides the tetrahedron and icosahedron contain degree-4 vertices.
These vertices pose a problem since their duals are quadrilaterals.
However, it's reasonably easy to "correct" these vertices to a higher degree
  by selectively raising pyramids on faces (*augmenting*).
This section will focus on the coplanar entries on the table.


#### Triangulated Rhombohedron ($Dih_3$)

<!-- TODO: add image? -->
The figure made by cutting the short diagonals of a
  [rhombohedron](https://en.wikipedia.org/wiki/Rhombohedron)
  is one such degree-4 vertex-free deltahedron.
It can also be seen as the figure formed by adding two triangular pyramids
  to opposite faces of an octahedron (a *biaugmentation*)
  or as a [*gyroelongated triangular bipyramid*](https://en.wikipedia.org/wiki/Gyroelongated_bipyramid).

Unfortunately, the polyhedron viewer I typically use is unsuitable for visualizing this figure.
[This viewer](https://drajmarsh.bitbucket.io/poly3d.html)
  supports more operations, and features an operator *z* which triangulates faces.
It produces the desired figure when applied to the cube
  (which is topologically equivalent to rhombohedra),

Truncating the order-5 vertices produces an eggy-looking figure,
It inherits the dihedral symmetry of degree 3 from the vertices of the dodecahedron.

:::: {.row layout-ncol="2" layout-valign="center"}
::: {.column .slim-column layout="[[1,1],[1]]"}
![
  Biaugmented octahedron
](./drajmarsh_zC.png)

![
  Base case: order-5 truncation of left figure
](./drajmarsh_t5zC.png)

![
  Graph of the pole of the base case.<br>
  On the opposite pole, blue and red hexagons are exchanged.
](./truncated gyroelongated bipyramid pole.png)
:::

::: {.column}
```{python}
#| classes: plain

display_mini_conway_table(6, "F")
```
:::
::::


#### Base-Triangulated Frustum ($Dih_3$)

Consider two faces of a figure, one a single equilateral triangle and the other
  containing four coplanar ones.
These two faces can be joined by three triples of triangles in the shape of a half-hexagon.
This figure can be seen as an octahedron with three tetrahedra placed around it (a *triaugmentation*),
  or as triangulation of the triangular prism.

Truncating all vertices except those of degree 3 ($t_5 t_6$) produces a figure
  with 8 hexagons and indeed, 12 pentagons.
Unfortunately, this figure is not easily constructible from normal seed polyhedra,
  even in the alternative viewer.
However, it is still possible to operate on its projection as a planar graph:

::: {#fig-triangular-frustum layout-ncol="4"}
![Initial graph](./triangulated_frustum.png)

![After truncating degree-6 vertices](./t6_frustum.png)

![After redrawing by "inverting" the interior and exterior](./inverted_t6_frustum.png)

![After truncating degree-5 vertices](./t5_t6_frustum.png)
:::

The pentagons gather in groups of 4 and are separated by three bands of hexagons,
  joined by two hexagons at either end.
The symmetry inherited by this figure is also dihedral of degree 3, but centered about a hexagon,
  rather than (a complex of) pentagons.

::: {layout-ncol="2" layout-valign="center"}
[
![
  One-third of the figure, showing a group of four pentagons.<br>
  Pentagons in green, band hexagons in red, cap hexagons in blue.
](./frustum_third.png){.column .slim-column}
](./frustum_third.png){target="_blank_"}

```{python}
#| classes: plain

display_mini_conway_table(8, "F")
```
:::


#### Others

Before their respective truncations, both of these figures of triangles which are compatible with
  (dual) Goldberg-Coxeter operators.
For example, larger triangular subdivisions (in the $u_n$ sense) will also give degenerate deltahedra,
Triangular subdivision can add degree-6 vertices, so for the triangulated rhombohedron,
  an additional order-6 truncation needs to be done.
Doing so means $t_5 t_6$ is applied to either case to form the solution figure.

An example subidivision is shown below.
Note how the 3 pentagons at the pole have been separated from another 3 by a triangle of hexagons.
I am fairly sure it is possible for mismatched pole configurations to be joined to one another
  for a more selective subdivision.
Other Goldberg-Coxeter operations other than *u* can also be used;
  *dwd* for example connects non-polar pentagons with spirals of hexagons.

::: {#fig-triangular-frustum-subdivision layout-ncol="2"}
![
  $t_5 t_6 uzC$ <br>
  $F_6$ = 60, V = 140, E = 210
](./drajmarsh_t5t6uzC.png)

![
  $t_5 t_6 dwdzC$ <br>
  $F_6$ = 114, V = 248, E = 372
](./drajmarsh_t5t6dwdzC.png)
:::


### Truncated Trapezohedron ($Dih_6$)

A *trapezohedron* is the polyhedral dual of an antiprism.
An example is a d10, which is the [pentagonal case](https://en.wikipedia.org/wiki/Pentagonal_trapezohedron).
Despite the name "*n*-gonal trapezohedron", the figure is made entirely from 2*n* kites
  with the long edges meeting at points and the short edges meeting with each other.

Truncating the order-*n* vertices where the long edges of the kites meet produces a
  [truncated trapezohedron](https://en.wikipedia.org/wiki/Truncated_trapezohedron),
These figures possess two regular *n*-gonal "caps" separated by a ring of pentagons.
In a way, they can be considered as a pentagonal analogue to prisms (quadrilaterals)
  and antiprisms (triangles).

The dodecahedron itself can be realized as a (order-5) truncated pentagonal trapezohedron ($t_5 d A_5$),
  which emphasizes its degree-5 dihedral symmetry.
The next largest truncated trapezohedron is the hexagonal case ($t_6 d A_6$),
  which contains and 2 hexagons and has dihedral symmetry of degree 6.
This is a symmetry beyond that of the icosahedron, but exists due to the symmetry of the hexagon.

:::: {layout-ncol="2" layout-valign="center"}
![
  Base case: truncated hexagonal trapezohedron
](./polyhedronisme-K300t6dA6.png)

```{python}
#| classes: plain

display_mini_conway_table(
    2,
    "t_6dA_6 = T_6",
    recipes = {
        "": "K300t6dA6",
        "dk": "dkK300t6dA6",
        "c": "K300cK30t6dA6",
        "w": "K300wK30t6dA6",
        "tk": "K300tkK30t6dA6",
    },
)
```
::::


### Medially-Separated Dodecahedron ($Dih_5$)

As the "truncated pentagonal trapezohedron", the dodecahedron can be separated into halves along an equator
  (specifically, along the [Petrie polygon](https://en.wikipedia.org/wiki/Petrie_polygon)).
This produces two halves with the pole configuration of the dodecahedron, joined together with hexagons.
Organizing the pentagons into two pairs of 6 is similar to the earlier case with the triangulated cube,
  but with a different arrangement of pentagons.
The resultant figure has dihedral symmetry of degree 5.

:::: {layout-ncol="2" layout-valign="center"}
::: {.column .slim-column layout="[[1,1],[1]]"}
![
  Dodecahedral graph
](./dodecahedral graph.png)

![
  Pole configuration of solution
](./dodeca graph 2.png)

![
  Complete base solution.<br>Note how the outer face has been rotated when compared with the dodecahedral graph.
](./petrial expanded dodecahedron.png)
:::

::: {.column}
```{python}
#| classes: plain

display_mini_conway_table(5, "D_\\pi")
```
:::
::::

Interestingly, this is also the first solution polyhedron with an odd number of faces.


### Truncated Gyro-Pyramids ($Dih_5$, $Dih_6$)

*g*, for "gyro", is a Conway operator I have used, but refused to explain.
A *gyro-pyramid* (i.e., the gyro operator applied to pyramids,
  [example]](https://levskaya.github.io/polyhedronisme/?recipe=K300gY5))
  can roughly be described as a ring of 2*n* pentagons which alternate in orientation,
  the tips of which have an additional edge connecting to one of two antipodal endpoints.
Truncating these endpoints these adds a face which is the same as the base of the original pyramid,
  surrounded by hexagons.

Solution polyhedra can be found by examining the pentagonal ($t_5 g Y_5$) and hexagonal ($t_6 g Y_6$) cases.
The former has dihedral symmetry of degree 5 and the latter has that of degree 6.

:::: {layout-ncol="2" layout-valign="center"}
::: {.column .slim-column}
![
  Base case 1: truncated pentagonal gyro-pyramid
](./polyhedronisme-K300t5K30gY5.png){.column .slim-column}
:::

```{python}
#| classes: plain

display_mini_conway_table(
    10,
    "t_5gY_5 = G_5",
    recipes = {
        "": "K300t5K30gY5",
        "dk": "dkK300t5K30gY5",
        "c": "K300dudt5K30gY5",
        "w": "K300wK30t5K30gY5",
        "tk": "tkK300t5K30gY5",
    },
)
```

::: {.column .slim-column}
![
  Base case 2: truncated hexagonal gyro-pyramid
](./polyhedronisme-K300t6K30gY6.png)
:::

```{python}
#| classes: plain

display_mini_conway_table(
    14,
    "t_6gY_6 = G_6",
    recipes = {
        "": "K300t6K30gY6",
        "dk": "dkK300t6K30gY6",
        "c": "K300dudK40t6gY6",
        "w": "K300wK30t6K30gY6",
        "tk": "tkK300t6K30gY6",
    },
)
```
::::

The pentagonal case demonstrates something interesting: an appeal to the partition
  $12 = 10 + 1 + 1$.
The similar partition $12 = 5 + 5 + 1 + 1$ is unlikely to bear fruit,
  as an odd number of pentagons cannot alternate up and down.


### Japanese Floor Tiling

*Tatami* are a Japanese traditional style of floor mat, used even today in Japan
  as an intuitive measure for the surface area of living spaces.
A single mat has an aspect ratio of 2:1, but the complexity comes in how they are arranged.
Layouts are termed "inauspicious" (不祝儀敷き, *fushūgi-jiki*) when there are points where four mats meet,
  while "auspicious" (祝儀敷き, *shūgi-jiki*) layouts have all mats meet in threes.
For a sample of the fascination mathematicians have with these arrangements,
  you need only [search for "tatami" in the OEIS](http://oeis.org/search?q=tatami&language=english&go=Search)
  to find dozens of combinatorial sequences.

::: {layout-ncol="3"}
![
  Inauspicious 4x4 layout
](./tatami_inauspicious.png)

![
  Auspicious 4x4 layout
](./tatami_auspicious.png)

![
  Possible topologies for a single mat
](./tatami_topology.png)
:::

Topologically, each *tatami* mat in an arrangement can be thought of as either a quadrilateral,
  a pentagon or a hexagon.
Obviously, when considered alone, one mat is a rectangle, as is each mat in the inauspicious layout above.
When two mats are affixed to one side of the mat, it becomes a pentagon,
  as in the auspicious layout above; all 8 mats are pentagons.
When done to both sides, it becomes a hexagon.

Naturally, the interplay between the latter two elements, as well as the condition
  that all mats meet in threes has a direct application to the problem at hand.
In fact, the graph of the whirl operation on the surface of a cube
  clearly makes the shape of one of these arrangements (with a half-mat included):

:::: {#fig-tatami-polyhedron}
::: {layout="[[-1,2,-1,2,-1]]"}
<a title="Tomruen, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File:Conway_wC.png">
<img width="384" alt="Conway wC" src="https://upload.wikimedia.org/wikipedia/commons/f/f4/Conway_wC.png">
</a>

<a title="See page for author, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File:Tearoom_layout.svg">
<img style="background-color: white !important; padding: 5px" width="384" alt="Tearoom layout" src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Tearoom_layout.svg/256px-Tearoom_layout.svg.png">
</a>
:::

Images were retrieved from Wikimedia and belong to their respective owners.
::::

Two things pose a small issue for auspicious floor layouts:

- when interpreted as a graph, the external face very quickly accumulates a large number of edges and
- each of the mats at the corners of the room have a single degree-2 vertex

Both of these can be (partially) amended by connecting pairs of corner vertices,
  which creates additional faces while decreasing the number of edges on the perimeter.

The standard 4x6 auspicious layout is shown below, with corners connected and pentagons/hexagons identified.
Next to it is an equivalent graph with a regular hexagon as an outer face.
As a 3D figure, it has only three hexagons and degree-3 dihedral symmetry, another odd number.

:::: {layout-ncol="2" layout-valign="center"}
::: {.column .slim-column .text-center}
![](./tatami_graph.png){.image-wide}

![](./regularized_tatami_graph.png){.image-wide}
:::

```{python}
#| classes: plain

display_mini_conway_table(3, "M")
```
::::

While *c* and *w* produce counts which are multiples of 3,
  *dk* somewhat strangely generates 29 and 107, which are both prime.
In fact, up to $(dk)^{11}$, the values are either prime or semiprime,
  and up to $(dk)^{28}$, the values are either 1-, 2-, or 3-, almost primes.


#### Tatamified Projections

While other rectangular *tatami* arrangements contain either too few or too many edges,
  graphs of solution polyhedra can be produced by using mats with nonstandard aspect ratios
  (and connecting corners as necessary).

Some of these are shown below:

::: {layout-ncol="2"}
![
  Dodecahedral graph, as a *tatami* arrangement with a single 4x1 mat.
](./dodecahedron_tatami.png)

![
  Medially-separated dodecahedron, as seen above.
](./medial_dodecahedron.png)

![
  Truncated icosahedral graph. Note the path of 2x1 mats.
](./truncated_icosahedron_tatami.png)

![
  Triacontahedron (not a base case shown above), initially similar to tI.
](./18_hexagon_tatami.png)
:::


Final Tabulation
----------------

The solutions examined have been collected in the table below.

```{python}
#| classes: plain
#| tbl-cap: "T, T' are members of the sequence 1, 3, 4, 7, 9, 12, 13, 16, 19, 21... ([OEIS A003136](http://oeis.org/A003136))\
#|     <br>\\\\*: This is the result of a GC operator applied to $2T - 14$, which requires *T* > 7"

TFilter: TypeAlias = Callable[[int], bool] | None

@dataclass
class TotalHexagonRow:
    symmetry_group: str
    class_name: str
    f6_expression: sympy.Expr
    t_filter: TFilter = None

antitruncation = TotalHexagonRow(
    symmetry_group="Tetrahedral",
    class_name="Tetrahedral Goldberg Antitruncations",
    f6_expression=(2*t_param - 14),
    t_filter=lambda t: t > 7,
)
centered_edge = TotalHexagonRow(
    symmetry_group="Tetrahedral",
    class_name="Class I Edge-preserving",
    f6_expression=(4*((n_param + 1)**2 - 3)),
    t_filter=lambda t: t > 1,
)

def apply_gc(row: TotalHexagonRow, class_name: str) -> TotalHexagonRow:
    return TotalHexagonRow(
        symmetry_group = row.symmetry_group,
        class_name = class_name,
        f6_expression = from_base_case(
            row.f6_expression.subs(t_param, temp_param)
        ).subs(t_param, aux_t_param).subs(temp_param, t_param),
        t_filter=row.t_filter,
    )

full_tabulation_rows = [
    TotalHexagonRow(
        symmetry_group="Icosahedral",
        class_name="Dodecahedral Goldberg",
        f6_expression=from_base_case(0),
    ),
    antitruncation,
    apply_gc(
      antitruncation,
      "GC(Antitruncations)*",
    ),
    centered_edge,
    apply_gc(
      centered_edge,
      "GC(Edge-preserves)",
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_3$",
        class_name="Triangulated Rhombohedron",
        f6_expression=from_base_case(6),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_3$",
        class_name="Triangular Frustum",
        f6_expression=from_base_case(8),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_3$",
        class_name="[Tatamihedron](https://en.wikipedia.org/wiki/26-fullerene_graph)",
        f6_expression=from_base_case(3),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_5$",
        class_name="Medially-separated Dodecahedron",
        f6_expression=from_base_case(5),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_5$",
        class_name="Truncated Pentagonal Gyropyramid",
        f6_expression=from_base_case(10),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_6$",
        class_name="Truncated Hexagonal Trapezohedron",
        f6_expression=from_base_case(2),
    ),
    TotalHexagonRow(
        symmetry_group="$Dih_6$",
        class_name="Truncated Hexagonal Gyropyramid",
        f6_expression=from_base_case(14),
    ),
]


def get_example_counts(expr: sympy.Expr, amount: int, t_filter: TFilter = None):
    """Build values from the expression in terms of the number of hexagons"""
    t_params = list(itertools.islice(loeschian_numbers(), 2*amount))

    return [
        count
        for count, _ in itertools.groupby(sorted([
            int(
                expr.subs(
                    aux_t_param, t1
                ).subs(
                    t_param, t2
                ).subs(
                    n_param, n
                )  # type: ignore
            )
            for t1 in t_params
            for t2 in t_params
            for n in range(1, 2*amount)
            if t_filter is None or t_filter(t2) and t1 > 2
        ]))
        if count > 0
    ][:amount]

Markdown(tabulate(
  [
    [
      group_name if i == 0 else "",
      row.class_name,
      f"${sympy.latex(row.f6_expression)}$",
      ", ".join(map(str, get_example_counts(row.f6_expression, 10, row.t_filter))),
    ]
    for group_name, group in itertools.groupby(
      full_tabulation_rows,
      lambda x: x.symmetry_group,
    )
    for i, row in enumerate(group)
  ],
  headers=["Symmetry", "Classification", "$F_6$", "Example Values"],
))
```

Here are a few hexagon counts from the table, sorted in ascending order:

```{python}
Markdown(
    ", ".join([
        str(i)
        for i in group_dict(
            itertools.chain(*[
                get_example_counts(row.f6_expression, 10, row.t_filter)
                for row in full_tabulation_rows
            ])
        ).keys()
    ][:20]) + "..."
)
```

However, keep in mind that this list is *not* exhaustive.
In particular, it may be possible to construct additional entries by applying
  selective GC operators which preserve certain symmetries,
  like in the edge-preserving tetrahedral and deltahedron cases.

Some small naturals which do not appear on the list are 1, 7, 9, 11, and 13.
Without constructing them, I am unsure whether they can exist.
Some possible partitions to consider are $7 = 5 + 1 + 1$ (five equatorial and two polar hexagons, $Dih_5$)
  and $11 = 3 + 3 + 3 + 1 + 1$ (three hexagons along three lines of longitude
  or three triangles of hexagons pointing toward a pole, $Dih_3$).


Closing
-------

Despite the Goldberg-Coxeter construction for dodecahedra being well-known,
  the 12 pentagons rule applies to a much broader class of polyhedra.
In fact, due to the GC construction, any polyhedron satisfying its conditions can be expanded
  and twisted into larger and larger solutions, which are far out of the way of ordinary soccer balls.

Polyhedron images were generated using [polyHédronisme](https://levskaya.github.io/polyhedronisme/)
  and Dr. Andrew J. Marsh's [polyhedron generator](https://drajmarsh.bitbucket.io/poly3d.html).
Nets and graphs were created with GeoGebra.
Other images were retrieved from Wikimedia and belong to their respective owners.
