codexmodular-multiplication-units-and-zero-divisors.py
modular-multiplication-units-and-zero-divisors.py
"""Compare multiplication by a unit and a zero divisor modulo twelve.
Run from _media to write the PNG to CWD. Tested with Python 3.14.4,
NumPy 2.3.5 and Matplotlib 3.10.7 (distribution build 3.10.7+dfsg1).
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
def main():
plt.rcParams.update({"font.size": 12})
fig, ax = plt.subplots(figsize=(9, 3.7), dpi=120, facecolor="white")
fig.subplots_adjust(left=0.04, right=0.98, bottom=0.17, top=0.82)
inputs = np.arange(12)
palette = plt.get_cmap("tab20")
colors = [palette(i) for i in (1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 2)]
ax.text(-0.2, 2.45, "Input x", ha="right", va="center")
for x in inputs:
ax.text(x + 0.5, 2.45, str(x), ha="center", va="center")
for factor, y in [(5, 1.55), (6, 0.2)]:
outputs = factor * inputs % 12
ax.text(-0.2, y + 0.35, f"{factor}x mod 12", ha="right", va="center")
for x, output in zip(inputs, outputs):
ax.add_patch(Rectangle((x + 0.03, y), 0.94, 0.7,
facecolor=colors[output],
edgecolor="#ffffff", linewidth=1))
ax.text(x + 0.5, y + 0.35, str(output), ha="center",
va="center", color="#17212b", fontsize=13, weight="bold")
note = ("Every output appears once; multiplying by 5 reverses the row"
if factor == 5 else
"Only 0 and 6 appear; inputs 0 and 2 both give 0")
ax.text(6, y - 0.22, note, ha="center", va="center", fontsize=11)
ax.set(xlim=(-2.1, 12.1), ylim=(-0.48, 2.8))
ax.axis("off")
fig.suptitle("Multiplication modulo twelve: reversible or many-to-one",
fontsize=15, y=0.96)
fig.text(0.5, 0.075, "Units: 1, 5, 7, 11 | Same output = same cell color",
ha="center", fontsize=12)
fig.savefig(Path.cwd() / "modular-multiplication-units-and-zero-divisors.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()