Research source · python

check_ordered_suffix.py

tools/research/check_ordered_suffix.py

198 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: 76146fb994e708376c780d503ea6ed59a33340b41bee3b1e4ac5b7ae67f2175d

#!/usr/bin/env python3
"""Finite semantics audit; tuple deque/FIFO and integer reader lag are models.
Linked unit gaps and linked endpoint digits expose the actual cursor steps.
Native complexity follows from the written proof and imported strict primitives,
not Python tuple cost. Integer set/min operations below belong to test oracles.
"""
from dataclasses import dataclass, replace
from itertools import product
from random import Random
import json
from check_sparse_age_minimum import Unit, depth, numeral, decode, release, step, idle


def units(n):
    p=None
    for _ in range(n): p=Unit(p)
    return p


@dataclass(frozen=True)
class Entry:
    gap: object
    endpoint: object


def sequence(hist):
    p=0; seq=()
    for x in sorted(k for k,c in hist.items() if c):
        seq+=(Entry(units(x-p),numeral(x)),); p=x
    return seq


def inspect(seq):
    p=0; result=[]
    for e in seq:
        g=depth(e.gap); assert g>0
        p+=g; assert p==decode(e.endpoint); result.append(p)
    return tuple(result)


def splice(seq,hist,old,n):
    suffix=seq; prefix=(); acc=None; gap=suffix[0].gap if suffix else None
    old_cursor=units(old); retained=False
    for d in range(1,n+1):
        at_old=old_cursor is not None and old_cursor.tail is None
        old_cursor=old_cursor.tail if old_cursor is not None else None
        acc=Unit(acc); retained=False
        if suffix:
            assert gap is not None
            gap=gap.tail
            if gap is None:
                # hist[d] models the paid, parallel current histogram cursor.
                if not(at_old and hist[d]==1):
                    prefix+=(Entry(acc,suffix[0].endpoint),); acc=None; retained=True
                suffix=suffix[1:]; gap=suffix[0].gap if suffix else None
    if n<=old: return seq
    if not retained:
        assert acc is not None
        prefix+=(Entry(acc,numeral(n)),)  # models input-paid position numeral
    if suffix:
        assert gap is not None
        suffix=(replace(suffix[0],gap=gap),)+suffix[1:]
    return prefix+suffix


@dataclass(frozen=True)
class Row:
    lower: object
    upper: object


@dataclass(frozen=True)
class Compare:
    row: Row
    index: int  # fixed finite palette position, not an endpoint number
    left: object
    right: object


@dataclass(frozen=True)
class Lane:
    minimum: object=None
    queue: tuple=()
    active: object=None


def enqueue(lanes,row):
    return (replace(lanes[0],minimum=release(lanes[0].minimum,row.lower)),)+tuple(
        replace(s,queue=s.queue+(row,)) for s in lanes[1:])


def lane_step(s,palette,j):
    s=replace(s,minimum=step(s.minimum))
    if j==0: return s
    c=s.active
    if c is None:
        if not s.queue: return s
        row=s.queue[0]
        return replace(s,queue=s.queue[1:],active=Compare(row,0,palette[0],row.upper))
    a,b=c.left,c.right
    unequal=(a is None)!=(b is None) or (a is not None and b is not None and a.bit!=b.bit)
    if unequal:
        if c.index+1==j:
            return replace(s,active=None,minimum=release(s.minimum,c.row.lower))
        i=c.index+1
        return replace(s,active=Compare(c.row,i,palette[i],c.row.upper))
    if a is None: return replace(s,active=None)
    return replace(s,active=replace(c,left=a.tail,right=b.tail))


def quiet(s): return not s.queue and s.active is None and idle(s.minimum)


def serve(lanes,palette,count):
    for _ in range(count):
        if all(quiet(s) for s in lanes): break
        lanes=tuple(lane_step(s,palette,j) for j,s in enumerate(lanes))
    return lanes


def answers(lanes,maxima,rows,stop):
    palette=tuple(numeral(x) for x in maxima); m=len(maxima)
    positions=[0]*m; values=[None]*m
    for n in range(1,stop+1):
        lanes=serve(lanes,palette,1024*m)
        for j,s in enumerate(lanes):
            if values[j] is None and quiet(s) and s.minimum is not None:
                values[j]=decode(s.minimum.best)
            if values[j] is not None: positions[j]=min(n,positions[j]+4)
        # Range/lag arithmetic models the fixed unary readers, not a native op.
        got=any((0 if j==0 else maxima[j-1])<n<=maxima[j] and values[j] is not None
                and positions[j]==n and values[j]<=n for j in range(m))
        assert got==any(b<=n<=h for b,h in rows),(maxima,rows,n,got)
    return stop


def main():
    rng=Random(196); splices=forks=prefixes=debt_checks=0
    for counts in product(range(3),repeat=5):
        hist={i+1:c for i,c in enumerate(counts) if c}; seq=sequence(hist)
        for old in (0,)+tuple(hist):
            for n in range(1,8):
                before=inspect(seq); got=splice(seq,hist,old,n); new=dict(hist)
                if n>old:
                    if old: new[old]-=1
                    new[n]=new.get(n,0)+1
                assert inspect(got)==tuple(sorted(x for x,c in new.items() if c))
                assert inspect(seq)==before; splices+=1
    for M in (100,10000):
        hist={1:1,2:1,M:1}; seq=sequence(hist)
        assert inspect(splice(seq,hist,1,2))==(2,M)
        assert inspect(splice(seq,hist,1,3))==(2,3,M)
        assert inspect(seq)==(1,2,M); splices+=2
    for trial in range(75):
        m=1+trial%5; maxima=sorted(rng.sample(range(1,160),m))
        palette=tuple(numeral(x) for x in maxima); lanes=tuple(Lane() for _ in maxima)
        rows=[]; saved=[]
        for i in range(1,36):
            lanes=serve(lanes,palette,64*m*i.bit_length())
            h=rng.choice(maxima); b=rng.randrange(1,h+1)
            lanes=enqueue(lanes,Row(numeral(b),numeral(h))); rows.append((b,h))
            if i%7==0: saved.append((lanes,tuple(rows)))
        for state,selected in saved:
            prefixes+=answers(state,maxima,selected,maxima[-1]+2)
            done=serve(state,palette,100000); assert all(quiet(s) for s in done)
            for j,s in enumerate(done):
                bs=[b for b,h in selected if j==0 or h>maxima[j-1]]
                assert (s.minimum is None)==(not bs)
                if bs: assert decode(s.minimum.best)==min(bs)
            forks+=1
    M=(1<<1800)+1
    for maxima,rows in [([2,M,M+2],[(1,2),(7,M),(9,M+2)]),
                        ([1,3,M],[(2,3),(4,M)]),([M,M+2,M+4],[(1,M+4),(2,M)])]:
        lanes=tuple(Lane() for _ in maxima)
        for b,h in rows: lanes=enqueue(lanes,Row(numeral(b),numeral(h)))
        prefixes+=answers(lanes,maxima,rows,40)
    for maxima in ([1,3,7],[31,63,127],[3,5,9,17]):
        palette=tuple(numeral(x) for x in maxima); lanes=tuple(Lane() for _ in maxima); rows=[]
        for i in range(1,55):
            lanes=serve(lanes,palette,2*i.bit_length())
            h=rng.choice(maxima); b=rng.randrange(1,h+1)
            lanes=enqueue(lanes,Row(numeral(b),numeral(h))); rows.append((b,h))
            for j in range(1,len(maxima)):
                bs=[b for b,h in rows if h>maxima[j-1]]
                k=max(4,maxima[j-1].bit_length(),min(bs).bit_length() if bs else 0)
                D=sum(x.bit_length()+2 for x in maxima[:j])+2*k
                bound=D+sum((1<<d)*max(0,D-2*d) for d in range(1,D//2+1))
                s=lanes[j]; count=0
                while not quiet(s): s=lane_step(s,palette,j); count+=1
                assert count<=bound,(maxima,j,i,count,bound); debt_checks+=1
    for k in range(4,513):
        assert 4*k<=16*(1<<(k-4)); assert 64-15*k<=16
    print(json.dumps(dict(occupied_sequence_updates=splices,retained_receipt_forks=forks,
        query_prefix_answers=prefixes,combined_debt_checks=debt_checks,deadline_inequalities=509,
        scope='semantic deque/FIFO, histogram, setup and reader models; linked gap/digit audit'),indent=2))


if __name__=='__main__': main()