"""Authored additive series, plus a retrospective period-three extraction."""
from fractions import Fraction as F
import json
def decompose(y):
    if len(y)<6:raise ValueError('at least two period-three cycles')
    y=list(map(F,y));trend=[None]*len(y)
    for i in range(1,len(y)-1):trend[i]=sum(y[i-1:i+2],F(0))/3
    detrended=[None if t is None else v-t for v,t in zip(y,trend)]
    by_phase=[[v for i,v in enumerate(detrended) if i%3==p and v is not None] for p in range(3)]
    raw=[sum(v,F(0))/len(v) for v in by_phase];center=sum(raw,F(0))/3
    seasonal=[v-center for v in raw]
    residual=[None if t is None else y[i]-t-seasonal[i%3] for i,t in enumerate(trend)]
    assert sum(seasonal)==0
    for i,t in enumerate(trend):
        if t is not None:assert t+seasonal[i%3]+residual[i]==y[i]
    return trend,seasonal,residual
def run():
    trend=[F(10+i) for i in range(9)];seasonal=[F(v) for v in [-2,0,2]*3];rem=list(map(F,[0,1,-1,0,-1,1,0,0,0]))
    y=[t+s+r for t,s,r in zip(trend,seasonal,rem)];et,es,er=decompose(y)
    assert y==list(map(F,[8,12,13,11,13,18,14,17,20]))
    assert et[0] is et[-1] is None and sum(es)==0
    assert any(et[i]!=trend[i] for i in range(1,8))
    return [{'t':i+1,'phase':i%3,'observed':str(y[i]),'known_trend':str(trend[i]),'known_seasonal':str(seasonal[i]),'known_remainder':str(rem[i]),'estimated_centered_trend':None if et[i] is None else str(et[i]),'estimated_seasonal':str(es[i%3]),'estimated_remainder':None if er[i] is None else str(er[i])} for i in range(9)]
if __name__=='__main__':print(json.dumps({'rows':run(),'period':3,'retrospective_not_forecast':True,'STL_run':False},indent=2))
