"""Authored intercept-only Poisson rate with positive exposure; no GLM library."""
from fractions import Fraction as F
import math,json
def fit(counts,exposures):
    if not counts or len(counts)!=len(exposures):raise ValueError('equal nonempty lengths')
    if any(type(y) is not int or y<0 for y in counts):raise ValueError('nonnegative integer counts')
    if any(not math.isfinite(t) or t<=0 for t in exposures):raise ValueError('finite positive exposures')
    ts=[F(t) for t in exposures];rate=F(sum(counts),1)/sum(ts)
    return rate,math.log(float(rate)) if rate else None,[rate*t for t in ts]
def nll_without_constants(beta,counts,ts):
    # Sum(mu - y*log(mu)); log(y!) omitted because independent of beta.
    return sum(math.exp(beta)*float(t)-y*(beta+math.log(float(t))) for y,t in zip(counts,ts))
def demo():
    y=[0,3,6];ts=[F(1,2),F(1),F(2)];rate,beta,mu=fit(y,ts)
    assert rate==F(18,7) and mu==[F(9,7),F(18,7),F(36,7)] and sum(mu)==sum(y)
    gradient=math.exp(beta)*float(sum(ts))-sum(y);curvature=math.exp(beta)*float(sum(ts));opt=nll_without_constants(beta,y,ts)
    assert abs(gradient)<1e-12 and curvature>0
    assert all(nll_without_constants(beta+d,y,ts)>opt for d in [-.1,-.001,.001,.1])
    zero=fit([0,0],[F(1),F(2)]);assert zero==(F(0),None,[F(0),F(0)])
    rejected=0
    for bad_y,bad_t in [([-1],[F(1)]),([1],[F(0)]),([1],[float('nan')]),([1,2],[F(1)]),([True],[F(1)])]:
        try:fit(bad_y,bad_t)
        except ValueError:rejected+=1
        else:raise AssertionError('invalid count/exposure accepted')
    return {'authored_counts':y,'authored_exposure_hours':[str(t) for t in ts],'estimated_rate_per_hour':str(rate),'log_rate_intercept':beta,'predicted_mean_counts':[str(x) for x in mu],'sum_mean_counts':str(sum(mu)),'score_derivative_at_fit':gradient,'positive_curvature':curvature,'nll_without_log_factorial':opt,'neighbor_beta_checks':4,'zero_count_boundary':{'rate':0,'finite_log_intercept':None,'status':'boundary limit beta tends to negative infinity'},'invalid_inputs_rejected':rejected,'covariates_fitted':False,'forecast_validated':False,'statsmodels_executed':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
