import math
import os
# STYLE
FONT = "Helvetica,Arial,sans-serif"
# PALETTE
INK = "#000000"
MUTED = "#8e8e93"
PALE = "#bababf"
PAPER = "#ffffff"
CONCEPT = {
"gasket": "#00cad8",
"carpet": "#008cff",
"sponge": "#6768fa",
"prime": "#ff325a",
"bound": "#ff8f2c",
"window": "#ff3d40",
"control": "#8e8e93",
}
PALETTE = {
"black": "#000000",
"white": "#ffffff",
"red": "#ff3d40",
"red-light": "#ff9d95",
"red-dark": "#a80016",
"orange": "#ff8f2c",
"orange-light": "#ffc093",
"orange-dark": "#a25400",
"yellow": "#ffd100",
"yellow-light": "#ffe591",
"yellow-dark": "#9e8100",
"green": "#32cc58",
"green-light": "#5eee79",
"green-dark": "#007f2c",
"mint": "#00d1bb",
"mint-light": "#48efd8",
"mint-dark": "#008173",
"teal": "#00cad8",
"teal-light": "#48e9f7",
"teal-dark": "#007c85",
"cyan": "#1ec9f3",
"cyan-light": "#86e2ff",
"cyan-dark": "#007c98",
"blue": "#008cff",
"blue-light": "#84bdff",
"blue-dark": "#00559f",
"indigo": "#6768fa",
"indigo-light": "#9ea9ff",
"indigo-dark": "#3c2abc",
"purple": "#d332e9",
"purple-light": "#f08aff",
"purple-dark": "#870097",
"pink": "#ff325a",
"pink-light": "#ff9a9f",
"pink-dark": "#a50030",
"brown": "#b18462",
"brown-light": "#dfaf8c",
"brown-dark": "#754c2b",
"gray": "#8e8e93",
"gray-light": "#bababf",
"gray-dark": "#56565a",
}
# PALETTE END
STROKE = 1.2
THIN = 0.6
MARGIN = 36
def esc(s):
return s.replace("&", "&").replace("<", "<").replace(">", ">")
def header(width, height):
return ['"]) + "\n")
# STYLE END
# DRAW
MASS = CONCEPT["control"]
COST = CONCEPT["bound"]
CROSSING = CONCEPT["window"]
GAMMA = 0.5772156649015328606065120900824024310421
WALL = 3690
LO, HI = 2.0, 9.0
FLOOR, CEIL = 0.86, 1.14
def harmonic(n):
return math.log(n) + GAMMA + 1.0 / (2 * n)
def proved(q):
n = -((-(q - 2)) // 2)
phi = (4 / math.pi) * q + (2 * q / math.pi) * harmonic(n) + (1 - 2 / math.pi) * (q - 2) + 0.727
return 1.0 + phi / q
def mass(q):
return math.log(q - 1) / math.log(q)
def cost(q):
return 0.75 + math.log(proved(q)) / math.log(q)
def samples(f, steps=112):
out = []
for i in range(steps + 1):
x = LO + (HI - LO) * i / steps
out.append((x, f(int(round(10 ** x)))))
return out
def main():
width, height = 640, 300
left, right = MARGIN + 28, width - MARGIN
top, bottom = MARGIN - 6, height - MARGIN - 8
parts = header(width, height)
def at(x, y):
return (left + (right - left) * (x - LO) / (HI - LO), bottom - (bottom - top) * (y - FLOOR) / (CEIL - FLOOR))
curve_mass = samples(mass)
curve_cost = samples(cost)
wall = math.log10(WALL)
band = [at(*p) for p in curve_cost if p[0] >= wall]
band = [at(wall, mass(WALL))] + band + [at(HI, mass(int(10 ** HI)))]
band += [at(*p) for p in reversed([p for p in curve_mass if p[0] >= wall])]
parts.append('' % (" ".join("%.1f,%.1f" % p for p in band), COST))
parts.append(line(left, bottom, right, bottom, PALE, THIN))
parts.append(line(left, top, left, bottom, PALE, THIN))
for e in range(2, 10):
x, y = at(e, FLOOR)
parts.append(line(x, y, x, y + 4, PALE, THIN))
parts.append(text(x, y + 16, "1e%d" % e, size=9, fill=MUTED, anchor="middle"))
for v in (0.90, 1.00, 1.10):
x, y = at(LO, v)
parts.append(line(x - 4, y, x, y, PALE, THIN))
parts.append(text(x - 8, y + 3, "%.2f" % v, size=9, fill=MUTED, anchor="end"))
xw, _ = at(wall, FLOOR)
parts.append(line(xw, top, xw, bottom, CROSSING, THIN))
parts.append(text(xw + 5, top + 10, "q = 3690", size=9, fill=CROSSING))
for series, colour in ((curve_mass, MASS), (curve_cost, COST)):
pts = " ".join("%.1f,%.1f" % at(*p) for p in series)
parts.append('' % (pts, colour, STROKE))
parts.append(circle(*at(wall, mass(WALL)), 3.0, INK))
parts.append(text(at(6.3, 1.030)[0], at(6.3, 1.030)[1], "mass exponent", size=10, fill=MASS))
parts.append(text(at(6.3, 0.895)[0], at(6.3, 0.895)[1], "cost exponent", size=10, fill=COST))
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "figures")
os.makedirs(out, exist_ok=True)
save(parts, os.path.join(out, "figure.svg"))
print("drew figures/figure.svg")
if __name__ == "__main__":
main()