smith_window.py

11.4 kB · python · 412 lines

1import sys23# BITMASK POLYNOMIALS OVER F245def polymul2(a, b):6    r = 07    while b:8        lb = b & -b9        r ^= a * lb10        b ^= lb11    return r1213def pow2mask(m):14    r = 115    s = 116    while m:17        if m & 1:18            r ^= r << s19        s <<= 120        m >>= 121    return r2223def divmod2(a, b):24    db = b.bit_length() - 125    q = 026    while a and a.bit_length() - 1 >= db:27        sh = a.bit_length() - 1 - db28        q ^= 1 << sh29        a ^= b << sh30    return q, a3132def bits(v):33    out = []34    while v:35        lb = v & -v36        out.append(lb.bit_length() - 1)37        v ^= lb38    return out3940def echelon(vecs):41    B = []42    for v in vecs:43        for u in B:44            v = min(v, v ^ u)45        if v:46            B.append(v)47            B.sort(reverse=True)48    return B4950def reduce_by(v, B):51    for u in B:52        v = min(v, v ^ u)53    return v5455def pivots(vecs):56    piv = {}57    for v in vecs:58        while v:59            t = v.bit_length() - 160            if t in piv:61                v ^= piv[t]62            else:63                piv[t] = v64                break65    return piv6667def residue(v, piv, keys):68    for t in keys:69        if (v >> t) & 1:70            v ^= piv[t]71    return v7273def spans(v, piv):74    while v:75        t = v.bit_length() - 176        if t not in piv:77            return False78        v ^= piv[t]79    return True8081def nullspace_right(rows, n):82    piv = {}83    for r in rows:84        for p in sorted(piv, reverse=True):85            if (r >> p) & 1:86                r ^= piv[p]87        if r:88            q = r.bit_length() - 189            for pp in list(piv):90                if (piv[pp] >> q) & 1:91                    piv[pp] ^= r92            piv[q] = r93    out = []94    for f in [c for c in range(n) if c not in piv]:95        v = 1 << f96        for p, r in piv.items():97            if (r >> f) & 1:98                v |= 1 << p99        out.append(v)100    return out101102# JACOBSTHAL AND THE SLOT103104def jac(k):105    return 0 if k < 0 else (2 ** k - (-1) ** k) // 3106107def slot(R):108    b = 0109    while (1 << b) < 3 * R - 1:110        b += 1111    g = abs(2 * R - (1 << (b - 1)) - 1)112    e = 1113    while jac(e) < (g + 1) // 2:114        e += 1115    return b, g, e, b - 1 - e116117def window(R, b):118    A = 1 << b119    i0 = max(2, 4 * R - A)120    hi = (6 * R + 2 - A) // 3121    hi -= hi % 2122    return i0, (hi - i0) // 2123124def fibpoly(t):125    if t == 0:126        return 1127    a, b = 0b11, 1128    for _ in range(t - 1):129        a, b = polymul2(a, 2) ^ b, a130    return a131132def law_e(D):133    R = (D - 1) // 2134    b, gslot, e, k = slot(R)135    i0, K = window(R, b)136    tt = (jac(k) - 1) // 2137    C = K - (2 * jac(e - 1) if k % 2 == 0 else 0)138    w = C - tt * (1 << e)139    ce = 2 * jac(e - 2) - 1 if e >= 3 else 1140    m = max(0, 2 * w - ce)141    g = 0142    for a in bits(fibpoly(tt)):143        g ^= 1 << (a * (1 << e))144    return K, C, g << m, min(w + 1, ce + 1 - w)145146def tent(R):147    b, gslot, e, k = slot(R)148    N = jac(e) - jac(e - 1)149    u = (gslot + 1) // 2 - jac(e - 1) - 1150    p = u if R > (1 << (b - 2)) else N - 1 - u151    return p, min(p, N - 1 - p)152153def reach_law(R):154    b, gslot, e, k = slot(R)155    p, w = tent(R)156    if e == 1 and k % 2:157        return 1 if R == 1 << (b - 2) else (3 if R == 3 else 5)158    return 3 * w + (2 if e % 2 == 0 else 0) + ((1 + p % 2) if k % 2 else 0)159160# THE MOD-4 SYMBOL AND THE KERNEL FAMILY161162FW = 20163FM = (1 << FW) - 1164165def symbol4(D):166    out = [0] * (2 * D + 1)167    c = 1168    for k in range(D):169        v = c % 4170        if v:171            out[2 * k] = (out[2 * k] + v) % 4172            out[2 * k + 1] = (out[2 * k + 1] + v * D) % 4173            out[2 * k + 2] = (out[2 * k + 2] + v) % 4174        c = c * (D - 1 - k) // (k + 1)175    return out176177def symbol4pack(D):178    p = 0179    for i, v in enumerate(symbol4(D)):180        if v:181            p |= v << (FW * i)182    return p183184def family(D):185    R = (D - 1) // 2186    b, gslot, e, k = slot(R)187    i0, K = window(R, b)188    A = 1 << b189    G = polymul2(pow2mask(4 * R), 0b111)190    out = []191    for j in range(K + 1):192        i = i0 + 2 * j193        q = 1194        for a in bits(i):195            q = polymul2(q, 1 ^ (1 << (3 << a)))196        f = polymul2(1 << ((6 * R + 2 - 3 * i - A) // 2), q)197        f ^= f << A198        h, r = divmod2(f, G)199        assert r == 0, "family element off the ideal at D=%d" % D200        out.append(h)201    return out, K202203def colblock(R, G, wide):204    dec = [0, 0, 0]205    for a in bits(G):206        dec[a % 3] |= 1 << (a // 3)207    m = (1 << (R + 1)) - 1208209    def T(a):210        c = 1 - R + a211        r = c % 3212        q = (c - r) // 3213        v = dec[r]214        return ((v >> q) if q >= 0 else (v << -q)) & m215216    out = [T(0)]217    for j in range(1, wide):218        out.append(T(-j) ^ T(j))219    return out220221def columns(D):222    R = (D - 1) // 2223    return colblock(R, polymul2(pow2mask(4 * R), 0b111), R + 1)224225def obstruction(D, X, Ppack):226    R = (D - 1) // 2227    out = []228    for h in X:229        hp = 0230        for a in bits(h):231            hp |= 1 << (FW * a)232        pr = hp * Ppack233        o = 0234        for nu in range(R + 1):235            s = (pr >> (FW * (3 * nu + 1))) & FM236            assert s % 2 == 0, "family element not in the mod-2 kernel at D=%d" % D237            if s % 4 == 2:238                o |= 1 << nu239        out.append(o)240    return out241242# THE LAYER-2 WINDOW243244def layer2(D):245    X, K = family(D)246    cols = columns(D)247    piv = pivots(cols)248    keys = sorted(piv, reverse=True)249    raw = obstruction(D, X, symbol4pack(D))250    red = [residue(o, piv, keys) for o in raw]251    nb = max((o.bit_length() for o in red), default=0)252    rows = []253    for nu in range(nb):254        r = 0255        for j in range(K + 1):256            if (red[j] >> nu) & 1:257                r |= 1 << j258        if r:259            rows.append(r)260    V2 = echelon(nullspace_right(rows, K + 1))261    return K, V2, raw, cols, X, piv262263def shift_law(D, X, K, raw, piv):264    R = (D - 1) // 2265    G = polymul2(pow2mask(4 * R), 0b111)266    box = (1 << (2 * R + 1)) - 1267    for j in range(K):268        H = X[j]269        hi, lo = H << 3, H >> 3270        both = hi & lo271        assert X[j + 1] == (hi ^ lo), "family shift breaks at D=%d j=%d" % (D, j)272        Z = H ^ both273        assert (Z | box) == box, "Z leaves the box at D=%d j=%d" % (D, j)274        assert Z == rev_box(Z, R), "Z not palindromic at D=%d j=%d" % (D, j)275        ZG = polymul2(Z, G)276        az = 0277        for nu in range(R + 1):278            if (ZG >> (3 * nu + 1)) & 1:279                az |= 1 << nu280        assert spans(az, piv), "A(Z) off the image at D=%d j=%d" % (D, j)281        assert raw[j + 1] == fold_shift(raw[j], R) ^ az, "Lambda law breaks at D=%d j=%d" % (D, j)282    return True283284def rev_box(v, R):285    out = 0286    for a in bits(v):287        out |= 1 << (2 * R - a)288    return out289290def fold_shift(f, R):291    out = 0292    for nu in range(R + 1):293        a = f >> (nu - 1) & 1 if nu >= 1 else 0294        b = (f >> (nu + 1)) & 1 if nu + 1 <= R else ((f >> (R - 1)) & 1 if nu == R else 0)295        if a ^ b:296            out |= 1 << nu297    return out298299def corrector_index(cols, target):300    piv = {}301    for J, c in enumerate(cols):302        v = c303        while v:304            t = v.bit_length() - 1305            if t in piv:306                v ^= piv[t]307            else:308                piv[t] = v309                break310        if spans(target, piv):311            return J312    return None313314# THE SWEEP315316def sweep(lo, hi):317    n = bw = bg = bc = bm = br = bt = bf = ne = fr = 0318    cap, flo, tie = [], [], []319    r4 = [0] * 4320    r8 = [0] * 8321    for D in range(lo, hi + 1, 2):322        R = (D - 1) // 2323        b, gs, e, k = slot(R)324        K, V2, raw, cols, X, piv = layer2(D)325        assert V2, "empty layer-2 window at D=%d" % D326        assert X[K].bit_length() - 1 <= 2 * R, "family top leaves the box at D=%d" % D327        gen = min(V2)328        dg = gen.bit_length() - 1329        C = max(v.bit_length() - 1 for v in V2)330        n += 1331        r4[R % 4] += 1332        r8[R % 8] += 1333        if echelon([polymul2(gen, 1 << s) for s in range(C - dg + 1)]) != V2:334            bw += 1335            print("window fails at D=%d" % D)336        Kp, Cp, gp, L2p = law_e(D)337        if (Kp, Cp, gp, L2p) != (K, C, gen, len(V2)):338            print("law E fails at D=%d: %r against %r" % (D, (Kp, Cp, gp, L2p), (K, C, gen, len(V2))))339            if gp != gen:340                bg += 1341            if Cp != C or L2p != len(V2):342                bc += 1343        shift_law(D, X, K, raw, piv)344        om = 0345        for j in bits(gen):346            om ^= raw[j]347        J = corrector_index(cols, om)348        if k % 2 or e == 1:349            assert C == K, "upper half not free at D=%d" % D350            fr += 1351        else:352            assert K - C == 2 * jac(e - 1), "ceiling deficit is not 2J(e-1) at D=%d" % D353        if tent(R)[1] != C - dg:354            bt += 1355            print("tent identity fails at D=%d" % D)356        rl = reach_law(R)357        if rl != R - J:358            br += 1359            print("reach law fails at D=%d: %d against %d" % (D, rl, R - J))360        a, bq = K - dg, rl // 3361        if e == 1 and k % 2 and R > (1 << (b - 2)):362            ne += 1363            if (bq, C - dg, a) != (1, 0, 0):364                bf += 1365                print("escaping row misread at D=%d" % D)366        elif bq != C - dg + (1 if (k % 2 and e % 2 == 0) else 0):367            bf += 1368            print("floor identity fails at D=%d" % D)369        if C - dg != min(a, bq):370            bm += 1371            print("corrector law fails at D=%d" % D)372        elif a < bq:373            cap.append(D)374        elif bq < a:375            flo.append(D)376        else:377            tie.append(D)378    print("odd D = %d..%d: %d rows" % (lo, hi, n))379    print("R mod 4 classes %r, R mod 8 classes %r" % (r4, r8))380    print("window structure V2 = g F_2[z]_(<= C - deg g): %d/%d" % (n - bw, n))381    print("Law E generator z^m c_t(z^(2^e)): %d/%d" % (n - bg, n))382    print("ceiling C = K - 2J(e-1)[k even] and L_2: %d/%d" % (n - bc, n))383    print("the family shift law H^(j+1) = psi H^(j) - 2 Z_j and ob(X_(j+1)) = Lambda ob(X_j) + A(Z_j): %d/%d" % (n, n))384    print("the slot tent w = min(p, N - 1 - p) = C - deg g: %d/%d" % (n - bt, n))385    print("the reach law reach = 3w + 2[e even] + [k odd](1 + p mod 2), D = 4^m + 3 apart: %d/%d" % (n - br, n))386    print("floor(reach/3) = C - deg g + [k odd and e even] off the %d rows D = 4^m + 3, where it reads 1 against C - deg g = K - deg g = 0: %d/%d" % (ne, n - bf, n))387    print("corrector law C - deg g = min(K - deg g, floor(reach/3)) from the closed form: %d/%d" % (n - bm, n))388    print("upper half free where C = K, that is k odd or e = 1, else deficit K - C = 2J(e-1): free %d, open %d" % (fr, n - fr))389    print("branches: floor strict %d, K cap strict %d, tie %d" % (len(flo), len(cap), len(tie)))390    print("floor-strict rows are exactly the C < K rows: %s" % (sorted(flo) == sorted(d for d in range(lo, hi + 1, 2) if law_e(d)[1] < law_e(d)[0])))391392def unboxed(lo, hi):393    n = bad = co = 0394    for D in range(lo, hi + 1, 2):395        R = (D - 1) // 2396        G = polymul2(pow2mask(4 * R), 0b111)397        piv = pivots(colblock(R, G, 4 * R + 12))398        X, K = family(D)399        n += 1400        if len(piv) != R:401            co += 1402        if any(not spans(o, piv) for o in obstruction(D, X, symbol4pack(D))):403            bad += 1404    print("unboxed corrector image has corank exactly 1: %d/%d" % (n - co, n))405    print("every family obstruction meets that image, so the unboxed layer-2 window is all of the mod-2 kernel: %d/%d" % (n - bad, n))406407if __name__ == "__main__":408    a = sys.argv[1:]409    lo = int(a[0]) if a else 5410    hi = int(a[1]) if len(a) > 1 else 1601411    sweep(lo, hi)412    unboxed(5, min(hi, 601))