import os
import sys
sys.dont_write_bytecode = True
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import verify as V
# 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
CELL = 20
PLATE = 17
SAT = CONCEPT["bound"]
SHORT = CONCEPT["window"]
EXACT = CONCEPT["control"]
REFUTED = CONCEPT["carpet"]
def cells(word):
rows, wide = V.grid(word)
return [(i, j) for i, row in enumerate(rows) for j in range(wide) if (row >> j) & 1], wide
def board(parts, x, y, word):
filled, wide = cells(word)
have = set(filled)
for i in range(wide):
for j in range(wide):
px, py = x + j * CELL, y + i * CELL
if (i, j) in have:
parts.append(rect(px, py, CELL, CELL, INK))
else:
parts.append('' % (px, py, CELL, CELL, PALE, THIN))
parts.append('' % (x, y, wide * CELL, wide * CELL, MUTED, STROKE))
return wide * CELL
def witness(parts, x, y, left, right):
top = y + CELL
width = board(parts, x, top, [left])
parts.append(text(x + width + 11, top + CELL + 5, "\u00d7", size=15, anchor="middle"))
step = x + width + 22
board(parts, step, top, [right])
parts.append(text(step + width + 11, top + CELL + 5, "=", size=15, anchor="middle"))
end = step + width + 22
span = board(parts, end, y, [left, right])
pieces = V.drawn([left, right])
parts.append(text(end + span + 12, y + 2 * CELL + 5, "%d %s" % (pieces, "piece" if pieces == 1 else "pieces"), size=12))
return pieces
def verdicts():
sat, phi = {}, {}
for a, b in V.PAIRS:
wa, wb = V.rate_weight(a, b)
sat[(a, b)] = (wa, wb) == (V.fill_weight(a), V.fill_weight(b))
phi[(a, b)] = (wa, wb) == (V.phi_weight(a), V.phi_weight(b))
return sat, phi
def ledger(parts, x, y):
sat, phi = verdicts()
codes = V.CODES
for i, a in enumerate(codes):
parts.append(text(x - 6, y + i * PLATE + PLATE - 5, str(a), size=8, fill=MUTED, anchor="end"))
parts.append(text(x + i * PLATE + PLATE / 2, y - 6, str(a), size=8, fill=MUTED, anchor="middle"))
for i, a in enumerate(codes):
for j, b in enumerate(codes):
px, py = x + j * PLATE, y + i * PLATE
if a == b:
parts.append(rect(px, py, PLATE - 1, PLATE - 1, PALE, 0.55))
continue
key = (min(a, b), max(a, b))
if j > i:
parts.append(rect(px, py, PLATE - 1, PLATE - 1, EXACT if phi[key] else REFUTED))
else:
parts.append(rect(px, py, PLATE - 1, PLATE - 1, SAT if sat[key] else SHORT))
return sum(1 for v in sat.values() if v), sum(1 for v in phi.values() if v)
def key(parts, x, y, swatch, label):
parts.append(rect(x, y, 11, 11, swatch))
parts.append(text(x + 17, y + 9, label, size=10, fill=MUTED))
def main():
width, height = 620, 600
parts = header(width, height)
parts.append(text(MARGIN, 26, "One multiset, two orders", size=12, weight="bold"))
first = witness(parts, MARGIN, 40, 3, 6)
second = witness(parts, MARGIN, 40 + 5 * CELL, 6, 3)
parts.append(text(MARGIN, 40 + 9 * CELL + 22, "Four black cells either way; %d pieces above, %d below." % (first, second), size=10, fill=MUTED))
base = 40 + 10 * CELL + 42
parts.append(text(MARGIN, base - 22, "All 105 two-letter alphabets", size=12, weight="bold"))
saturating, exact = ledger(parts, MARGIN + 16, base)
legend = base + 15 * PLATE + 26
key(parts, MARGIN, legend, SAT, "lower left: exponent meets the fill ceiling (%d)" % saturating)
key(parts, MARGIN, legend + 18, SHORT, "lower left: falls short (%d)" % (105 - saturating))
key(parts, MARGIN + 300, legend, EXACT, "upper right: the constant-word rule holds (%d)" % exact)
key(parts, MARGIN + 300, legend + 18, REFUTED, "upper right: the constant-word rule is refuted (%d)" % (105 - exact))
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "figures")
os.makedirs(out, exist_ok=True)
save(parts, os.path.join(out, "ledger.svg"))
plates(os.path.join(out, "plates.tex"))
print("drew figures/ledger.svg and figures/plates.tex")
# TIKZ
def tikz_board(lines, x, y, word, unit):
filled, wide = cells(word)
have = set(filled)
for i in range(wide):
for j in range(wide):
px, py = x + j * unit, y + (wide - 1 - i) * unit
shade = "black!84" if (i, j) in have else "white"
lines.append("\\fill[%s] (%.2f,%.2f) rectangle ++(%.2f,%.2f);" % (shade, px, py, unit, unit))
lines.append("\\draw[black!22,line width=0.2pt,step=%.2f] (%.2f,%.2f) grid ++(%.2f,%.2f);" % (unit, x, y, wide * unit, wide * unit))
lines.append("\\draw[black!55,line width=0.4pt] (%.2f,%.2f) rectangle ++(%.2f,%.2f);" % (x, y, wide * unit, wide * unit))
return wide * unit
def tikz_witness(lines, x, y, left, right, unit):
top = y + unit
width = tikz_board(lines, x, top, [left], unit)
lines.append("\\node at (%.2f,%.2f) {$\\otimes$};" % (x + width + 0.45, top + unit))
step = x + width + 0.9
tikz_board(lines, step, top, [right], unit)
lines.append("\\node at (%.2f,%.2f) {$=$};" % (step + width + 0.45, top + unit))
end = step + width + 0.9
span = tikz_board(lines, end, y, [left, right], unit)
pieces = V.drawn([left, right])
lines.append("\\node[anchor=west,font=\\small] at (%.2f,%.2f) {%d %s};" % (end + span + 0.22, y + 2 * unit, pieces, "piece" if pieces == 1 else "pieces"))
return end + span + 1.55
def plates(path):
unit = 0.30
lines = ["\\newcommand{\\witnessplate}{%", "\\begin{tikzpicture}[x=1cm,y=1cm]"]
span = tikz_witness(lines, 0.0, 0.0, 3, 6, unit)
tikz_witness(lines, span + 0.55, 0.0, 6, 3, unit)
lines += ["\\end{tikzpicture}}", "", "\\newcommand{\\ledgerplate}{%", "\\begin{tikzpicture}[x=1cm,y=1cm]"]
sat, phi = verdicts()
step = 0.34
for i, a in enumerate(V.CODES):
top = (len(V.CODES) - 1 - i) * step
lines.append("\\node[anchor=east,font=\\tiny,text=black!55] at (%.2f,%.2f) {%d};" % (-0.06, top + step / 2, a))
lines.append("\\node[anchor=south,font=\\tiny,text=black!55] at (%.2f,%.2f) {%d};" % (i * step + step / 2, len(V.CODES) * step + 0.02, a))
for j, b in enumerate(V.CODES):
px, py = j * step, top
if a == b:
shade = "black!12"
elif j > i:
shade = "black!35" if phi[(min(a, b), max(a, b))] else "blue!58!black"
else:
shade = "orange!85!black" if sat[(min(a, b), max(a, b))] else "red!72!black"
lines.append("\\fill[%s] (%.3f,%.3f) rectangle ++(%.3f,%.3f);" % (shade, px, py, step - 0.03, step - 0.03))
lines += ["\\end{tikzpicture}}", ""]
with open(path, "w") as handle:
handle.write("\n".join(lines) + "\n")
if __name__ == "__main__":
main()