"""Authored tiny convolution: explicit loop, NumPy linear, FFT circular/padded."""
import json,math,platform
import numpy as np
def direct(a,b):
 out=[0]*(len(a)+len(b)-1)
 for i,x in enumerate(a):
  for j,y in enumerate(b):out[i+j]+=x*y
 return out
def transform(a,b,n):
 if not a or not b or len(a)>8 or len(b)>8 or any(not math.isfinite(v) for v in a+b):raise ValueError('bounded finite nonempty')
 if type(n) is not int or n<max(len(a),len(b)) or n>16:raise ValueError('length')
 z=np.fft.ifft(np.fft.fft(a,n)*np.fft.fft(b,n));assert max(abs(v.imag) for v in z)<1e-12
 return [float(v.real) for v in z]
def demo():
 a=[1,2,3];b=[1,-1];expected=direct(a,b);linear=np.convolve(a,b,'full').tolist();short=transform(a,b,3);padded=transform(a,b,4)
 assert expected==linear==[1,1,1,-3] and np.allclose(short,[-2,1,1],rtol=0,atol=1e-12) and np.allclose(padded,expected,rtol=0,atol=1e-12)
 folded=[sum(expected[j] for j in range(i,len(expected),3)) for i in range(3)];assert folded==[-2,1,1]
 impulses=[]
 for x,h in [([1,0,0],[2,-1]),([0,1,0],[2,-1]),([1],[1,-1])]:
  y=transform(x,h,len(x)+len(h)-1);oracle=direct(x,h);assert np.allclose(y,oracle,rtol=0,atol=1e-12);impulses.append({'a':x,'b':h,'fft':y,'direct':oracle})
 rejects=[]
 for x,h,n in [([],b,4),(a,b,2),([float('nan')],b,2)]:
  try:transform(x,h,n)
  except ValueError:rejects.append('ValueError')
  else:raise AssertionError('bad case')
 return {'runtime':platform.python_version(),'numpy':np.__version__,'a':a,'b':b,'linear_loop':expected,'numpy_convolve_full':linear,'fft_length3_circular':short,'folded_linear_length3_oracle':folded,'fft_length4_zero_padded':padded,'absolute_tolerance':1e-12,'impulses':impulses,'rejected':rejects,'scope':'Tiny real-valued fixtures, no FFT speed benchmark/SciPy run/audio filtering/model training; padding length at least N+M-1 for full linear result.'}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=True,indent=2))
