"""Owned finite two-state marginal evolution, no sampled individual trajectory."""
from fractions import Fraction as F
import json
def validate(P,p):
    if len(P)!=2 or any(len(r)!=2 for r in P) or len(p)!=2:raise ValueError('two-state subset')
    if any(v<0 for row in P for v in row) or any(sum(row)!=1 for row in P):raise ValueError('row-stochastic required')
    if min(p)<0 or sum(p)!=1:raise ValueError('initial probability mass required')
def move(P,p):
    validate(P,p);q=[sum(p[i]*P[i][j] for i in range(2)) for j in range(2)];assert sum(q)==1;return q
def stationary(P):
    validate(P,[F(1),F(0)]);a,b=P[0][1],P[1][0]
    if a+b==0:return {'unique':False,'reason':'identity: every initial distribution is stationary','one_stationary_distribution':[F(1,2),F(1,2)]}
    pi=[b/(a+b),a/(a+b)];assert move(P,pi)==pi
    return {'unique':True,'pi':pi}
def trace(P,p,n):
    rows=[p[:]]
    for _ in range(n):p=move(P,p);rows.append(p)
    return rows
def demo():
    P=[[F(3,4),F(1,4)],[F(1,2),F(1,2)]];pi=stationary(P)['pi'];rows=trace(P,[F(1),F(0)],5)
    assert pi==[F(2,3),F(1,3)]
    for t,row in enumerate(rows):assert row[0]-pi[0]==F(1,3)*F(1,4)**t
    flip=[[F(0),F(1)],[F(1),F(0)]];osc=trace(flip,[F(1),F(0)],5);assert stationary(flip)['pi']==[F(1,2),F(1,2)] and osc[0]==osc[2]!=osc[1]
    identity=[[F(1),F(0)],[F(0),F(1)]];assert move(identity,[F(1,3),F(2,3)])==[F(1,3),F(2,3)]
    rejected=[]
    for name,B in [('wrong_row_sum',[[F(3,4),F(1,2)],[F(1,2),F(1,2)]]),('negative_entry',[[F(2),F(-1)],[F(1,2),F(1,2)]])]:
        try:validate(B,[F(1),F(0)])
        except ValueError:rejected.append(name)
        else:raise AssertionError(name)
    return {'row_distribution_convention':True,'P':P,'stationary_pi':pi,'marginal_trace_t0_to_t5':rows,'exact_first_component_error_formula':'(1/3)*(1/4)^t','periodic_flip_P':flip,'periodic_stationary':stationary(flip),'periodic_marginal_trace':osc,'identity_stationary_family':stationary(identity),'invalid_matrices_rejected':rejected,'individual_trajectory_sampling_or_QuantEcon_executed':False}
if __name__=='__main__':print(json.dumps(demo(),default=str,indent=2))
