stack.py

5.6 kB · python · 190 lines

1import hashlib2import time3from math import gcd45FAREY_N = 556Q = 99737M = 2 * Q8PROBES = 489PROBE_NS = [55, 5555, 10 ** 18]10SWEEP_NS = [1, 2, 55, 5555, 19945, 19946, 19947, 40001]11R = 51212D = 2 * R13IMAGE_N = 5514ODDS = list(range(1, IMAGE_N + 1, 2))15K = len(ODDS)1617def farey_nodes(n_max):18    out = []19    for b in range(1, n_max + 1):20        for a in range(1, b + 1):21            if gcd(a, b) == 1:22                out.append((a, b))23    return out2425def farey_by_form(n_max):26    return ["%d/%d:%d" % (a, b, n_max // b) for (a, b) in farey_nodes(n_max)]2728def farey_by_membership(n_max):29    scales = []30    for n in range(1, n_max + 1):31        scales.append(set((k // gcd(k, n), n // gcd(k, n)) for k in range(1, n + 1)))32    lines = []33    for (a, b) in farey_nodes(n_max):34        lines.append("%d/%d:%d" % (a, b, sum(1 for s in scales if (a, b) in s)))35    return lines3637def digest(lines):38    return hashlib.sha256("\n".join(lines).encode("utf-8")).hexdigest()3940def brightness_sum(lines):41    return sum(int(t.split(":")[1]) for t in lines)4243def probe_points():44    return [((137 * i + 61) % Q, (211 * i + 97) % Q) for i in range(PROBES)]4546def bad_residues(a, b):47    return [r for r in range(1, M, 2) if (r * a) % M >= Q and (r * b) % M >= Q]4849def brightness_closed(bad, n_max):50    dark = 051    for r in bad:52        if r <= n_max:53            dark += (n_max - r) // M + 154    return (n_max + 1) // 2 - dark5556def good_residues(a, b):57    return [r for r in range(1, M, 2) if (r * a) % M < Q or (r * b) % M < Q]5859def brightness_direct(good, n_max):60    lit = 061    for r in good:62        if r <= n_max:63            lit += (n_max - r) // M + 164    return lit6566def brightness_literal(a, b, n_max):67    c = 068    for n in range(1, n_max + 1, 2):69        if not (((n * a) // Q) % 2 == 1 and ((n * b) // Q) % 2 == 1):70            c += 171    return c7273def column_masks():74    masks = []75    for j in range(R):76        u = 2 * j + 177        m = 078        for k, n in enumerate(ODDS):79            if ((n * u) // D) % 2 == 1:80                m |= 1 << k81        masks.append(m)82    return masks8384def render_masks():85    masks = column_masks()86    buf = bytearray(R * R)87    for i in range(R):88        my = masks[i]89        row = i * R90        for j in range(R):91            buf[row + j] = K - (my & masks[j]).bit_count()92    return buf9394def render_layers():95    xs = [2 * j + 1 for j in range(R)]96    img = [[0] * R for _ in range(R)]97    for n in ODDS:98        odd_col = [((n * v) // D) % 2 == 1 for v in xs]99        for i in range(R):100            row = img[i]101            if ((n * xs[i]) // D) % 2 == 1:102                for j in range(R):103                    if not odd_col[j]:104                        row[j] += 1105            else:106                for j in range(R):107                    row[j] += 1108    buf = bytearray()109    for i in range(R):110        buf.extend(bytes(img[i]))111    return buf112113def render_pixels():114    buf = bytearray()115    for i in range(R):116        y = 2 * i + 1117        for j in range(R):118            x = 2 * j + 1119            c = 0120            for n in ODDS:121                if not (((n * x) // D) % 2 == 1 and ((n * y) // D) % 2 == 1):122                    c += 1123            buf.append(c)124    return buf125126def main():127    form = farey_by_form(FAREY_N)128    member = farey_by_membership(FAREY_N)129    print("farey N", FAREY_N)130    print("farey nodes", len(form))131    print("farey brightness sum", brightness_sum(form))132    print("farey N(N+1)/2", FAREY_N * (FAREY_N + 1) // 2)133    print("farey digest closed form", digest(form))134    print("farey digest membership", digest(member))135    print("farey digests equal", digest(form) == digest(member))136137    pts = probe_points()138    t0 = time.perf_counter()139    tables = [bad_residues(a, b) for (a, b) in pts]140    t_tables = time.perf_counter() - t0141    print("probe count", len(pts))142    print("probe modulus 2q", M)143    print("probe residue tables seconds", round(t_tables, 4))144145    values = {}146    for n_max in PROBE_NS:147        t0 = time.perf_counter()148        values[n_max] = [brightness_closed(t, n_max) for t in tables]149        dt = time.perf_counter() - t0150        print("closed form N", n_max, "seconds", round(dt, 4))151        print("closed form N", n_max, "first four", *values[n_max][:4])152153    t0 = time.perf_counter()154    twins = [good_residues(a, b) for (a, b) in pts]155    print("direct count residue tables seconds", round(time.perf_counter() - t0, 4))156    for n_max in PROBE_NS:157        t0 = time.perf_counter()158        other = [brightness_direct(t, n_max) for t in twins]159        dt = time.perf_counter() - t0160        print("direct count N", n_max, "seconds", round(dt, 4))161        print("direct count N", n_max, "equals closed form", other == values[n_max])162163    for n_max in (55, 5555):164        agree = sum(165            1166            for i, (a, b) in enumerate(pts)167            if values[n_max][i] == brightness_literal(a, b, n_max)168        )169        print("probes closed equal literal N", n_max, "%d/%d" % (agree, len(pts)))170171    bad = 0172    for i, (a, b) in enumerate(pts):173        for n_max in SWEEP_NS:174            if brightness_closed(tables[i], n_max) != brightness_literal(a, b, n_max):175                bad += 1176    print("sweep N values", *SWEEP_NS)177    print("sweep comparisons", len(pts) * len(SWEEP_NS))178    print("sweep mismatches", bad)179180    a = render_masks()181    b = render_layers()182    c = render_pixels()183    print("image side", R)184    print("image layers", K)185    print("image sha256 masks", hashlib.sha256(bytes(a)).hexdigest())186    print("image sha256 layers", hashlib.sha256(bytes(b)).hexdigest())187    print("image sha256 pixels", hashlib.sha256(bytes(c)).hexdigest())188    print("image three routes equal", a == b == c)189190main()