Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
duyuefeng0708
GitHub Repository: duyuefeng0708/Cryptography-From-First-Principle
Path: blob/main/scripts/generate_readme_images.py
483 views
unlisted
1
#!/usr/bin/env python3
2
"""Generate README showcase images using matplotlib (no SageMath needed)."""
3
4
import numpy as np
5
import matplotlib
6
matplotlib.use('Agg')
7
import matplotlib.pyplot as plt
8
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch
9
from pathlib import Path
10
11
OUT = Path(__file__).resolve().parent.parent / "docs" / "images"
12
OUT.mkdir(parents=True, exist_ok=True)
13
14
# Consistent style
15
plt.rcParams.update({
16
'figure.facecolor': '#0d1117',
17
'axes.facecolor': '#0d1117',
18
'text.color': '#e6edf3',
19
'axes.labelcolor': '#e6edf3',
20
'xtick.color': '#8b949e',
21
'ytick.color': '#8b949e',
22
'axes.edgecolor': '#30363d',
23
'grid.color': '#21262d',
24
'font.family': 'monospace',
25
})
26
27
CYAN = '#58a6ff'
28
GREEN = '#3fb950'
29
ORANGE = '#d29922'
30
RED = '#f85149'
31
PURPLE = '#bc8cff'
32
PINK = '#f778ba'
33
34
35
def generate_elliptic_curve():
36
"""Elliptic curve y^2 = x^3 - x + 1 with point addition."""
37
fig, ax = plt.subplots(figsize=(8, 6))
38
39
# Curve
40
x = np.linspace(-1.5, 2.5, 2000)
41
rhs = x**3 - x + 1
42
mask = rhs >= 0
43
y = np.sqrt(np.maximum(rhs, 0))
44
ax.plot(x[mask], y[mask], color=CYAN, linewidth=2.5)
45
ax.plot(x[mask], -y[mask], color=CYAN, linewidth=2.5)
46
47
# Two points P and Q on the curve
48
px, py = 0.0, 1.0 # P: y^2 = 0 - 0 + 1 = 1
49
qx, qy = 1.0, 1.0 # Q: y^2 = 1 - 1 + 1 = 1
50
51
# Line through P and Q (slope = 0 since py == qy)
52
m = (qy - py) / (qx - px) if qx != px else None
53
if m is not None:
54
# Third intersection: x^3 - x + 1 - (m*(x - px) + py)^2 = 0
55
# For m=0, b=1: x^3 - x + 1 - 1 = x^3 - x = x(x-1)(x+1)
56
# Roots: x = -1, 0, 1. Third root is x = -1
57
rx = -1.0
58
ry = m * (rx - px) + py # = 1.0
59
sx, sy = rx, -ry # Reflect: P + Q = (rx, -ry) = (-1, -1)
60
61
# Draw the secant line
62
x_line = np.linspace(-1.8, 1.8, 100)
63
y_line = m * (x_line - px) + py
64
ax.plot(x_line, y_line, '--', color=ORANGE, linewidth=1.2, alpha=0.7)
65
66
# Vertical reflection line
67
ax.plot([rx, rx], [ry, sy], ':', color='#8b949e', linewidth=1.2, alpha=0.6)
68
69
# Points
70
ax.plot(px, py, 'o', color=GREEN, markersize=12, zorder=5)
71
ax.annotate('P', (px, py), textcoords='offset points', xytext=(-15, 10),
72
fontsize=14, fontweight='bold', color=GREEN)
73
74
ax.plot(qx, qy, 'o', color=GREEN, markersize=12, zorder=5)
75
ax.annotate('Q', (qx, qy), textcoords='offset points', xytext=(10, 10),
76
fontsize=14, fontweight='bold', color=GREEN)
77
78
ax.plot(rx, ry, 's', color=PURPLE, markersize=10, zorder=5, alpha=0.6)
79
ax.annotate('R', (rx, ry), textcoords='offset points', xytext=(-18, 8),
80
fontsize=12, color=PURPLE, alpha=0.7)
81
82
ax.plot(sx, sy, '*', color=PINK, markersize=18, zorder=5)
83
ax.annotate('P + Q', (sx, sy), textcoords='offset points', xytext=(10, -15),
84
fontsize=14, fontweight='bold', color=PINK)
85
86
ax.set_xlim(-2, 2.8)
87
ax.set_ylim(-3, 3)
88
ax.set_aspect('equal')
89
ax.grid(True, alpha=0.3)
90
ax.set_title('Elliptic Curve Point Addition', fontsize=16, pad=15, color='#e6edf3')
91
ax.set_xlabel('$x$', fontsize=13)
92
ax.set_ylabel('$y$', fontsize=13)
93
94
# Equation label
95
ax.text(1.8, -2.4, r'$y^2 = x^3 - x + 1$', fontsize=13, color=CYAN,
96
style='italic', ha='center')
97
98
fig.tight_layout()
99
fig.savefig(OUT / 'elliptic-curve.png', dpi=150, bbox_inches='tight',
100
facecolor=fig.get_facecolor())
101
plt.close(fig)
102
print(f" Saved {OUT / 'elliptic-curve.png'}")
103
104
105
def generate_lattice():
106
"""2D lattice with good vs bad basis."""
107
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5.5))
108
109
# Good basis (short, nearly orthogonal)
110
b1_good = np.array([2, 1])
111
b2_good = np.array([0, 2])
112
113
# Bad basis (long, nearly parallel)
114
b1_bad = np.array([2, 5])
115
b2_bad = np.array([4, 7])
116
117
for ax, b1, b2, title, color in [
118
(ax1, b1_good, b2_good, '"Good" Basis (Short, Orthogonal)', GREEN),
119
(ax2, b1_bad, b2_bad, '"Bad" Basis (Long, Nearly Parallel)', RED),
120
]:
121
# Generate lattice points
122
pts = []
123
for i in range(-4, 5):
124
for j in range(-4, 5):
125
p = i * b1 + j * b2
126
if -10 <= p[0] <= 10 and -10 <= p[1] <= 10:
127
pts.append(p)
128
129
pts = np.array(pts)
130
ax.scatter(pts[:, 0], pts[:, 1], color=CYAN, s=30, zorder=4, alpha=0.8)
131
132
# Basis vectors as arrows
133
origin = np.array([0, 0])
134
ax.annotate('', xy=b1, xytext=origin,
135
arrowprops=dict(arrowstyle='->', color=color, lw=2.5))
136
ax.annotate('', xy=b2, xytext=origin,
137
arrowprops=dict(arrowstyle='->', color=ORANGE, lw=2.5))
138
139
ax.annotate(r'$\mathbf{b}_1$', xy=b1 / 2, textcoords='offset points',
140
xytext=(8, -12), fontsize=13, fontweight='bold', color=color)
141
ax.annotate(r'$\mathbf{b}_2$', xy=b2 / 2, textcoords='offset points',
142
xytext=(-20, 5), fontsize=13, fontweight='bold', color=ORANGE)
143
144
# Origin
145
ax.plot(0, 0, 'o', color='white', markersize=8, zorder=5)
146
147
ax.set_xlim(-8, 8)
148
ax.set_ylim(-8, 8)
149
ax.set_aspect('equal')
150
ax.grid(True, alpha=0.2)
151
ax.set_title(title, fontsize=12, pad=10, color='#e6edf3')
152
153
fig.suptitle('Lattice Basis Reduction', fontsize=16, y=1.02, color='#e6edf3')
154
fig.tight_layout()
155
fig.savefig(OUT / 'lattice-basis.png', dpi=150, bbox_inches='tight',
156
facecolor=fig.get_facecolor())
157
plt.close(fig)
158
print(f" Saved {OUT / 'lattice-basis.png'}")
159
160
161
def generate_module_flow():
162
"""Visual diagram of the 4-phase learning flow."""
163
fig, ax = plt.subplots(figsize=(12, 3.5))
164
ax.set_xlim(0, 12)
165
ax.set_ylim(0, 3.5)
166
ax.axis('off')
167
168
phases = [
169
('Explore', 'SageMath\nNotebooks', CYAN, 1.5),
170
('Implement', 'Rust\nExercises', GREEN, 4.5),
171
('Break', 'Attack Weak\nPrimitives', ORANGE, 7.5),
172
('Connect', 'Real-World\nProtocols', PURPLE, 10.5),
173
]
174
175
box_w, box_h = 2.2, 2.2
176
177
for name, subtitle, color, cx in phases:
178
# Box
179
rect = FancyBboxPatch((cx - box_w/2, 0.6), box_w, box_h,
180
boxstyle="round,pad=0.15",
181
facecolor=color + '18', edgecolor=color,
182
linewidth=2)
183
ax.add_patch(rect)
184
185
# Phase name
186
ax.text(cx, 2.15, name, ha='center', va='center',
187
fontsize=16, fontweight='bold', color=color)
188
189
# Subtitle
190
ax.text(cx, 1.35, subtitle, ha='center', va='center',
191
fontsize=11, color='#8b949e')
192
193
# Arrows between phases
194
for i in range(3):
195
x_start = phases[i][3] + box_w/2 + 0.05
196
x_end = phases[i+1][3] - box_w/2 - 0.05
197
ax.annotate('', xy=(x_end, 1.7), xytext=(x_start, 1.7),
198
arrowprops=dict(arrowstyle='->', color='#8b949e',
199
lw=2, connectionstyle='arc3,rad=0'))
200
201
# Stats bar at bottom
202
ax.text(6, 0.15, '123 notebooks | 57 Rust exercises | 12 modules | BSc to postgrad',
203
ha='center', va='center', fontsize=11, color='#8b949e', style='italic')
204
205
fig.tight_layout()
206
fig.savefig(OUT / 'module-flow.png', dpi=150, bbox_inches='tight',
207
facecolor=fig.get_facecolor())
208
plt.close(fig)
209
print(f" Saved {OUT / 'module-flow.png'}")
210
211
212
def generate_social_preview():
213
"""1280x640 social preview for GitHub link sharing."""
214
fig, ax = plt.subplots(figsize=(12.8, 6.4))
215
ax.set_xlim(0, 12.8)
216
ax.set_ylim(0, 6.4)
217
ax.axis('off')
218
219
# Small elliptic curve in the bottom-right corner
220
x = np.linspace(-1.2, 2.2, 1500)
221
rhs = x**3 - x + 1
222
mask = rhs >= 0
223
y = np.sqrt(np.maximum(rhs, 0))
224
# Scale and shift to bottom-right
225
sx, sy_offset, scale = 9.5, 1.8, 0.9
226
ax.plot(x[mask] * scale + sx, y[mask] * scale + sy_offset, color=CYAN, linewidth=1.8, alpha=0.35)
227
ax.plot(x[mask] * scale + sx, -y[mask] * scale + sy_offset, color=CYAN, linewidth=1.8, alpha=0.35)
228
229
# Title
230
ax.text(6.4, 4.6, 'Crypto From First Principles', ha='center', va='center',
231
fontsize=36, fontweight='bold', color='#e6edf3')
232
233
# Stats line
234
ax.text(6.4, 3.6, '123 notebooks | 57 Rust exercises | 12 modules',
235
ha='center', va='center', fontsize=18, color=CYAN)
236
237
# Tagline
238
ax.text(6.4, 2.6, 'Learn the math. Build it in Rust. Break it. See it in the wild.',
239
ha='center', va='center', fontsize=16, color='#8b949e', style='italic')
240
241
# Phase chips at the bottom
242
phases = [
243
('Explore', CYAN, 2.5),
244
('Implement', GREEN, 5.0),
245
('Break', ORANGE, 7.5),
246
('Connect', PURPLE, 10.0),
247
]
248
for label, color, cx in phases:
249
rect = FancyBboxPatch((cx - 1.0, 0.8), 2.0, 0.9,
250
boxstyle="round,pad=0.12",
251
facecolor=color + '20', edgecolor=color,
252
linewidth=1.5)
253
ax.add_patch(rect)
254
ax.text(cx, 1.25, label, ha='center', va='center',
255
fontsize=14, fontweight='bold', color=color)
256
257
fig.tight_layout(pad=0)
258
fig.savefig(OUT / 'social-preview.png', dpi=100, bbox_inches='tight',
259
facecolor=fig.get_facecolor(), pad_inches=0.2)
260
plt.close(fig)
261
print(f" Saved {OUT / 'social-preview.png'}")
262
263
264
if __name__ == '__main__':
265
print("Generating README images...")
266
generate_elliptic_curve()
267
generate_lattice()
268
generate_module_flow()
269
generate_social_preview()
270
print("Done!")
271
272