# Modular arithmetic on a circle

Inspired by David Richeson's applet on GeoGebra the Python script below plots the lines joining pairs of points, evenly spaced on the perimeter of a circle according to the following algorithm: label the $n$ points $k=0, 1, 2, \ldots n-1$; then join point $k$ to point $mk\,\mathrm{mod}\,n$.

The line segments inside and outside the circle can be plotted in different colours to make pleasing images. Pass $m$ and $n$ as arguments on the command line, e.g.

python circlines.py 82 110


python circlines.py 20 40


import sys
import numpy as np
import matplotlib.pyplot as plt

def add_lines(ax, m, n, line_segments=True, c='w', lw=0.5, *args, **kwargs):
ax.add_patch(plt.Circle((0, 0), 1, fc='none', ec='w', lw=2))
# Pop off oc ("outer line colour") if presentm otherwise set to the
# same as the inside colour.
oc = kwargs.pop('oc', c)

dtheta = 2 * np.pi / n
theta = np.arange(0, 2 * np.pi, dtheta)
pts = np.array([np.cos(theta), np.sin(theta)]).T

def get_bounded_coords(X, x1, y1, g):
"""
Get the coordinates of the intersection of the line through
(x1, y1) with gradient g at with x-coordinate X, unless this
has a y-coordinate outside the range (YMIN, YMAX), in which case
set y appropriately and determine the correspinding x-coordinate.
"""

def get_x_at_y(Y):
"""Return the x-coordinate at y=Y for the line."""
return (Y - y1) / g + x1

yA = y1 + g * (X - x1)
if yA > YMAX:
return get_x_at_y(YMAX), YMAX
elif yA < YMIN:
return get_x_at_y(YMIN), YMIN
return X, yA

for k, pt1 in enumerate(pts):
pt2 = pts[m * k % n]
x1, y1 = pt1
x2, y2 = pt2
if x1 > x2:
x1, y1, x2, y2 = x2, y2, x1, y1
if line_segments:
# Just join the line segments inside the circle.
plt.plot([x1, x2], [y1, y2], c=c, lw=lw, **kwargs)
continue

if x1 == x2:
# Vertical lines are a special case.
xA, xB, yA, yB = x1, x2, YMIN, YMAX
else:
# The gradient of the line.
g = (y2 - y1) / (x2 - x1)
# Get the start and end points of the line on the boundary
# rectangle, (XMIN, YMIN), (XMAX, YMAX).
xA, yA = get_bounded_coords(XMIN, x1, y1, g)
xB, yB = get_bounded_coords(XMAX, x1, y1, g)

if oc == c:
# Same colour inside and outside the circle.
plt.plot([xA, xB], [yA, yB], c=c, lw=lw, **kwargs)
else:
plt.plot([xA, x1], [yA, y1], c=oc, lw=lw, **kwargs)
plt.plot([x1, x2], [y1, y2], c=c, lw=lw, **kwargs)
plt.plot([x2, xB], [y2, yB], c=oc, lw=lw, **kwargs)

if __name__ == '__main__':
m, n = map(int, sys.argv[1:3])
XMIN, XMAX, YMIN, YMAX = -4, 4, -3, 3
fig, ax = plt.subplots(facecolor='k')

add_lines(ax, m, n, c='y', line_segments=False, oc='r')

ax.set_xlim(XMIN, XMAX)
ax.set_ylim(YMIN, YMAX)
ax.axis('off')
plt.show()

Current rating: 4