"""Original extended Hamming(8,4), explicit <=2-bit error guarantee."""
from itertools import product,combinations
import json

def bits(a,n):
 if type(a) is not list or len(a)!=n or any(type(x) is not int or x not in [0,1] for x in a):raise ValueError('exact-length integer bits, no bool')
 return a[:]

def encode(data):
 data=bits(data,4);b=[0]*9
 for pos,x in zip([3,5,6,7],data):b[pos]=x
 for parity in [1,2,4]:b[parity]=sum(b[i] for i in range(1,8) if i&parity)%2
 b[8]=sum(b[1:8])%2
 return b[1:]

def decode(word):
 b=[0]+bits(word,8);s=0
 for parity in [1,2,4]:s+=parity*(sum(b[i] for i in range(1,8) if i&parity)%2)
 overall=sum(b[1:])%2;corrected=None
 if s and not overall:return {'syndrome':s,'overall':overall,'status':'double_detected','corrected_position':None,'data':None,'reliable_under_at_most_two_errors':False}
 if s and overall:corrected=s;b[s]^=1;status='single_hamming_position_corrected'
 elif not s and overall:corrected=8;b[8]^=1;status='overall_parity_corrected'
 else:status='no_error_detected'
 return {'syndrome':s,'overall':overall,'status':status,'corrected_position':corrected,'data':[b[i] for i in [3,5,6,7]],'reliable_under_at_most_two_errors':True}

def flip(word,indices):
 out=word[:]
 for i in indices:out[i-1]^=1
 return out

def demo():
 words=[];single=[];double=[]
 for data in map(list,product([0,1],repeat=4)):
  word=encode(data);out=decode(word);assert out['data']==data and out['syndrome']==out['overall']==0;words.append({'data':data,'word':word})
  for i in range(1,9):
   d=decode(flip(word,[i]));assert d['data']==data and d['corrected_position']==i;single.append({'data':data,'flipped':[i],**d})
  for pair in combinations(range(1,9),2):
   d=decode(flip(word,pair));assert d['status']=='double_detected' and d['data'] is None;double.append({'data':data,'flipped':list(pair),**d})
 assert len(single)==128 and len(double)==448
 # Independent exhaustive codebook-distance oracle for all received 8-bit words.
 # This neither computes syndrome nor uses correction flags.
 distance_hist={};distance_checks=0
 for rx in map(list,product([0,1],repeat=8)):
  dist=[sum(x!=y for x,y in zip(rx,row['word'])) for row in words];m=min(dist);nearest=[i for i,x in enumerate(dist) if x==m];actual=decode(rx)
  if m<=1:assert len(nearest)==1 and actual['data']==words[nearest[0]]['data']
  else:assert m==2 and actual['data'] is None
  distance_hist[m]=distance_hist.get(m,0)+1;distance_checks+=1
 original=[0,0,0,0];triple=decode(flip(encode(original),[1,2,3]));assert triple['data']!=original and triple['status']=='overall_parity_corrected'
 rejected=[]
 for label,fn in [('bad_data_length',lambda:encode([0,1])),('bool_bit',lambda:encode([0,0,True,1])),('bad_word_bit',lambda:decode([0]*7+[2])),('nonlist',lambda:decode('00000000'))]:
  try:fn()
  except ValueError:rejected.append(label)
  else:raise AssertionError('expected rejection')
 return {'layout_one_based':{'hamming_parity':[1,2,4],'data':[3,5,6,7],'overall_parity':8},'all16_messages':words,'single_error128_cases':single,'double_error448_cases_data_not_returned':double,'independent_all256_received_word_min_distance_oracle':{'cases':distance_checks,'nearest_distance_histogram':distance_hist},'outside_guarantee_three_bit_misclassification':{'original_data':original,'flipped':[1,2,3],'decoder':triple,'output_is_wrong':True},'invalid_cases_rejected':rejected,'scope':'classifications reliable only under at-most-two-bit-error model; no authentication, confidentiality, audio codec, hardware/VHDL run or correction of arbitrary errors; own overall parity position8 differs from OpenLogic position0'}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=True,indent=2))
