codex/coprime-squarefree-integers-square-sieve.py

coprime-squarefree-integers-square-sieve.py
"""Original exact finite square-divisor sieve; Python 3.14, matplotlib 3.10.

Run from _media; the PNG is written to the current working directory.
Every exclusion displays its smallest prime-square divisor.
"""

from math import isqrt
from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.patches import Patch, Rectangle


def primes_through(bound):
    return [p for p in range(2, bound + 1)
            if all(p % d for d in range(2, isqrt(p) + 1))]


def main():
    bound = 60
    primes = primes_through(isqrt(bound))
    assert primes == [2, 3, 5, 7]
    witnesses = {n: next((p * p for p in primes if n % (p * p) == 0), None)
                 for n in range(1, bound + 1)}
    count = sum(w is None for w in witnesses.values())
    assert count == 37
    plt.rcParams.update({'font.family': 'DejaVu Sans', 'font.size': 12})
    fig, ax = plt.subplots(figsize=(9.6, 6.5), facecolor='white')
    fig.subplots_adjust(left=0.045, right=0.985, top=0.79, bottom=0.19)
    green, red = '#d5eee5', '#f8ded3'
    dark_green, dark_red = '#12574e', '#8f321e'
    fig.suptitle('Square-free means no repeated prime factor', fontsize=19, y=0.96)
    fig.text(0.5, 0.89, 'Exact enumeration for 1–60: test prime squares 4, 9, 25, 49',
             ha='center', fontsize=12)
    ax.set_xlim(0, 10)
    ax.set_ylim(0, 6)
    ax.axis('off')
    for n, witness in witnesses.items():
        col = (n - 1) % 10
        y = 5 - (n - 1) // 10
        squarefree = witness is None
        ax.add_patch(Rectangle((col + 0.04, y + 0.045), 0.92, 0.91,
                               facecolor=green if squarefree else red,
                               edgecolor='white', linewidth=1))
        color = dark_green if squarefree else dark_red
        ax.text(col + 0.5, y + (0.51 if squarefree else 0.64), str(n),
                ha='center', va='center', fontsize=16, color=color, weight='bold')
        if witness is not None:
            ax.text(col + 0.5, y + 0.28, f'{witness} | {n}',
                    ha='center', va='center', fontsize=10, color=dark_red)
    fig.legend(handles=[Patch(facecolor=green, label=f'Square-free: {count} integers'),
                        Patch(facecolor=red, label='Excluded: a displayed square divides n')],
               loc='lower center', bbox_to_anchor=(0.5, 0.09), ncol=2,
               frameon=False, fontsize=11)
    fig.text(0.5, 0.04, '6 and 10 are square-free; their product 60 is not, because 2 occurs twice.',
             ha='center', fontsize=12)
    output = Path(__file__).with_suffix('.png').name
    fig.savefig(output, dpi=110, facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()