codexquadratic-residue-character-product-table.py
quadratic-residue-character-product-table.py
"""Show how the Legendre character compresses multiplication modulo seven."""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
def main():
p = 7
residues = sorted({x * x % p for x in range(1, p)})
nonresidues = sorted(set(range(1, p)) - set(residues))
order = residues + nonresidues
fig = plt.figure(figsize=(10.6, 6.0), dpi=100, facecolor="white")
grid = fig.add_gridspec(1, 2, width_ratios=(1.35, 1),
left=0.035, right=0.97, top=0.80, bottom=0.13, wspace=0.26)
ax = fig.add_subplot(grid[0, 0])
ax.set(xlim=(-1.1, 6.1), ylim=(-0.2, 7.1), aspect="equal")
ax.axis("off")
fig.suptitle("Multiplication modulo 7 becomes sign multiplication",
fontsize=20, weight="bold", y=0.975)
fig.text(0.5, 0.899, "Q = {1, 2, 4}: sign +1 N = {3, 5, 6}: sign −1",
ha="center", fontsize=14)
for i, a in enumerate(order):
y = 5 - i
ax.text(-0.38, y + 0.5, str(a), ha="center", va="center", fontsize=14, weight="bold")
ax.text(i + 0.5, 6.35, str(a), ha="center", va="center", fontsize=14, weight="bold")
for j, b in enumerate(order):
value = a * b % p
square = value in residues
color = "#deefe2" if square else "#f9e4d2"
ax.add_patch(Rectangle((j, y), 1, 1, facecolor=color,
edgecolor="white", linewidth=1.2))
ax.text(j + 0.5, y + 0.66, str(value), ha="center", va="center", fontsize=15)
ax.text(j + 0.5, y + 0.27, "+1" if square else "−1",
ha="center", va="center", fontsize=11, color="#344956")
for position in (0, 3, 6):
ax.plot([position, position], [0, 6], color="#485965", lw=1.8)
ax.plot([0, 6], [position, position], color="#485965", lw=1.8)
ax.text(2.95, 6.88, "a × b: product above, sign below", ha="center", fontsize=12)
side = fig.add_subplot(grid[0, 1])
side.set(xlim=(-0.7, 2.25), ylim=(-1.45, 2.9), aspect="equal")
side.axis("off")
side.text(1, 2.75, "Four blocks, two signs", ha="center", fontsize=15, weight="bold")
for i, row in enumerate(("Q (+1)", "N (−1)")):
side.text(-0.12, 1.5 - i, row, ha="right", va="center", fontsize=12)
side.text(i + 0.5, 2.18, row, ha="center", va="center", fontsize=12)
for j in range(2):
square = i == j
side.add_patch(Rectangle((j, 1 - i), 1, 1,
facecolor="#deefe2" if square else "#f9e4d2",
edgecolor="#485965", linewidth=1.2))
side.text(j + 0.5, 1.5 - i, "Q\n+1" if square else "N\n−1",
ha="center", va="center", fontsize=15, linespacing=1.5)
side.text(0.85, -0.43, "3 × 5 ≡ 1: (−1)(−1) = +1", ha="center", fontsize=12)
side.text(0.85, -0.88, "2 × 3 ≡ 6: (+1)(−1) = −1", ha="center", fontsize=12)
fig.text(0.5, 0.044, "Zero is separate: multiplying by zero gives product 0 and Legendre symbol 0.",
ha="center", fontsize=12)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()